ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

分库分表实战:ShardingSphere-JDBC 从配置到扩容全解析

分库分表实战:ShardingSphere-JDBC 从配置到扩容全解析 分库分表与 ShardingSphere当单库无法支撑海量数据和高并发写入时分库分表成为必经之路。本文覆盖分库分表核心概念 → ShardingSphere-JDBC 5.5.0 分库分表实战配置 → 分片算法对比 → 扩容策略。一、为什么分库分表1.1 三大根因根因典型阈值后果数据量增长单表 2000 万行三层 BTree 极限树层数增加I/O 次数上升单表写性能瓶颈写入 QPS 2000需 8C 高线程写入高并发插入争用自增主键锁热点页 X 锁竞争写入争用热点页锁高并发集中写入相同 ID 范围页锁冲突导致吞吐量断崖式下降1.2 分库 vs 分表维度分库分表目标水平拆分到不同 MySQL实例跨机器水平拆分到同一 MySQL 实例内不同表解决单机硬件上限CPU/内存/磁盘/网络带宽单表行数/数据量上限2000 万行分片策略数据分布到不同物理机器ds0, ds1数据分布到同库的不同物理表table_0, table_1性能瓶颈分布式事务跨库跨分片 JOIN、分片键回表、排序横向拆分水平分片才是互联网业务最常见的第一优先级先解决数据量上限再解决写入瓶颈。1.3 数据倾斜 vs 热点概念定义场景数据倾斜数据不均匀分布到分片如user_id1写满 shard1其余几乎没数据大客户历史数据占全量 99%热点数据写请求集中写入同一个分片如user_id1在当前时刻同时写入 1000 次订单高峰期用户下单集中爆发分库分表无法解决数据倾斜必须设计业务规则。比如设定分片键为时间维度确保新数据分散到不同分片或者写入前用本地缓存来随机选择分片。二、项目结构与依赖2.1 pom.xml分库分表专用propertiesshardingsphere.version5.5.0/shardingsphere.version/propertiesdependencies!-- 核心JDBC 驱动模式 --dependencygroupIdorg.apache.shardingsphere/groupIdartifactIdshardingsphere-jdbc/artifactIdversion${shardingsphere.version}/version/dependency!-- 必须显式引入spring-boot-starter-jdbc 在 5.5.0 中是内嵌模块默认不拉取 --dependencygroupIdorg.apache.shardingsphere/groupIdartifactIdshardingsphere-jdbc-core-spring-boot-starter/artifactIdversion${shardingsphere.version}/version/dependency!-- 必须JAXB 2.3.9javax 命名空间--dependencygroupIdorg.glassfish.jaxb/groupIdartifactIdjaxb-runtime/artifactIdversion2.3.9/version/dependency/dependencies版本铁律SS 必须5.5.0。shardingsphere-jdbc-core-spring-boot-starter在 5.5.0 之前未包含spring-boot-starter-jdbc的传递依赖这是 5.5.0 版本特有的。生产 5.5.0 实测稳定5.5.1 模块拆分导致缺包。2.2 项目结构shardingsphere-jdbc5.5.0-sharding-springboot-3.2.5/ ├── pom.xml # 依赖 共享常量 ├── shardingsphere.yaml # 核心配置数据源、分库、分表、日期路由算法 ├── src/main/java │ ├── Application.java │ └── shardingsphere/sharding/ │ ├── config/DateBasedSharding.java # 自定义日期表名路由算法 │ ├── entity/Order.java │ ├── mapper/OrderMapper.java │ └── test/ShardingSphereTest.java # 主入口先表结构 → 再写测试 → 再查结果 └── src/main/resources/application.yml # 数据库连接、MyBatis、日志级别分库分表 6 板斧① 主键雪花自增② 按字段分库③ 按字段日期分表④ 初始化spring.factories⑤ 表结构自动化⑥ 集成 SS-JDBC 5.5.0 配置动态数据源。三、shardingsphere.yaml 配置详解3.1 数据源配置4 个 MySQL 实例# 共用连接池常量DB_HOST、DB_USER、DB_PASS 等通过 Maven Filter 或环境变量注入dataSources:ds0:dataSourceClassName:com.zaxxer.hikari.HikariDataSourcedriverClassName:com.mysql.cj.jdbc.DriverjdbcUrl:jdbc:mysql://${DB_HOST}:3300/${DB_NAME}?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueusername:${DB_USER}password:${DB_PASS}maximumPoolSize:10ds1:jdbcUrl:jdbc:mysql://${DB_HOST}:3301/${DB_NAME}?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue# ... 同上ds2:jdbcUrl:jdbc:mysql://${DB_HOST}:3302/${DB_NAME}?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue# ... 同上ds3:jdbcUrl:jdbc:mysql://${DB_HOST}:3303/${DB_NAME}?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue# ... 同上关键共用一个 Maven 常量只改端口。3.2 分库算法Shard4Mod_4rules:-!SHARDINGtables:t_order:actualDataNodes:ds${0..3}.t_order_${0..3}tableStrategy:standard:shardingColumn:order_idshardingAlgorithmName:hash4mod_4databaseStrategy:standard:shardingColumn:user_idshardingAlgorithmName:shard4mod_4shardingAlgorithms:shard4mod_4:type:INLINEprops:algorithm-expression:ds${user_id % 4}hash4mod_4:type:INLINEprops:algorithm-expression:t_order_${(order_id.hashCode() Integer.MAX_VALUE) % 4}Shard4Mod_4 公式4 取模 4 0 库/表1 % 4 12 % 4 23 % 4 3。推荐做法分库键 → 跨库的平键user_id分表键 → 跨表的分键order_id。actualDataNodes: ds${0..3}.t_order_${0..3}即 4 库 × 4 表 16 个实际表。3.3 按日期分片键分表HashDate 双算法-!SHARDINGtables:t_order:actualDataNodes:ds${0..3}.t_order_${0..3}tableStrategy:complex:shardingColumns:user_id,order_dateshardingAlgorithmName:date_shard_hashdatabaseStrategy:standard:shardingColumn:user_idshardingAlgorithmName:shard4mod_4shardingAlgorithms:date_shard_hash:type:CLASS_BASEDprops:strategy:complexalgorithmClassName:com.xxx.config.DateBasedSharding3.4 日期表名路由自定义算法JavaComponentShardingSphereAlgorithmType(date_shard)publicclassDateBasedShardingimplementsComplexShardingAlgorithmString{OverridepublicCollectionStringdoSharding(CollectionStringavailableTargetNames,ComplexShardingValueStringcomplexShardingValue){MapString,CollectionStringcolumnNameAndShardingValuesMapcomplexShardingValue.getColumnNameAndShardingValuesMap();// 取出 order_date 分片值写 SQL 时按字符串传CollectionStringdateValuescolumnNameAndShardingValuesMap.get(order_date);if(CollectionUtils.isEmpty(dateValues)){returnavailableTargetNames;// 没传日期就全扫}StringdateValuedateValues.iterator().next();// 统一格式化2026-06-30StringtableDatedateValue.split( )[0].replace(-,);returnCollections.singletonList(t_order_tableDate);}OverridepublicStringgetType(){returndate_shard;}Overridepublicvoidinit(Propertiesprops){}}实际路由1 库 → 4 表基于用户 ID Hash 日期→2026_06_30单日 10 万单。HashDate 双算法2 维度分片order_date二次 Hash 确保同一用户跨表日期的数据均匀分布。t_order_20260630。四、分片算法对比4.1 大表 vs 按分片对比大表未分片按分片分库分表主键特性标准顺序递增AUTO_INCREMENT乱序无顺序意义ID 只是唯一标识业务逻辑顺序好追踪、统计易必须加order_id或user_id作为查询条件典型应用无索引复杂但性能很好精确查询无影响慢路由算法只能定位聚簇查询全扫1/4 或 1/8 范围精准定位关键性能慢索引维护、热点页锁非常快范围扫描1 个表扫描多个分片表扫描分库分表后没有唯一递增 ID总订单号不是系统问题必须额外设计全局序号中心。4.2 标准设计 vs 按片设计维度标准设计老系统按片设计分库分表表名t_order无后缀t_order_0/t_order_1/t_order_20260630主键id自增必须改为user_id非业务主键或雪花 ID查询条件直接WHERE idxx必须带user_id或order_id分片键业务影响无影响查询所有接口必须改 WHERE 条件主键追踪可以先查主键再查必须按顺序先查询后追踪优点简单按分片快速查询写无热点查询 1 对 N 好扩展缺点热点冲突不适合超大数据需要改分片键不能 id 查询但性能 3~10 倍4.3 分片键对比适合带操作分片键优点缺点适用场景user_id查询快易查询用户中心用户操作集中无利于订单订单表、用户中心order_id订单分布均匀无热点订单操作查询后不利于主键无业务语义订单表、用户中心order_date时间范围查询好适合历史天热点、无法定位用户历史表、物流表user_id order_date时间用户双维度平衡无法 1 主键查询复杂订单表、分库分表优先分库分表后查询必须带分片键user_id或order_date否则全扫4 库 × 4 表 16 个表。实战最佳分库键用user_id分表键用order_date Hash双维度。user_id5, 10, 15, 20均匀分到 4 个库库内按日期再分 4 表。五、跨分片查询问题5.1 跨分片聚合JOIN场景问题方案跨分片 JOIN 等值性能良好路由无问题无需改造跨分片 JOIN 范围条件路由不可计算需全表改为分片键或切分表跨分片聚合COUNT/SUM需聚合结果全局聚合如t_order_total逐片后聚合排序后分页查询 16 张表后排序必须按分片键分页或先查询后内存聚合5.2 大表分区 vs 分库分表方案优缺场景大表不分片简单支持事务数据量小无并发大表分区如按时间单库简单维护范围查询优秀中等数据查询模式固定如按月分库分表高扩展抗并发但路由复杂JOIN 难大数据、高并发、读写分离、扩容分区也是一次方案但迁移成本高。建议按分片键初定但分区库就固定根据大小决定是否合并。六、扩容策略6.1 三种扩容策略策略核心优点缺点适用场景静态分片预先分 16 片DBA 配置管理按容量分简单无复杂规则扩容需迁移维护复杂数据量小、增长可预测动态分片库按容量动态调整系统自动按规则动态增长弹性好无需人工规则复杂需管理元数据数据量大动态增长一致性哈希虚拟节点映射到实际节点Hash 环均匀动态扩容平滑无需全迁移新增虚拟节点需微调映射大规模、无规律增长静态分片最简单但最复杂的是管理映射规则一致性哈希最复杂但扩容最平滑。在数据可控、容量预期明确的场景下静态分片足够。6.2 一致性哈希详解核心思想不直接对实际节点取模而是对虚拟节点取模。每个真实节点在 Hash 环上对应多个虚拟节点如 150~300 个。维度一致性哈希标准 Hash如user_id % 4扩容只需迁移相邻虚拟节点对应的数据约 1/N所有数据都需重新计算 Hash全量迁移缩容同上只需迁移消失节点的数据同上全量迁移数据倾斜风险虚拟节点足够多 → 均匀分布简单取模分布不均虚拟节点数量通常 150~300 个 / 真实节点无无论是静态分片、动态分片还是一致性哈希核心是** Hash → 分片键 → 数据映射**。标准 Hash 简单但扩容时所有数据迁移一致性哈希只迁移约 1/N但需管理虚拟节点。大规模系统Redis 集群、MongoDB 分片用一致性哈希 虚拟节点。6.3 扩容 4 步骤静态分片1. 新库DB4、DB5建好与旧库DB0~3并行 2. DBA 将分片规则从「4 取模」改为「6 取模」 3. 老数据按新规则「6 取模」重新迁移到 DB4、DB5 4. 新写按新规则路由新数据按 6 取模分配静态分片扩容的关键① 新库并行建好② 规则修改4→6③ 老数据迁移④ 新写按新规则。一致性哈希只需改虚拟节点映射不需要全量迁移老数据。七、核心速查与踩坑7.1 分库分表 6 板斧主键雪花自增IdWorker.getId()非顺序递增防分片热点按字段分库user_id % 4 → ds0~3按字段日期分表t_order_20260630日期Hash 双维度初始化spring.factoriesSS 5.5.0 启动扫描 SPI 依赖表结构自动化TableName或自动建表脚本集成 SS-JDBC 5.5.0Driver 模式 显式 starter JAXB 2.3.97.2 踩坑速查表#坑根因解1spring-boot-starter-jdbc缺失5.5.0 中内嵌不自动拉取显式引入shardingsphere-jdbc-core-spring-boot-starter2动态数据源不生效shardingsphere.yaml没有!SHARDING规则必须配置databaseStrategy和tableStrategy3查询不带分片键 → 全扫 16 表路由算法无法定位所有查询必须带user_id或order_date4日期分表未按order_date格式SQL 传2026-06-30 10:00:00而非纯日期自定义算法中split( )[0].replace(-, )标准化5自定义算法未注册spring.factories未配置org.apache.shardingsphere.sharding.spi.ShardingAlgorithmcom.xxx.DateBasedSharding6按片 ID 查询必须用分片键按片系统无全局递增 ID 追踪系统生成全局唯一 ID雪花业务层映射7分库后全局 ID 冲突4 个库各自自增统一用雪花 ID非 DB 自增8跨分片 JOIN 性能差数据分布到不同库/表避免跨分片 JOIN或先查单表再聚合7.3 读写分离与分库分表的联动假设配置rules:-!READWRITE_SPLITTINGdataSources:ds_0:# 对应 ds0writeDataSourceName:ds0readDataSourceNames:-ds0_slaveds_1:writeDataSourceName:ds1readDataSourceNames:-ds1_slave# ... ds2, ds3 同理先分库分表水平拆分再读写分离垂直拆分。先切数据再优化读写负载。核心速查表分片键设计决策查询是否按用户维度 ├─ 是 → 分库键 user_id分表键 order_date Hash └─ 否 → 分库键 业务主键分表键 时间维度 查询是否按时间范围 ├─ 是 → 日期分表优先如 t_order_202606 └─ 否 → Hash 分表优先如 t_order_0~3三种分片策略对比策略扩容方式数据迁移量复杂度推荐度静态分片改取模数 全量迁移全量低数据量可控、有 DBA动态分片自动增加容量无需迁移中云原生、弹性需求一致性哈希加虚拟节点1/N高大规模、Redis/MongoDB一句话心法分库分表不是银弹它解决了数据量上限和写入热点但引入了跨分片查询、分布式事务、全局 ID 等复杂度。设计时先选对分片键决定 80% 的查询性能再选对分片策略决定扩容成本最后才是框架配置ShardingSphere 只是执行器。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表