分库分表-节目服务
阅读此文前,建议小伙伴先阅读分库分表的前置知识,对分库分表 和 shardingsphere 有了大概的理解后,在继续阅读本文
介绍
在开始介绍前,需要先知道大麦网中数据库表的关系是怎么样,关于数据库表设计的详细介绍,小伙伴可跳转到相关文档
配置
引入 ShardingSphere 的相关依赖
xml
<properties>
<shardingsphere.version>5.3.2</shardingsphere.version>
</properties>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core</artifactId>
<version>${shardingsphere.version}</version>
<exclusions>
<exclusion>
<artifactId>logback-classic</artifactId>
<groupId>ch.qos.logback</groupId>
</exclusion>
</exclusions>
</dependency>根据规则进行分库分表的规则配置
ShardingSphere 官网的规则配置说明:
节目项目相关配置:
yaml
spring:
datasource:
driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver
url: jdbc:shardingsphere:classpath:shardingsphere-program.yamlshardingsphere-program.yaml配置:
yaml
dataSources:
# 第一个节目库
ds_0:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://127.0.0.1:3306/damai_program_0?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai&autoReconnect=true
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/damai_program_1?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai&autoReconnect=true
username: root
password: root
rules:
- !SHARDING
tables:
# 对d_program表进行分库分表
d_program:
# 库为damai_program_0 damai_program_1 表为d_program_0 至 d_program_1
actualDataNodes: ds_${0..1}.d_program_${0..1}
# 分库策略
databaseStrategy:
standard:
# 使用id作为分片键
shardingColumn: id
# 使用id分库算法
shardingAlgorithmName: databaseProgramModModel
# 分表策略
tableStrategy:
standard:
# 使用id作为分片键
shardingColumn: id
# 使用id分表算法
shardingAlgorithmName: tableProgramModModel
# 对d_program_group表进行分库分表
d_program_group:
# 库为damai_program_0 damai_program_1 表为d_program_group_0 至 d_program_group_1
actualDataNodes: ds_${0..1}.d_program_group_${0..1}
# 分库策略
databaseStrategy:
standard:
# 使用id作为分片键
shardingColumn: id
# 使用id分库算法
shardingAlgorithmName: databaseProgramGroupModModel
# 分表策略
tableStrategy:
standard:
# 使用id作为分片键
shardingColumn: id
# 使用id分库算法
shardingAlgorithmName: tableProgramGroupModModel
# 对d_program_show_time表进行分库分表
d_program_show_time:
# 库为damai_program_0 damai_program_1 表为d_program_show_time_0 至 d_program_show_time_1
actualDataNodes: ds_${0..1}.d_program_show_time_${0..1}
# 分库策略
databaseStrategy:
standard:
# 使用id作为分片键
shardingColumn: program_id
# 使用id分库算法
shardingAlgorithmName: databaseProgramShowTimeModModel
# 分表策略
tableStrategy:
standard:
# 使用id作为分片键
shardingColumn: program_id
# 使用id分表算法
shardingAlgorithmName: tableProgramShowTimeModModel
# 对d_seat表进行分库分表
d_seat:
# 库为damai_program_0 damai_program_1 表为d_seat_0 至 d_seat_1
actualDataNodes: ds_${0..1}.d_seat_${0..1}
# 分库策略
databaseStrategy:
standard:
# 使用id作为分片键
shardingColumn: program_id
# 使用id分库算法
shardingAlgorithmName: databaseSeatModModel
# 分表策略
tableStrategy:
standard:
# 使用id作为分片键
shardingColumn: program_id
# 使用id分表算法
shardingAlgorithmName: tableSeatModModel
# 对d_ticket_category表进行分库分表
d_ticket_category:
# 库为damai_program_0 damai_program_1 表为d_ticket_category_0 至 d_ticket_category_1
actualDataNodes: ds_${0..1}.d_ticket_category_${0..1}
# 分库策略
databaseStrategy:
standard:
# 使用id作为分片键
shardingColumn: program_id
# 使用id分库算法
shardingAlgorithmName: databaseTicketCategoryModModel
# 分表策略
tableStrategy:
standard:
# 使用id作为分片键
shardingColumn: program_id
# 使用id分表算法
shardingAlgorithmName: tableTicketCategoryModModel
# 对d_program_record_task表进行分库分表
d_program_record_task:
# 库为damai_program_0 damai_program_1 表为d_program_record_task_0 至 d_program_record_task_1
actualDataNodes: ds_${0..1}.d_program_record_task_${0..1}
# 分库策略
databaseStrategy:
standard:
# 使用create_time作为分片键
shardingColumn: create_time
# 使用create_time分库算法
shardingAlgorithmName: databaseProgramRecordTaskModModel
# 分表策略
tableStrategy:
standard:
# 使用create_time作为分片键
shardingColumn: create_time
# 使用create_time分表算法
shardingAlgorithmName: tableProgramRecordTaskModModel
# 广播表
broadcastTables:
- d_program_category
# 具体的算法(采用基因位分离策略:分库用低位bit0,分表用次低位bit1,避免数据倾斜)
shardingAlgorithms:
# d_program表分库算法 - 使用id的低位(bit0)
databaseProgramModModel:
type: INLINE
props:
algorithm-expression: ds_${id % 2}
# d_program表分表算法 - 使用id的次低位(bit1)
tableProgramModModel:
type: INLINE
props:
algorithm-expression: d_program_${(id.intdiv(2)) % 2}
# d_program_group表分库算法 - 使用id的低位(bit0)
databaseProgramGroupModModel:
type: INLINE
props:
algorithm-expression: ds_${id % 2}
# d_program_group表分表算法 - 使用id的次低位(bit1)
tableProgramGroupModModel:
type: INLINE
props:
algorithm-expression: d_program_group_${(id.intdiv(2)) % 2}
# d_program_show_time表分库算法 - 使用program_id的低位(bit0)
databaseProgramShowTimeModModel:
type: INLINE
props:
algorithm-expression: ds_${program_id % 2}
# d_program_show_time表分表算法 - 使用program_id的次低位(bit1)
tableProgramShowTimeModModel:
type: INLINE
props:
algorithm-expression: d_program_show_time_${(program_id.intdiv(2)) % 2}
# d_seat表分库算法 - 使用program_id的低位(bit0)
databaseSeatModModel:
type: INLINE
props:
algorithm-expression: ds_${program_id % 2}
# d_seat表分表算法 - 使用program_id的次低位(bit1)
tableSeatModModel:
type: INLINE
props:
algorithm-expression: d_seat_${(program_id.intdiv(2)) % 2}
# d_ticket_category表分库算法 - 使用program_id的低位(bit0)
databaseTicketCategoryModModel:
type: INLINE
props:
algorithm-expression: ds_${program_id % 2}
# d_ticket_category表分表算法 - 使用program_id的次低位(bit1)
tableTicketCategoryModModel:
type: INLINE
props:
algorithm-expression: d_ticket_category_${(program_id.intdiv(2)) % 2}
# d_program_record_task表分库算法 - 使用create_time的hashCode低位(bit0)
databaseProgramRecordTaskModModel:
type: INLINE
props:
algorithm-expression: ds_${Math.abs(create_time.hashCode()) % 2}
# d_program_record_task表分表算法 - 使用create_time的hashCode次低位(bit1)
tableProgramRecordTaskModModel:
type: INLINE
props:
algorithm-expression: d_program_record_task_${(Math.abs(create_time.hashCode()).intdiv(2)) % 2}
props:
# 打印真实sql
sql-show: true节目服务的分库分表配置比较常规,基本都是用program_id作为分片键,d_program_category表存储的是节目种类数据,数据量并不是很大,也就几百条,并且每个节目数据都需要此表关联,所以d_program_category比较适合作为广播表
分库分表的算法详解
问题背景:为什么传统 MOD 算法会导致数据倾斜?
如果分库和分表都使用相同的 id % 2 算法:
groovy
// 错误的配置方式
分库: ds_${id % 2}
分表: d_program_${id % 2}导致的问题:
plain
表_0 表_1
库_0 (ds_0) ✔️ ✖️ ← 偶数库的奇数表永远没数据
库_1 (ds_1) ✖️ ✔️ ← 奇数库的偶数表永远没数据解决方案:intdiv 基因位分离策略
groovy
// 正确的配置方式
分库: ds_${id % 2} // 使用 bit0
分表: d_program_${(id.intdiv(2)) % 2} // 使用 bit1intdiv 是什么?
intdiv 是 Groovy 的整数除法,等价于 id / 2 取整数部分:
groovy
id.intdiv(2) 等价于 id / 2 (取整数部分,即右移1位)核心原理:使用不同的二进制位
plain
id 的二进制表示: ... bit3 bit2 bit1 bit0
│ │ │ │
│ │ │ └─── id % 2 → 分库
│ │ └─────── (id/2) % 2 → 分表id % 2:提取最低位 (bit0),用于决定分库(id.intdiv(2)) % 2:先除以2(右移1位),再取最低位,即提取次低位 (bit1),用于决定分表
完整数据分布表
| id | 二进制 | 分库 (bit0) | 分表 (bit1) | 路由结果 |
|---|---|---|---|---|
| 0 | 00 | 0 | 0 | ds_0.d_program_0 |
| 1 | 01 | 1 | 0 | ds_1.d_program_0 |
| 2 | 10 | 0 | 1 | ds_0.d_program_1 |
| 3 | 11 | 1 | 1 | ds_1.d_program_1 |
| 4 | 100 | 0 | 0 | ds_0.d_program_0 |
| 5 | 101 | 1 | 0 | ds_1.d_program_0 |
结果:所有4个分片都有数据,均匀分布!
plain
表_0 表_1
库_0 (ds_0) ✔️ ✔️ ← 偶数库的奇数表和偶数表都有数据
库_1 (ds_1) ✔️ ✔️ ← 奇数库的奇数表和偶数表都有数据关联表分片键选择
| 表 | 分片键 | 原因 |
|---|---|---|
| d_program | id | 主键,唯一标识 |
| d_program_group | id | 独立分组 |
| d_program_show_time | program_id | 与节目关联,便于查询 |
| d_seat | program_id | 与节目关联,便于查询 |
| d_ticket_category | program_id | 与节目关联,便于查询 |
设计思想:所有与节目相关的子表都使用 program_id 作为分片键,确保同一节目的所有数据落在同一个分片,提升关联查询性能。
架构图
plain
┌─────────────────────────────────────────────────────────────┐
│ Program Service │
├─────────────────────────────────────────────────────────────┤
│ ShardingSphere │
│ (基因位分离策略) │
├──────────────────────────┬──────────────────────────────────┤
│ damai_program_0 │ damai_program_1 │
├──────────────────────────┼──────────────────────────────────┤
│ d_program_0~1 │ d_program_0~1 │
│ d_program_group_0~1 │ d_program_group_0~1 │
│ d_program_show_time_0~1 │ d_program_show_time_0~1 │
│ d_seat_0~1 │ d_seat_0~1 │
│ d_ticket_category_0~1 │ d_ticket_category_0~1 │
│ d_program_record_task_0~1│ d_program_record_task_0~1 │
├──────────────────────────┴──────────────────────────────────┤
│ d_program_category (广播表) │
└─────────────────────────────────────────────────────────────┘更新: 2026-01-23 09:56:09
原文: https://www.yuque.com/u22210564/ykdrdh/sk52gx76cu3a73cc