采用Sharding-Jdbc对mysql 读写分离、分库分表
一、ShardingSphere介绍
ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy 和 Sharding-Sidecar这3款相互独立的产品组成。他们均提供标准化的数据分片、分布式事务 和 数据库治理功能,可满足各种多样化的应用场景。
官网:http://shardingsphere.apache.org/index_zh.html
SpringBoot配置地址:https://shardingsphere.apache.org/document/legacy/4.x/document/cn/manual/sharding-jdbc/configuration/config-spring-boot/
行表达式地址:https://shardingsphere.apache.org/document/current/cn/features/sharding/concept/inline-expression/
二、Sharding-Jdbc与MyCat区别
MyCat是一个基于第三方应用中间件数据库代理框架,客户端所有的jdbc请求都必须要先交给MyCat,再有MyCat转发到具体的真实服务器中。Sharding-Jdbc是一个Jar形式,在本地应用层重写Jdbc原生的方法,实现数据库分片形式。MyCat属于服务器端数据库中间件,而Sharding-Jdbc是一个本地数据库中间件框架。
三、什么时候分表分库?
单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
摘自:阿里巴巴java开发手册
四、Sharding-Jdbc 实现读写分离
-
mysql 主从复制
配置读写分离必须依托于mysql的主从复制,主从复制的配置可参考我另一篇博客:https://blog.csdn.net/qq_43692950/article/details/107731431
-
新建SpringBoot项目,配置pom
<!-- sharding -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<!--阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.14</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
- application.yml
spring:
main:
allow-bean-definition-overriding: true
shardingsphere:
datasource:
names:
master,slave
# 主数据源
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.1.1:3306/db1?characterEncoding=utf-8
username: root
password: root
# 从数据源
slave:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.1.2:3306/db2?characterEncoding=utf-8
username: root
password: root
masterslave:
# 读写分离配置 ROUND_ROBIN(轮询),RANDOM(随机)
load-balance-algorithm-type: round_robin
# 最终的数据源名称
name: dataSource
# 主库数据源名称
master-data-source-name: master
# 从库数据源名称列表,多个逗号分隔
slave-data-source-names: slave
props:
# 开启SQL显示,默认false
sql:
show: true
其中 load-balance-algorithm-type为有多个数据库时的规则,可选择轮询或随机。props.sql.show=true为开起日志,会打印当前使用的数据库和sql。
- 实体类
@Data
@TableName("user")
public class UserEntity {
@TableId(value = "id",type = IdType.AUTO)
private Integer id;
private String username;
private String passwd;
public UserEntity(){
}
public UserEntity(String username, String passwd) {
this.username = username;
this.passwd = passwd;
}
}
- Mapper
@Mapper
public interface UserMapper extends BaseMapper<UserEntity> {
}
- 使用
@RestController
public class TestController {
@Autowired
UserMapper userMapper;
@GetMapping("/AddUser/{username}/{passwd}")
public String AddUser(@PathVariable("username")String username,@PathVariable("passwd")String passwd){
if (userMapper.insert(new UserEntity(username,passwd)) > 0){
return "Success";
}
return "Fail";
}
@GetMapping("/GetAllUser")
public List<UserEntity> GetAllUser(){
return userMapper.selectList(null);
}
@GetMapping("/GetMasterAllUser")
public List<UserEntity> GetMasterAllUser(){
HintManager.getInstance().setMasterRouteOnly();
return userMapper.selectList(null);
}
}
其中 HintManager.getInstance().setMasterRouteOnly();为强制使用主表查询,因为在有些时候可能因为网络的震荡,主数据库的数据同步给从数据库产生延时,对数据插入后紧接着要查询的情况下可以使用这个api强制主数据库查询。
- 接着调用上述接口可以看到打印的日志,在插入时走的master,查询时走的slave,强制使用主表查询的日志为master。
五、Sharding-Jdbc 实现单库分表拆分
下面演示分表分库规则都是根据id取模计算的。下面实现根据id %3,将数据均匀的放到一个数据库中的三个表中,实现数据的分表操作。
- 为演示功能先在数据库建三个简单的测试表 user_info_0,user_info_1,user_info_2, 表里只有id,name两个字段。
- application.yml
server:
port: 8082
#单库水平分割
spring:
main:
allow-bean-definition-overriding: true
shardingsphere:
datasource:
names: db1 # 配置数据源,给数据源起名db1,db2...此处可配置多数据源
# 配置数据源具体连接内容
db1:
url: jdbc:mysql://192.168.1.1:3306/db1?characterEncoding=utf-8&useUnicode=true&useSSL=false&serverTimezone=UTC
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
# 配置表的分布,表的策略
sharding:
tables:
user_info:
actual-data-nodes: db1.user_info_$->{0..2}
# 指定user_info表 主键id 生成策略为 SNOWFLAKE
key-generator:
column: id
type: SNOWFLAKE
# 指定分片策略 约定id % 3 确定存入哪张表
table-strategy:
inline:
sharding-column: id
algorithm-expression: user_info_$->{id % 3 }
# 打开sql输出日志
props:
sql:
show: true
- 实体类
@Data
public class UserInfo {
private int id;
private String name;
public UserInfo(){}
public UserInfo(int id, String name) {
this.id = id;
this.name = name;
}
}
- Mapper
@Mapper
public interface UserInfoMapper extends BaseMapper<UserInfo> {
}
- 使用
@RestController
public class TestController {
@Autowired
UserInfoMapper userInfoMapper;
@GetMapping("/AddUser/{id}/{name}")
public String AddUser(@PathVariable("id")int id,@PathVariable("name")String name){
if (userInfoMapper.insert(new UserInfo(id,name))>0){
return "Success";
}
return "Fail";
}
@GetMapping("/FindUsers")
public Object FindUser(){
return userInfoMapper.selectList(null);
}
@GetMapping("/FindUser/{id}")
public Object FindUser(@PathVariable("id")int id){
return userInfoMapper.selectById(id);
}
}
5.测试
当请求:http://localhost:8082/AddUser/0/test时,打印日志:
查看user_info_0表中的数据:
当请求:http://localhost:8082/AddUser/2/test时,打印日志:
查看user_info_2表中的数据:
以此类推,根据id%3,将数据放置不同的表同。
当执行查询时:
会自动将所有的数据规整为一起,但最好不要查询全部,他会分别查询这三个表然后合到一起返回,数据量大时效率比较低,最好根据id查询。
以上便实现数据分表,下面实现分库单表。
五、Sharding-Jdbc 实现分库水平拆分
下面实现根据id %2,将数据均匀的放到两个个数据库中的user_info表中,实现数据的分库操作。
- 新建db1,db2数据库,库中各建user_info表,表中字段均为id,name。
- application.yml
#多库水平分割
spring:
main:
allow-bean-definition-overriding: true
shardingsphere:
datasource:
names: db1,db2 # 配置数据源,给数据源起名db1,db2...此处可配置多数据源
# 配置数据源具体连接内容
db1:
url: jdbc:mysql://192.168.1.2:3306/db1?characterEncoding=utf-8&useUnicode=true&useSSL=false&serverTimezone=UTC
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
db2:
url: jdbc:mysql://192.168.1.1:3306/db2?characterEncoding=utf-8&useUnicode=true&useSSL=false&serverTimezone=UTC
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
# 配置库分布,库的策略
sharding:
tables:
user_info:
actual-data-nodes: db$->{1..2}.user_info
# 指定user_info表 主键id 生成策略为 SNOWFLAKE
key-generator:
column: id
type: SNOWFLAKE
database-strategy:
inline:
sharding-column: id
algorithm-expression: db$->{id % 2 +1}
# 打开sql输出日志
props:
sql:
show: true
- 实体类和Mapper及Controller和上面单裤多表使用相同。
- 测试
当访问:http://localhost:8082/AddUser/0/test0 时,打印日志:
查看db1中的数据为:
当访问:http://localhost:8082/AddUser/1/test1 时,打印日志:
查看db2中的数据为:
以此类推,数据会根据id%2,均匀的放到两个不同的数据库中,实现分库的想过。
当进行查询时,同样也会合并为一起返还给调用者:
上面便实现了,分库的效果,但有时候数据量会非常大,使用在一个库中分n张表,或分n个相同的库确实也可以解决,但维护的成本会相应增大,不如使用多库多表的方式,有多个数据库,每个库中又有多个表。
六、Sharding-Jdbc 实现多库多表拆分
建立两个数据库db1,db2,每个数据库中包含三张表(注意:如果根据id取模的方式分库分表,库的个数和表的个数不要同为偶数或同为奇数,一偶一奇才能均匀分布)
- 库:db1,db2,表:user_info_0,user_info_1,user_info_2,字段:id,name。
- application.yml
#单库水平分割
spring:
main:
allow-bean-definition-overriding: true
shardingsphere:
datasource:
names: db1,db2 # 配置数据源,给数据源起名db1,db2...此处可配置多数据源
# 配置数据源具体连接内容
db1:
url: jdbc:mysql://192.168.1.2:3306/db1?characterEncoding=utf-8&useUnicode=true&useSSL=false&serverTimezone=UTC
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
db2:
url: jdbc:mysql://192.168.1.1:3306/db2?characterEncoding=utf-8&useUnicode=true&useSSL=false&serverTimezone=UTC
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
# 配置库和表的分布,库和表的策略
sharding:
tables:
user_info:
actual-data-nodes: db$->{1..2}.user_info_$->{0..2}
# 指定user_info表 主键id 生成策略为 SNOWFLAKE
key-generator:
column: id
type: SNOWFLAKE
#数据库的策略
database-strategy:
inline:
sharding-column: id
algorithm-expression: db$->{id % 2 +1}
#表的策略
table-strategy:
inline:
sharding-column: id
algorithm-expression: user_info_$->{id % 3 }
# 打开sql输出日志
props:
sql:
show: true
- 实体类和Mapper及Controller和上面单裤多表使用相同。
- 当执行下面接口时:
http://localhost:8082/AddUser/0/test0
http://localhost:8082/AddUser/1/test1
http://localhost:8082/AddUser/2/test2
http://localhost:8082/AddUser/3/test3
http://localhost:8082/AddUser/4/test4
http://localhost:8082/AddUser/5/test5
控制台打印的日志为:
数据库中的结果:
多库多表的分库分表效果便实现了,从实现的过程可以看出,Sharding-jdbc的配置已经非常简单了。