一、读写分离背景
分库分表虽然可以优化数据库操作,但是要实现高并发,主从架构就应运而生了。数据库的主从复制架构,将数据库的写操作定位到主库中进行,主库和从库之间通过异步复制、半同步复制保持数据一致。所有的读操作都在主库的N个从库上进行。通过负载均衡使得每一次查询均匀的落在每一个从库上。
一主n从,做读写分离(数据写入主库,通过mysql数据同步机制将主库数据同步到从库–>程序读取从库数据),多个从库之间可以实现负载均衡。次外,ShardingSphere-ShardingJdbc可手动强制部分读请求到主库上。(因为主从同步有延迟,对实时性要求高的系统,可以将部分读请求也走主库)
MySQL配置主从同步可参考另一篇博客:https://blog.csdn.net/u014553029/article/details/108832268
二、读写分离实现
2.1 环境准备
程序环境:SpringBoot+MyBatis-plus
数据库环境:
数据库ip | 数据库 | 作用 |
---|---|---|
127.0.0.1 | ShardingSphere | master |
127.0.0.1 | ShardingSphere1 | slave1 |
127.0.0.1 | ShardingSphere2 | slave2 |
2.2 添加ShardingSphere依赖
<!--shardingsphere数据分片、脱敏工具-->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.1.0</version>
</dependency>
2.3 配置读写分离规则
#### spring ####
spring:
# 配置说明地址 https://shardingsphere.apache.org/document/legacy/4.x/document/cn/manual/sharding-jdbc/configuration/config-spring-boot/#%E6%95%B0%E6%8D%AE%E5%88%86%E7%89%87
shardingsphere:
# 数据库
datasource:
# 数据库的别名
names: ds0,ds1,ds2
# 主库1 ,master数据库
ds0:
### 数据源类别
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://146.56.192.87:3306/shardingsphere?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
username: oyc
password: oyc@123456
# 从库1 ,slave数据库
ds1:
### 数据源类别
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://146.56.192.87:3306/shardingsphere1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
username: oyc
password: oyc@123456
# 从库2 ,slave数据库
ds2:
### 数据源类别
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://146.56.192.87:3306/shardingsphere2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
username: oyc
password: oyc@123456
# *** 数据库分库分表配置 start
masterslave:
# 查询时的负载均衡算法,目前有2种算法,round_robin(轮询)和random(随机),
# 算法接口是io.shardingjdbc.core.api.algorithm.masterslave.MasterSlaveLoadBalanceAlgorithm。
# 实现类有RandomMasterSlaveLoadBalanceAlgorithm 和 RoundRobinMasterSlaveLoadBalanceAlgorithm。
load-balance-algorithm-type: round_robin
name: dataSource
# 主数据源名称
master-data-source-name: ds0
# 从数据源名称,多个用逗号隔开
slave-data-source-names: ds1,ds2
# *** 数据库分库分表配置 end
props:
# 打印SQL
sql.show: true
check:
table:
metadata: true
# 是否在启动时检查分表元数据一致性
enabled: true
query:
with:
cipher:
column: true
2.4 测试读写分离效果
(1)写入数据,都是写主库:
(2)读取数据,根据规则负载从从库选择读取;
2.5 强制读取主库数据
读延迟问题解决方案:
刚插入一条数据,然后马上就要去读取,这个时候有可能会读取不到?归根到底是因为主节点写入完之后数据是要复制给从节点的,读不到的原因是复制的时间比较长,也就是说数据还没复制到从节点,你就已经去从节点读取了,肯定读不到。mysql5.7 的主从复制是多线程了,意味着速度会变快,但是不一定能保证百分百马上读取到,这个问题我们可以有两种方式解决:
(1)业务层面妥协,是否操作完之后马上要进行读取
(2)对于操作完马上要读出来的,且业务上不能妥协的,我们可以对于这类的读取直接走主库,当然Sharding-JDBC也是考虑到这个问题的存在,所以给我们提供了一个功能,可以让用户在使用的时候指定要不要走主库进行读取。在读取前使用下面的方式进行设置就可以了:
/**
* 用户列表,强制路由主库
*/
@RequestMapping("ds0")
public List<User> userListDs0() {
logger.info("********TestController userListDs0():强制路由主库");
HintManager hintManager = HintManager.getInstance();
List<User> users = userMapper.selectList(null);
//清除分片键值,分片键值保存在ThreadLocal中,所以需要在操作结束时调用hintManager.close()来清除ThreadLocal中的内容。hintManager实现了AutoCloseable接口,可推荐使用try with resource自动关闭。
hintManager.close();
List<User> users1 = userMapper.selectList(null);
users.addAll(users1);
return users;
}
强制路由到主库:
强制读取主库结果:
从上图可以看出,我们设置的强制读取主库之后,每次查询都是去获取主库的数据。
官网传送门:https://shardingsphere.apache.org/document/legacy/4.x/document/cn/manual/sharding-jdbc/usage/read-write-splitting/
源码传送门:https://github.com/oycyqr/springboot-learning-demo/tree/master/springboot-shardingsphere-load
来源:oschina
链接:https://my.oschina.net/u/4283847/blog/4690772