MySQL分库分表实战:ShardingSphere垂直与水平拆分方案

MySQL单表数据量超过5000万行、单库磁盘逼近物理上限,读写延迟开始恶化,这是分库分表决策的临界点。拆分方案分垂直与水平两类:垂直按业务域切库,水平按分片键打散数据。分库分表一旦上线,扩容与数据迁移的成本极高,拆分键设计、扩容策略必须在动手前想清楚。

拆分时机判断:什么信号出现才需要拆

不要过早拆分。三个信号同时出现再动手:单表行数持续增长且超过5000万,B+树层级加深导致索引扫描产生大量随机IO;慢查询里深分页与聚合占比上升,读写分离已无法缓解;磁盘与连接数成为容量瓶颈,硬件升级成本超过改造投入。如果只是读压力大,先读写分离、缓存与索引优化;只有单表写入吞吐真的扛不住,才值得走分库分表这条重路线。

分片键设计:决定数据分布均匀度与查询效率

分片键决定行落在哪个库哪个表,选错了高频查询会演变成跨分片广播。原则:高频查询条件字段优先。电商订单表选user_id,保证用户查自己的订单路由到单分片:

# ShardingSphere YAML配置示例
tables:
  t_order:
    actual-data-nodes: ds_$->$->{0..1}.t_order_$->{0..3}
    database-strategy:
      standard:
        sharding-column: user_id
        sharding-algorithm:
          inline:
            algorithm-expression: ds_$->$->{user_id % 2}
    table-strategy:
      standard:
        sharding-column: user_id
        sharding-algorithm:
          inline:
            algorithm-expression: t_order_$->$->{user_id % 4}

注意路由隐患:管理员按订单号查询,订单号不在分片键里,SQL会广播到全部分片再归并。常用解法是基因法——把user_id的后几位编入订单号,从订单号反解分片路由;或维护一张异构索引表。跨维度的需求(按商户维度统计)交给异构的OLAP库,别让分片库扛分析负载。

ShardingSphere接入:JDBC驱动层的透明分片

ShardingSphere-JDBC作为JDBC增强驱动嵌入应用,无需额外部署代理,适合绝大多数团队:

spring:
  shardingsphere:
    datasource:
      names: ds-0,ds-1
      ds-0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://mysql-0:3306/order_db
    rules:
      sharding:
        sharding-algorithms:
          db-mod:
            type: MOD
            props: { "sharding-count": 2 }
        tables:
          t_order:
            actual-data-nodes: ds-$->{0..1}.t_order_$->{0..3}

应用代码照常写SQL,路由、归并、分布式主键全部由驱动层完成。全局主键用内置雪花算法,避免各分片自增冲突。跨分片查询结果归并发生在内存,ORDER BY分页要控制limit深度,防止归并OOM。

平滑扩容与数据迁移:双写灰度的标准动作

分片数从2扩到4不是改配置重启这么简单。标准流程:新旧集群双写(旧库为主),存量数据按新分片规则迁移并校验(checksum比对),灰度读切换校验一致性,全量切读后停旧库写入。迁移工具用ShardingSphere-Scaling或DataX,校验环节不可省——数据迁移事故几乎都出在校验跳步。分片数初始就规划为2的幂,扩容翻倍时路由算法只需右移一位,避免全量重新分布。

分库分表后的运维代价清单

拆分不是终点而是新起点:跨分片JOIN消失,ER图里的关联要么冗余字段要么异构查询;分布式事务引入Seata等组件;备份从单库mysqldump变成分片逐一备份再汇总校验;监控要按分片维度建延迟与容量视图。所有这些运维成本在做容量规划时一并计入,拆分的收益才能算清账。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/mysql-fen-ku-fen-biao-shi-zhan-shardingsphere-chui-zhi-yu/

赞 (0)
小编小编
上一篇 2026年9月11日
下一篇 2026年9月11日

相关推荐

MySQL分库分表实战:ShardingSphere垂直与水平拆分配置详解

什么场景下要做分库分表

单表数据量超过5000万行、单库TPS压到3000以上、慢SQL频率明显上升——这三条满足任何一条,就要考虑拆分了。分库分表不是上来就做的架构决策,而是在读写压力、数据量、运维成本之间找不到平衡点时的最后手段。ShardingSphere作为目前最成熟的分库分表中间件,支持Proxy和JDBC两种模式,小团队用JDBC模式嵌入应用就够了。

拆分策略选择:垂直拆分vs水平拆分

垂直拆分按业务边界拆——用户表放用户库,订单表放订单库,逻辑清晰,适合微服务拆分场景。水平拆分按数据规则拆——同一个表的数据按user_id或order_id分到不同库和表,解决单表数据量问题。

实际业务往往是两者结合:先按业务垂直拆库,再对大表水平分表。以电商场景为例:

垂直拆分:
  user_db: 用户表、角色表
  order_db: 订单表、订单明细表
  product_db: 商品表、库存表

水平拆分(order_db中的订单表):
  order_db_0: t_order_0, t_order_1
  order_db_1: t_order_0, t_order_1

ShardingSphere-JDBC配置实战

Spring Boot项目接入ShardingSphere-JDBC 5.x:

<!-- pom.xml -->
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-core</artifactId>
    <version>5.5.0</version>
</dependency>

application.yml分片配置:

mode:
  type: Standalone
  repository:
    type: JDBC

dataSources:
  ds_0:
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    jdbcUrl: jdbc:mysql://127.0.0.1:3306/order_db_0
    username: root
    password: root
  ds_1:
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    jdbcUrl: jdbc:mysql://127.0.0.1:3306/order_db_1
    username: root
    password: root

rules:
  - !SHARDING
    tables:
      t_order:
        actualDataNodes: ds_${0..1}.t_order_${0..1}
        tableStrategy:
          standard:
            shardingColumn: order_id
            shardingAlgorithmName: t_order_mod
        databaseStrategy:
          standard:
            shardingColumn: user_id
            shardingAlgorithmName: ds_mod
        keyGenerateStrategy:
          column: order_id
          keyGeneratorName: snowflake

    shardingAlgorithms:
      t_order_mod:
        type: MOD
        props:
          sharding-count: 2
      ds_mod:
        type: MOD
        props:
          sharding-count: 2

    keyGenerators:
      snowflake:
        type: SNOWFLAKE
        props:
          worker-id: 1

这段配置的含义:t_order表按order_id取模分2张表(t_order_0, t_order_1),按user_id取模分2个库(ds_0, ds_1)。主键用雪花算法生成,避免分库后的ID冲突。

分片键选择的关键原则

分片键直接决定数据分布均匀度和查询效率,选错代价极大。几个硬性原则:

1. 查询必带:分片键必须是几乎所有查询都带的字段。订单表按user_id分,那么查询订单必须带user_id,否则会全库全表扫描。

2. 数据均匀:避免热点。按create_time分片,写操作全部集中在当天的分片上;按user_id取模分布更均匀。

3. 不可变更:分片键一旦确定,后续修改分片键意味着数据重分布,代价等于做一次迁移。

如果业务上有些查询不带分片键怎么办?ShardingSphere支持绑定表和广播表:

# 绑定表:关联查询时避免笛卡尔积
bindingTables:
  - t_order, t_order_item

# 广播表:每个库都存一份全量数据(如字典表)
broadcastTables:
  - t_dict

分页查询处理

分库分表后分页是最棘手的问题。SELECT * FROM t_order ORDER BY create_time LIMIT 100,10 在分片环境下,ShardingSphere需要从所有分片取100+10条数据再合并排序,页码越深性能越差。

优化方案——禁止深分页,改用游标分页:

-- 禁止的方式
SELECT * FROM t_order WHERE user_id = 1001
ORDER BY create_time DESC LIMIT 10000, 10

-- 游标分页:基于上一页最后一条的时间戳
SELECT * FROM t_order
WHERE user_id = 1001 AND create_time < '2026-07-20 10:30:00'
ORDER BY create_time DESC LIMIT 10

游标分页每个分片只需取10条,合并后总数据量 = 分片数 × 10,性能稳定不受页码影响。

数据迁移策略

已有大表做分片,不能停服迁移。用双写+增量同步方案:

1. 新代码同时写旧表和新的分片表(双写)
2. 用脚本把旧表存量数据迁移到分片表
3. 校验新旧数据一致性
4. 读流量切到分片表
5. 停止写旧表

ShardingSphere本身不提供迁移工具,但可以配合Canal监听binlog做增量同步。核心是保证双写期间数据不丢,校验通过后再切流量。

运维监控与问题排查

ShardingSphere自带SQL日志,开发阶段打开能看到路由到哪个分片:

props:
  sql-show: true

日志输出:Logic SQL: SELECT * FROM t_order WHERE user_id=1001 AND order_id=2001 → Actual SQL: ds_0 ::: SELECT * FROM t_order_1 WHERE user_id=1001 AND order_id=2001

生产关闭sql-show避免性能损耗,改用Prometheus采集ShardingSphere的指标:parse_query_count(SQL解析次数)、route_result_count(路由结果数)。如果route_result_count接近分片总数,说明出现了全路由查询,需要检查分片键覆盖情况。

常见坑:跨分片聚合函数结果不准、不含分片键的UPDATE语句全路由执行导致死锁、DDL变更需要同步到所有分片。这些在架构评审阶段就要纳入规范,写进研发手册。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/mysql-fen-ku-fen-biao-shi-zhan-shardingsphere-chui-zhi-yu/

赞 (0)
小编小编
上一篇 2026年7月23日
下一篇 2026年7月23日

相关推荐