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=2001Actual 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)
小编小编
上一篇 15小时前
下一篇 15小时前

相关推荐