读写分离,基本的原理是让主数据库处理事务性增、改、删操作,而从数据库处理SELECT查询操作,让两者分工明确达到提高数据库整体读写性能。当然,主数据库另外一个功能就是负责将事务性查询导致的数据变更同步到从库中,也就是写操作。即主从复制和读写分离是离不开的。
mysql主从复制原理
ps:mysql的主从复制配置,不在本文讨论范围之内。不过如果对mysql主从如何配置感兴趣的朋友,可以查看这篇文章
https://www.jianshu.com/p/1ac435a6510e
当有出现上述场景时,可以考虑用读写分离,但也并非一上来就采用读写分离,我们可以优先考虑优化我们的代码逻辑,根据慢查询日志来优化sql语句,引入缓存层如redis,甚至可以引入全文搜索引擎等
基于程序代码内部实现
在代码中根据select,insert进行路由分类,这类方法也是目前生产环境应用最广泛的,优点是性能好,因为在程序代码中已经将读写的数据源拆分至两个,所以不需要额外的mysql proxy解析sql报文,在进行路由至不同数据库节点。缺点是通常该架构较复杂,运维成本相对较高。
基于中间代理层实现
代理层一般位于客户端和服务器之间,代理服务器接到客户端请求后通过解析sql文本再将sql路由至可用的数据库节点中。优点是程序不需要改造可以实现无缝迁移,可移植性较好。缺点是性能相对前者略微逊色一些,并且并不是所有的读操作都能够被路由至从节点中。
本文主要介绍基于程序代码的实现,下边进行代码层面整合介绍
这边介绍三种代码层读写分离实现
1、采用AbstractRoutingDataSource搭配AOP
该方案在之前文章spring多数据源实现一文就有介绍,之前是以xml文件配置,本文是改成javaconfig配置,其实现原理一样。因此就不再论述,其实现具体可以查看链接
http://1t.click/azCR
2、mybatis-plus提供的dynamic-datasource-spring-boot-starter
a、pom.xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
</dependency>
b、application.yml配置
spring:
datasource:
dynamic:
primary: leader
datasource:
leader:
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/boot-learning?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&serverTimezone=UTC
druid: #以下均为默认值
initial-size: 3
max-active: 8
min-idle: 2
max-wait: -1
min-evictable-idle-time-millis: 30000
max-evictable-idle-time-millis: 30000
time-between-eviction-runs-millis: 0
validation-query: select 1
validation-query-timeout: -1
test-on-borrow: false
test-on-return: false
test-while-idle: true
pool-prepared-statements: true
max-open-prepared-statements: 100
filters: stat,wall
share-prepared-statements: true
follow:
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot-learning?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&serverTimezone=UTC
druid: #以下均为默认值
initial-size: 3
max-active: 8
min-idle: 2
max-wait: -1
min-evictable-idle-time-millis: 30000
max-evictable-idle-time-millis: 30000
time-between-eviction-runs-millis: 0
validation-query: select 1
validation-query-timeout: -1
test-on-borrow: false
test-on-return: false
test-while-idle: true
pool-prepared-statements: true
max-open-prepared-statements: 100
filters: stat,wall
share-prepared-statements: true
#DruidDataSourceAutoConfigure会注入一个DataSourceWrapper,其会在原生的spring.datasource下找url,username,password等。而我们动态数据源的配置路径是变化的。
autoconfigure:
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure,io.shardingsphere.shardingjdbc.spring.boot.SpringBootConfiguration
3、在service层加上 @DS注解,通过@DS制定要操作的数据库
@Override
@Transactional
@DS("follow")
public BookDTO addBook(BookDTO bookDTO) {
Book book = dozerMapper.map(bookDTO,Book.class);
boolean isExitBookByName = ObjectUtils.isNotEmpty(getBookByName(bookDTO.getBookName()));
if(isExitBookByName){
throw new BizException("书名已经存在");
}
book.setCreateDate(new Date());
book.setUpdateDate(new Date());
baseMapper.insert(book);
bookDTO = dozerMapper.map(book,BookDTO.class);
return bookDTO;
}
通过上面3步就可以实现读写分离,更多实现细节可以查看如下链接
https://mp.baomidou.com/guide/dynamic-datasource.html
3、通过Sharding-JDBC实现
架构图
a、pom.xml
<dependency>
<groupId>io.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
</dependency>
b、application.yml配置
sharding:
jdbc:
datasource:
#Canonical names should be kebab-case ('-' separated), lowercase alpha-numeric characters and must start with a letter
names: master-ds,slave-ds
master-ds:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/boot-learning?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&serverTimezone=UTC
username: root
password: 123456
slave-ds:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot-learning?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&serverTimezone=UTC
username: root
password: 123456
config:
sharding:
props:
sql.show: true
masterslave:
load-balance-algorithm-type: round_robin
name: dataSource
master-data-source-name: master-ds
slave-data-source-names: slave-ds
spring:
main:
allow-bean-definition-overriding: true
仅需上面2步,就可以实现读写分离。更多详细配置可以查看如下链接
https://shardingsphere.apache.org/document/current/cn/manual/sharding-jdbc/configuration/config-spring-boot/
以上就是代码层读写分离的几种方案,实现都挺简单的,那这几种方案,到底选哪一种呢,如果你业务是采用mybaits-plus来做dao层,很显然就可以使用上面所述的第二种。如果不是,推荐使用sharding-jdbc来实现。其实本文也就简单介绍下读写分离,推荐大家可以看下这篇博文浅谈高性能数据库集群 —— 读写分离
【mysql 读写分离】10分钟了解读写分离的作用
https://blog.csdn.net/u013421629/article/details/78793966
MySQL读写分离最佳实践
https://www.jianshu.com/p/1ac435a6510e
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-dynamic-datasource