分库分表
垂直分库: 按业务拆**。**把不同的表放到不通的数据库中。
**水平分库:**同一张表拆成多个表,放到不同的库中(相同表结构的数据库就是水平分表)。
**水平分表:**一张表拆数据行(把一张表横着拆成多张,一般与水平分库一起用)。
**垂直分表:**把一张大表的列拆开。
分片 = 总称
分库 = 分片的一种(拆数据库)
分表 = 分片的一种(拆表)
水平分表(不与水平分库一起用)
bash
db_order
├── t_order_0
├── t_order_1
├── t_order_2
├── t_order_3
水平分库与水平分表一起用
bash
db_order_0
├── t_order_0
├── t_order_1
db_order_1
├── t_order_0
├── t_order_1
垂直分库 + 水平分库 + 水平分表(最典型)
bash
db_user
t_user
对t_order进行水平分库和分表
db_order_0
t_order
db_order_1
t_order
只水平分库
bash
db_order_0
t_order
db_order_1
t_order
分库分表的核心思想:数据分散存储,使得单一数据库/表的数据量变小来缓解单一数据库的性能问题,从而达到提升数据库性能的目的。

**垂直分库:**以表为依据,根据业务将不同表拆分到不同库中。
- 每个库的表结构都不一样。
- 每个库的数据也不一样。
- 所有库的并集是全量数据。
**垂直分表:**以字段为依据,根据字段属性将不同字段拆分到不同表中。
- 每个表的结构都不一样。
- 每个表的数据也不一样,一般通过一列(主键/外键)关联。
- 所有表的并集是全量数据。

**水平分库:**以字段为依据,按照一定策略,将一个库的数据拆分到多个库中。
- 每个库的表结构都一样。
- 每个库的数据都不一样。
- 所有库的并集是全量数据。
**水平分表:**以字段为依据,按照一定策略,将一个表的数据拆分到多个表中。
- 每个表的表结构都一样。
- 每个表的数据都不一样。
- 所有表的并集是全量数据。
配置环境
JDK8,SpringBoot(2.3.9.RELEASE),ShardingSphere-jdbc(5.1.1),MySQL8(一个主库,两个从库配置好主从复制)。
Maven依赖
XML
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
读写分离配置
ShardingSphere启动模式:
| 模式 | 作用 |
|---|---|
| Memory | 规则存在JVM内存 |
| Standalone | 单机持久化 |
| Cluster | 注册中心模式 |
负载均衡算法常用ROUND_ROBIN、RANDOM。
java
spring:
application:
name: sharding-jdbc-dome
profiles:
active: dev
shardingsphere:
mode:
type: Memory
datasource:
# 声明所有真实数据源的名称。
#
# 这里定义了3个真实数据库连接:
# master:主库
# slave1:第1个从库
# slave2:第2个从库
#
# names中的名称必须与下面的数据源配置名称一致。
# 名称不是固定的,也可以写成:
# write-db,read-db-1,read-db-2
names: master,slave1,slave2
# 主库真实数据源配置。
master:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:33388/db_user
username: root
password: 123456
# 第1个从库真实数据源配置。
slave1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:33307/db_user
username: root
password: 123456
# 第2个从库真实数据源配置。
slave2:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:33308/db_user
username: root
password: 123456
rules:
# 读写分离规则。
# ShardingSphere只负责SQL路由,不负责MySQL数据同步。
# master到slave1、slave2的数据同步仍由MySQL主从复制完成。
readwrite-splitting:
data-sources:
# 逻辑读写分离数据源名称。
# myds不是一个真实MySQL数据库。
# 它代表master、slave1、slave2组成的读写分离数据源组。
# 名称可以自定义,例如:readwrite-ds,user-ds
myds:
# 读写分离类型。
#
# Static:静态读写分离。直接在配置文件中明确指定主库和从库。适用于固定的一主多从架构。
# Dynamic:动态读写分离。与数据库发现规则配合使用,根据数据库集群状态动态识别主库和从库。不直接在这里固定写库和读库名称。
#
# 现在明确知道master、slave1、slave2,
# 因此应该使用Static。
type: Static
props:
# 写数据源名称。
#
# 必须引用datasource.names中已经声明的数据源。
# INSERT、UPDATE、DELETE等写操作会路由到master。
write-data-source-name: master
# 读数据源名称列表。
#
# 多个从库之间使用英文逗号分隔。
# 普通查询会按照负载均衡算法,
# 在slave1和slave2之间选择。
read-data-source-names: slave1,slave2
# 当前读写分离数据源使用的负载均衡算法名称。
#
# alg_round是自定义名称,并不是固定值。
# 必须与下面load-balancers中的名称保持一致。
load-balancer-name: alg_round
load-balancers:
# 负载均衡算法实例名称。
#
# alg_round可以任意命名,例如:
# round-robin-algorithm,read-random
# 只要与上面的load-balancer-name保持一致即可。
alg_round:
# 从库负载均衡算法类型。
# ROUND_ROBIN:轮询。
# RANDOM:随机。
# WEIGHT:权重。
type: ROUND_ROBIN
props:
# 是否输出ShardingSphere的SQL解析和路由日志,可选值:
# true:输出逻辑SQL、实际SQL以及实际访问的数据源。
# false:不输出相关SQL路由日志。
sql-show: true
下面是权重类型的写法,其它的不变,Double越大,权重越大。
bash
load-balancer-name: olg_weight
load-balancers:
olg_weight:
type: WEIGHT
props:
slave1: 1.0
slave2: 2.0
如果在同一个事务下,有增删改也有查询,只会查询主数据库,而不是从数据库。
java
@Transactional
@Test
void transactionTest(){
User user = new User();
user.setUname("张三丰");
userMapper.insert(user);
List<User> users = userMapper.selectList(null);
System.out.println(users);
}
垂直分库+水平分库+水平分表 配置
环境说明
-
一个逻辑表:t_order
-
两个真实数据库:
-
server-order0 → MySQL数据库 db_order
-
server-order1 → MySQL数据库 db_order
-
-
每个数据库两个真实表:
-
t_order0
-
t_order1
-
最终形成:2个库 × 2张表 = 4张真实订单表,对应的配置如下:
bash
t_order:
actual-data-nodes:
server-order$->{0..1}.t_order$->{0..1}
分库规则
bash
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: alg_mod
配置说明根据:user_id,决定数据库算法:偶数用户:server-order0,奇数用户:server-order1
bash
alg_mod:
type: MOD
props:
sharding-count: 2 # 针对sharding-column: user_id字段,对2取模
| user_id | 计算 | 数据库 |
|---|---|---|
| 1 | 1%2=1 | server-order1 |
| 2 | 2%2=0 | server-order0 |
| 3 | 3%2=1 | server-order1 |
| 4 | 4%2=0 | server-order0 |
分表规则
table-strategy:
standard:
sharding-column: order_no
sharding-algorithm-name: alg_hash_mod
表示同一个数据库里面,再根据:order_no,决定放到哪个表中。
bash
alg_hash_mod:
type: HASH_MOD
props:
sharding-count: 2 # 针对sharding-column: user_id字段,对2取模
例如:订单号:hash(10001)%2=0 进入t_order0,另一个:hash(10002)%2=1 进入:t_order1。
雪花ID
id字段自动生成,id字段要配合mybatis-plus的"@TableId(type = IdType.AUTO)"。
XML
t_order:
key-generate-strategy:
column:id
key-generator-name:alg_snowflake
完整配置文件
XML
spring:
application:
name: sharding-jdbc-dome
profiles:
active: dev
shardingsphere:
mode:
type: Memory
datasource:
names: server-user,server-order0,server-order1
server-user:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:33008/db_user?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
server-order0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:33016/db_order?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
server-order1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:33017/db_order?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
props:
sql-show: true
rules:
sharding:
tables:
t_user:
actual-data-nodes: server-user.t_user
t_order:
actual-data-nodes: server-order$->{0..1}.t_order$->{0..1}
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: alg_mod
table-strategy:
standard:
sharding-column: order_no
sharding-algorithm-name: alg_hash_mod
key-generate-strategy:
column: id
key-generator-name: alg_snowflake
sharding-algorithms:
alg_inline_userId:
type: INLINE
props:
algorithm-expression: server-order$->{user_id % 2}
alg_mod:
type: MOD
props:
sharding-count: 2 # 针对sharding-column: user_id字段,对2取模
alg_hash_mod:
type: HASH_MOD
props:
sharding-count: 2 # 针对sharding-column: user_id字段,对2取模
key-generators:
alg_snowflake:
type: SNOWFLAKE
读写分离
环境说明
- 一个主库(master)负责写
- 两个从库(slave1、slave2)负责读
- 查询自动分发到从库
- 插入、更新、删除自动走主库
注意:如果是在一个事务中进行了增删改,之后并执行了查询,那么此时为了保证数据一致性,此时会自动查询主库。
配置读写分离规则
XML
rules:
readwrite-splitting:
创建一个逻辑数据源
myds对应的是:"master slave1 slave2"三个数据源。
XML
data-sources:
myds:
手动指定读写库
XML
myds:
type: Static
props:
write-data-source-name: master
read-data-source-names: slave1,slave2
负载均衡配置
XML
load-balancers:
alg_round:
type: ROUND_ROBIN
# 轮训或者按权重
olg_weight:
type: WEIGHT
props:
slave1: 1.0
slave2: 2.0
完整配置文件
XML
spring:
application:
name: sharding-jdbc-dome
profiles:
active: dev
shardingsphere:
mode:
type: Memory
datasource:
names: master,slave1,slave2
master:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:33388/db_user
username: root
password: 123456
slave1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:33307/db_user
username: root
password: 123456
slave2:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:33308/db_user
username: root
password: 123456
rules:
readwrite-splitting:
data-sources:
myds:
type: Static
props:
write-data-source-name: master
read-data-source-names: slave1,slave2
load-balancer-name: olg_weight
load-balancers:
alg_round:
type: ROUND_ROBIN
olg_weight:
type: WEIGHT
props:
slave1: 1.0
slave2: 2.0
props:
sql-show: true
绑定表
环境说明
订单系统这种"一张主表 + 多张关联子表",并且数据量很大,需要分库分表的场景。
适用于order和order_item这种同一个订单的数据、生命周期一致的表,保证在分库分表的情况下,order和order_item在同一个数据库中,而不是分散在不同的数据库中,避免跨库关联查询,提高关联查询效率。
使用场景
这个配置是在前面的水平分库分表 基础上,又增加了一个非常重要的概念:绑定表。经常一起JOIN,并且分片规则完全一样的表。
**解决:**分库分表之后,多表关联查询如何避免产生大量无效查询(笛卡尔积)。
XML
SpringBoot应用
|
ShardingSphere-JDBC
|
---------------------------------
| |
server-order0 server-order1
| |
t_order0 t_order0
t_order1 t_order1
t_order_item0 t_order_item0
t_order_item1 t_order_item1
ShardingSphere的绑定表支持多个表组成一组,也支持配置多个绑定表组。
使用前提:多个表的分库分表规则必须完全一致。
SQL联查绑定表关联时,最好使用**分片键(**例如:order_no)进行关联。
sql
SELECT o.order_no, SUM(i.price * i.count) AS amount
FROM t_order o
JOIN t_order_item i ON o.order_no = i.order_no
GROUP BY o.order_no;
完整配置文件
spring:
application:
name: sharding-jdbc-dome
profiles:
active: dev
shardingsphere:
mode:
type: Memory
datasource:
names: server-user,server-order0,server-order1
server-user:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:33008/db_user?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
server-order0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:33016/db_order?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
server-order1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:33017/db_order?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
props:
sql-show: true
rules:
sharding:
tables:
t_user:
actual-data-nodes: server-user.t_user
t_order:
actual-data-nodes: server-order$->{0..1}.t_order$->{0..1}
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: alg_mod
table-strategy:
standard:
sharding-column: order_no
sharding-algorithm-name: alg_hash_mod
key-generate-strategy:
column: id
key-generator-name: alg_snowflake
t_order_item:
actual-data-nodes: server-order$->{0..1}.t_order_item$->{0..1}
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: alg_mod
table-strategy:
standard:
sharding-column: order_no
sharding-algorithm-name: alg_hash_mod
key-generate-strategy:
column: id
key-generator-name: alg_snowflake
sharding-algorithms:
alg_inline_userId:
type: INLINE
props:
algorithm-expression: server-order$->{user_id % 2}
alg_mod:
type: MOD
props:
sharding-count: 2 # 针对sharding-column: user_id字段,对2取模
alg_hash_mod:
type: HASH_MOD
props:
sharding-count: 2 # 针对sharding-column: user_id字段,对2取模
key-generators:
alg_snowflake:
type: SNOWFLAKE
# 绑定表:指分片规则一致的一组分片表。 使用绑定表进行多表关联查询时,必须使用分片键进行关联,
# 否则会出现笛卡尔积关联或跨库关联,从而影响查询效率。
binding-tables[0]: t_order,t_order_item # 去掉笛卡尔积的关联
广播表
指所有的分片数据源中都存在的表,表结构及其数据在每个数据库中均完全一致。 适用于数据量不大且需要与海量数据的表进行关联查询的场景,例如:字典表,商品分类表。订单状态表。
广播具有以下特性:
(1)插入、更新操作会实时在所有节点上执行,保持各个分片的数据一致性
(2)查询操作,只从一个节点获取
(3)可以跟任何一个表进行 JOIN 操作
环境说明:
- 需要提前在这3个数据库中创建结构完全相同的t_dict表:
- 逻辑表 t_dict 对应3张真实表:
- server-user对应的db_user.t_dict
- server-order0对应的db_order.t_dict
- server-order1对应的db_order.t_dict
核心配置
逻辑表 t_dict 对应3张真实表:
t_dict:
actual-data-nodes: server-order$->{0..1}.t_dict,server-user.t_dict
broadcast-tables[0]: t_dict
完整配置文件
spring:
application:
name: sharding-jdbc-dome
profiles:
active: dev
shardingsphere:
mode:
type: Memory
datasource:
names: server-user,server-order0,server-order1
server-user:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:33008/db_user?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
server-order0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:33016/db_order?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
server-order1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:33017/db_order?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
props:
sql-show: true
rules:
sharding:
tables:
t_user:
actual-data-nodes: server-user.t_user
t_order:
actual-data-nodes: server-order$->{0..1}.t_order$->{0..1}
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: alg_mod
table-strategy:
standard:
sharding-column: order_no
sharding-algorithm-name: alg_hash_mod
key-generate-strategy:
column: id
key-generator-name: alg_snowflake
t_order_item:
actual-data-nodes: server-order$->{0..1}.t_order_item$->{0..1}
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: alg_mod
table-strategy:
standard:
sharding-column: order_no
sharding-algorithm-name: alg_hash_mod
key-generate-strategy:
column: id
key-generator-name: alg_snowflake
t_dict:
actual-data-nodes: server-order$->{0..1}.t_dict,server-user.t_dict
sharding-algorithms:
alg_inline_userId:
type: INLINE
props:
algorithm-expression: server-order$->{user_id % 2}
alg_mod:
type: MOD
props:
sharding-count: 2 # 针对sharding-column: user_id字段,对2取模
alg_hash_mod:
type: HASH_MOD
props:
sharding-count: 2 # 针对sharding-column: user_id字段,对2取模
key-generators:
alg_snowflake:
type: SNOWFLAKE
# 绑定表:指分片规则一致的一组分片表。 使用绑定表进行多表关联查询时,必须使用分片键进行关联,
# 否则会出现笛卡尔积关联或跨库关联,从而影响查询效率。
binding-tables[0]: t_order,t_order_item # 去掉笛卡尔积的关联
broadcast-tables[0]: t_dict