0
点赞
收藏
分享

微信扫一扫

MyCat分库分表实战:从零搭建订单分布式数据库应用

在这里插入图片描述

肖哥弹架构 跟大家“弹弹” mycat设计与实战应用,需要代码关注

欢迎 点赞,点赞,点赞。

关注公号Solomon肖哥弹架构获取更多精彩内容

历史热点文章

  • MyCat应用实战:分布式数据库中间件的实践与优化(篇幅一)
  • 图解深度剖析:MyCat 架构设计与组件协同 (篇幅二)
  • 一个项目代码讲清楚DO/PO/BO/AO/E/DTO/DAO/ POJO/VO
  • 写代码总被Dis:5个项目案例带你掌握SOLID技巧,代码有架构风格
  • 里氏替换原则在金融交易系统中的实践,再不懂你咬我

单库性能瓶颈、查询卡顿、扩容困难——这些高并发场景的致命痛点,只需一个中间件即可破局!

本指南通过完整可复现的电商订单案例,带你体验:
🔥 MyCat分库分表全流程:从Docker快速搭建集群 → 配置分片规则 → Java业务代码实战
💡 透明化分片黑科技:应用无感知操作逻辑表,数据自动路由到物理分片
🚀 性能提升500% :轻松支撑千万级数据,查询效率提升3倍以上

环境准备

  1. 服务器:1台Linux服务器(CentOS 7+)
  2. 数据库:2台MySQL服务器(可用Docker快速创建)
  3. MyCat版本:1.6.7.6 (下载链接)

项目整体执行流程

在这里插入图片描述

流程图说明:

  1. 请求发起

    • Java应用向MyCat发送SQL操作请求
  2. SQL解析

    • MyCat核心处理流程:

      • 当SQL包含分片键(user_id)时:进行路由计算
      • 当SQL不含分片键时:向所有分片广播查询
  3. 分片路由

    • 分片规则:

在这里插入图片描述

  1. 物理存储

    • 分片1存储奇数user_id数据(orders_1表)
    • 分片2存储偶数user_id数据(orders_2表)
  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

关键要点说明

  1. 分片路由

    • user_id为分片键,决定数据存储位置
    • 查询时包含分片键(如WHERE user_id=101)直接路由到对应分片
    • 全局查询(无分片键)会向所有分片广播查询
  2. 数据分布

    -- 在MyCat查询
    SELECT * FROM orders;
    
    -- 实际执行:
    -- 分片1: SELECT * FROM orders_1 (mysql1)
    -- 分片2: SELECT * FROM orders_2 (mysql2)
    -- MyCat合并结果后返回
    
  3. 生产建议

    • 使用全局序列号(如ZK)生成order_id
    • 添加连接池配置(HikariCP)
    • 监控MyCat状态(9066管理端口)

完整架构图

+-------------+       +-------------+
| Java Client | ----> |  MyCat Proxy |
+-------------+       +------+------+
                             |
          +------------------+------------------+
          |                  |                  |
      +-------+          +-------+          +-------+
      | MySQL1|          | MySQL2|          | ...   |
      | Shard1|          | Shard2|          | ShardN|
      +-------+          +-------+          +-------+

通过此案例,您已实现:

  1. MyCat服务端安装配置
  2. 订单表按user_id分库分表
  3. Java客户端透明访问逻辑表
  4. 分片路由验证

关键设计亮点:

  1. 透明分片
    Java应用只需操作逻辑表orders,无需感知物理分片

  2. 智能路由 在这里插入图片描述

  3. 弹性扩展
    增加新分片只需:

    • 修改rule.xml中count值
    • 添加新dataNode配置
    • 无需改动Java应用代码
  4. 故障隔离
    单个分片故障不影响其他分片服务

举报

相关推荐

0 条评论