
肖哥弹架构 跟大家“弹弹” 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应用代码
 
- 
故障隔离 
 单个分片故障不影响其他分片服务










