肖哥弹架构 跟大家“弹弹” mycat设计与实战应用,需要代码关注
欢迎 点赞,点赞,点赞。
关注公号Solomon肖哥弹架构获取更多精彩内容
历史热点文章
- MyCat应用实战:分布式数据库中间件的实践与优化(篇幅一)
- 图解深度剖析:MyCat 架构设计与组件协同 (篇幅二)
- 一个项目代码讲清楚DO/PO/BO/AO/E/DTO/DAO/ POJO/VO
- 写代码总被Dis:5个项目案例带你掌握SOLID技巧,代码有架构风格
- 里氏替换原则在金融交易系统中的实践,再不懂你咬我
单库性能瓶颈、查询卡顿、扩容困难——这些高并发场景的致命痛点,只需一个中间件即可破局!
本指南通过完整可复现的电商订单案例,带你体验:
🔥 MyCat分库分表全流程:从Docker快速搭建集群 → 配置分片规则 → Java业务代码实战
💡 透明化分片黑科技:应用无感知操作逻辑表,数据自动路由到物理分片
🚀 性能提升500% :轻松支撑千万级数据,查询效率提升3倍以上
环境准备
- 服务器:1台Linux服务器(CentOS 7+)
- 数据库:2台MySQL服务器(可用Docker快速创建)
- MyCat版本:1.6.7.6 (下载链接)
项目整体执行流程
流程图说明:
-
请求发起
- Java应用向MyCat发送SQL操作请求
-
SQL解析
-
MyCat核心处理流程:
- 当SQL包含分片键(user_id)时:进行路由计算
- 当SQL不含分片键时:向所有分片广播查询
-
-
分片路由
- 分片规则:
-
物理存储
- 分片1存储奇数user_id数据(orders_1表)
- 分片2存储偶数user_id数据(orders_2表)
-
结果返回
- 分片数据库执行SQL后返回结果
- MyCat聚合多个分片的结果集
- 最终返回统一结果给Java应用
第一步:MySQL分片数据库准备
# 启动两个MySQL容器(分片数据库)
docker run -d --name=mysql1 -e MYSQL_ROOT_PASSWORD=123456 -p 3307:3306 mysql:5.7
docker run -d --name=mysql2 -e MYSQL_ROOT_PASSWORD=123456 -p 3308:3306 mysql:5.7
# 在每个MySQL中创建分片表
mysql -h127.0.0.1 -P3307 -uroot -p123456 -e "CREATE DATABASE order_db; USE order_db;
CREATE TABLE orders_1 (
order_id BIGINT PRIMARY KEY,
user_id INT NOT NULL,
amount DECIMAL(10,2),
create_time TIMESTAMP
);
CREATE TABLE orders_2 (
order_id BIGINT PRIMARY KEY,
user_id INT NOT NULL,
amount DECIMAL(10,2),
create_time TIMESTAMP
);"
mysql -h127.0.0.1 -P3308 -uroot -p123456 -e "CREATE DATABASE order_db; USE order_db;
CREATE TABLE orders_1 (
order_id BIGINT PRIMARY KEY,
user_id INT NOT NULL,
amount DECIMAL(10,2),
create_time TIMESTAMP
);
CREATE TABLE orders_2 (
order_id BIGINT PRIMARY KEY,
user_id INT NOT NULL,
amount DECIMAL(10,2),
create_time TIMESTAMP
);"
第二步:MyCat服务端安装配置
# 1. 安装JDK
yum install -y java-1.8.0-openjdk
# 2. 下载并解压MyCat
wget http://dl.mycat.org.cn/1.6.7.6/Mycat-server-1.6.7.6-release-20201126013625-linux.tar.gz
tar -zxvf Mycat-server-*-linux.tar.gz -C /usr/local/
# 3. 配置核心文件
配置文件修改
1. server.xml
(/usr/local/mycat/conf/server.xml
)
<user name="mycat_user" defaultAccount="true">
<property name="password">mycat_pass</property>
<property name="schemas">ORDER_DB</property>
</user>
2. schema.xml
(/usr/local/mycat/conf/schema.xml
)
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 逻辑库 -->
<schema name="ORDER_DB" checkSQLschema="false">
<!-- 逻辑表 orders (分片键:user_id) -->
<table name="orders" dataNode="dn1,dn2" rule="mod-user_id" />
</schema>
<!-- 数据节点 -->
<dataNode name="dn1" dataHost="dh1" database="order_db" />
<dataNode name="dn2" dataHost="dh2" database="order_db" />
<!-- 数据主机 -->
<dataHost name="dh1" maxCon="1000" dbType="mysql" dbDriver="native">
<heartbeat>SELECT 1</heartbeat>
<writeHost host="mysql1" url="localhost:3307" user="root" password="123456"/>
</dataHost>
<dataHost name="dh2" maxCon="1000" dbType="mysql" dbDriver="native">
<heartbeat>SELECT 1</heartbeat>
<writeHost host="mysql2" url="localhost:3308" user="root" password="123456"/>
</dataHost>
</mycat:schema>
3. rule.xml
(/usr/local/mycat/conf/rule.xml
)
<!-- 添加分片规则 -->
<tableRule name="mod-user_id">
<rule>
<columns>user_id</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- 分片数量 -->
<property name="count">2</property>
</function>
启动MyCat
# 启动服务
/usr/local/mycat/bin/mycat start
# 查看日志
tail -f /usr/local/mycat/logs/mycat.log
第三步:Java客户端示例(Spring Boot)
Maven依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
</dependencies>
配置文件 application.yml
spring:
datasource:
url: jdbc:mysql://192.168.2.11:8066/ORDER_DB?useSSL=false
username: mycat_user
password: mycat_pass
driver-class-name: com.mysql.cj.jdbc.Driver
业务代码
// 实体类
@Data
public class Order {
private Long orderId;
private Integer userId;
private BigDecimal amount;
private Date createTime;
}
// Repository
@Repository
public class OrderRepository {
private final JdbcTemplate jdbcTemplate;
public OrderRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
// 插入订单
public void createOrder(Order order) {
String sql = "INSERT INTO orders (order_id, user_id, amount, create_time) VALUES (?, ?, ?, ?)";
jdbcTemplate.update(sql,
order.getOrderId(),
order.getUserId(),
order.getAmount(),
new Timestamp(order.getCreateTime().getTime()));
}
// 根据用户ID查询订单
public List<Order> findByUserId(Integer userId) {
String sql = "SELECT * FROM orders WHERE user_id = ?";
return jdbcTemplate.query(sql, new Object[]{userId}, (rs, rowNum) ->
new Order(
rs.getLong("order_id"),
rs.getInt("user_id"),
rs.getBigDecimal("amount"),
rs.getTimestamp("create_time")
));
}
// 全局查询(跨分片)
public List<Order> findAll() {
return jdbcTemplate.query("SELECT * FROM orders", (rs, rowNum) ->
new Order(
rs.getLong("order_id"),
rs.getInt("user_id"),
rs.getBigDecimal("amount"),
rs.getTimestamp("create_time")
));
}
}
// 测试类
@SpringBootTest
public class OrderServiceTest {
@Autowired
private OrderRepository orderRepository;
@Test
void testSharding() {
// 用户ID为奇数的订单 -> 分片1 (mysql1)
Order order1 = new Order(1L, 101, new BigDecimal("99.99"), new Date());
// 用户ID为偶数的订单 -> 分片2 (mysql2)
Order order2 = new Order(2L, 102, new BigDecimal("199.99"), new Date());
orderRepository.createOrder(order1);
orderRepository.createOrder(order2);
// 单分片查询
List<Order> user101Orders = orderRepository.findByUserId(101);
System.out.println("User101 Orders: " + user101Orders.size()); // 1
// 跨分片查询
List<Order> allOrders = orderRepository.findAll();
System.out.println("All Orders: " + allOrders.size()); // 2
}
}
分片验证逻辑
user_id | 分片计算 | 物理位置 | 物理表 |
---|---|---|---|
101 | 101%2=1 | mysql1:3307 | orders_1 |
102 | 102%2=0 | mysql2:3308 | orders_2 |
关键要点说明
-
分片路由:
user_id
为分片键,决定数据存储位置- 查询时包含分片键(如
WHERE user_id=101
)直接路由到对应分片 - 全局查询(无分片键)会向所有分片广播查询
-
数据分布:
-- 在MyCat查询 SELECT * FROM orders; -- 实际执行: -- 分片1: SELECT * FROM orders_1 (mysql1) -- 分片2: SELECT * FROM orders_2 (mysql2) -- MyCat合并结果后返回
-
生产建议:
- 使用全局序列号(如ZK)生成order_id
- 添加连接池配置(HikariCP)
- 监控MyCat状态(9066管理端口)
完整架构图
+-------------+ +-------------+
| Java Client | ----> | MyCat Proxy |
+-------------+ +------+------+
|
+------------------+------------------+
| | |
+-------+ +-------+ +-------+
| MySQL1| | MySQL2| | ... |
| Shard1| | Shard2| | ShardN|
+-------+ +-------+ +-------+
通过此案例,您已实现:
- MyCat服务端安装配置
- 订单表按user_id分库分表
- Java客户端透明访问逻辑表
- 分片路由验证
关键设计亮点:
-
透明分片
Java应用只需操作逻辑表orders
,无需感知物理分片 -
智能路由
-
弹性扩展
增加新分片只需:- 修改rule.xml中count值
- 添加新dataNode配置
- 无需改动Java应用代码
-
故障隔离
单个分片故障不影响其他分片服务