一、分库分表
1. 基本介绍
随着业务的不断增加,应用程序所存储的数据也将随之增长,对于传统的关系型数据而言,当单表的数据达到一定量级之后增删改效率也会有一定程度的降低,分库分表的需求也就应运而生。
2. 垂直分表
对于分表而言存在两种方式,这里先介绍一下垂直分表的实现逻辑。
垂直分表即将一张宽表进行纵向拆分,将使用频次较低的数据外置通过外键实现关联,从而降低单个数据表的大小,进而提高 SQL
的执行效率。
假如存在如下一张 user
表,其表结构与数据内容如下:
通过纵向拆分,可以将低频字段如 address
外提至 user_info
表,将利用外键 u_id
实现与 user
表的关联,拆分后得到下述两张用户表。
3. 水平分表
水平分表即将同样为将一张数据表拆分为多张数据表,但与垂直分表不同的是每张表的结构一致,通过特定的算法确定一条记录的归属。
最常见的分表策略即取模分表,即以主键或其它唯一标识字段为依据,通过该字段求模取余得到值确定记录的归属,对于非数值字段可执行哈希后再进行计算。
如将 user
表拆分为 user_1
与 user_2
两张表,即可通过 id
主键与 2
求模取余,得到的结果若为 1
则存入 user_1
,否则存入 user_2
,按照此逻辑拆分即可得到下述两张表:
二、工程集成
1. 依赖导入
Sharding Sphere
是 Apache
基金会下的开源分库分表中间件,旨在提供便利的分表分表实现。
这里就不过阐述其背景而专注于具体实现,首先在 Spring Boot
工程中引入下述依赖。
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.2.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.33</version>
</dependency>
2. 测试数据
在开始之前需要先准备好测试数据库与表,新建两个数据库 test_db1
与 test_db2
,并分别在两个库下创建下述两张表。
CREATE TABLE `user_info_1`
(
`id` int(11) NOT NULL,
`name` varchar(100) DEFAULT NULL,
`gender` varchar(100) DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user_info_2`
(
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL,
`gender` varchar(100) DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3. 工程创建
完成数据库的资源新建之后,通过工具生成对应的代码,工程代码结构如下:
三、基本配置
1. 服务模式
shardingsphere
提供多种服务启动模式,如下 Standalone
表示单体启动,同理还有 Cluster
集群模式。
spring:
shardingsphere:
mode:
type: Standalone
repository:
type: JDBC
2. 日志配置
通过下述配置开启执行的 SQL
语句日志输出,与 MyBatis
中的 mybatis.configuration.log-impl
配置项效果类似。
spring:
shardingsphere:
# 日志显示具体的SQL
props:
sql-show: true
四、数据分库
1. 连接配置
通过 spring.shardingsphere.datasource
配置生效的数据源连接配置,names
用于指定数据源名称。
spring:
shardingsphere:
# 配置数据库连接信息
datasource:
names: ds0, ds1
ds0:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test_db1?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
ds1:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test_db2?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
2. 分片规则
通过 rules
配置生效的分片规则,通过 actual-data-nodes
配置实际生效的资源表,database-strategy
用于配置分库策略,sharding-algorithm-name
取的值为 sharding-algorithms
中所定义的策略。
spring:
shardingsphere:
# 配置分片规则
rules:
sharding:
# 配置所有分片表
tables:
# 配置表名
user_info:
# 声明表所在的数据节点
actual-data-nodes: ds$->{0..1}.user_info_1
# 配置分库规则
database-strategy:
standard:
# 配置分库的路由键
sharding-column: id
# 配置分片算法,指向具体的策略
sharding-algorithm-name: db-inline
# 定义分片算法
sharding-algorithms:
db-inline:
# 使用自定义表达式
type: inline
props:
algorithm-expression: ds$->{id % 2}.user_info1
3. inline表达式
这里着重介绍一下 inline
表达式的语法,在上述配置中的 $->{}
即为 inline
表达式,其语法规则如下:
ds0, ds1
==> ds$->{0..1}
==> ds$->{['0','1']}
五、数据分表
1. 连接配置
分表的数据源连接配置与分库类似,配置如下:
spring:
shardingsphere:
# 配置数据库连接信息
datasource:
names: ds0
ds0:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test_db1?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
2. 分片规则
通过 rules
配置生效的分片规则,注意其中的 user_info
配置的为逻辑表名,若数据库存在表 user_info_1
与 user_info_2
则需要配置为 user_info
。
spring:
shardingsphere:
# 配置分片规则
rules:
sharding:
# 配置所有分片表
tables:
# 配置表名
user_info:
# 声明表所在的数据节点
actual-data-nodes: ds0.user_info_$->{1..2}
# 配置分表规则
table-strategy:
standard:
# 配置分表的路由键
sharding-column: id
# 策略算法名称
sharding-algorithm-name: user-inline
# 定义分片算法
sharding-algorithms:
user-inline:
# 使用自定义表达式
type: inline
props:
algorithm-expression: user_info_$->{(id % 2) == 0 ? 2:1}