引言
读写分离要做的事情就是对于一条SQL该选择哪个数据库去执行,至于谁来做选择数据库这件事儿,无非两个,要么中间件帮我们做,要么程序自己做。因此,一般来讲,读写分离有两种实现方式。
- 第一种是依靠中间件(比如:MyCat),也就是说应用程序连接到中间件,中间件帮我们做SQL分离;
- 第二种是应用程序自己去做分离。
这里我们选择程序自己来做,主要是利用Spring提供的路由数据源,以及AOP
然而,应用程序层面去做读写分离最大的弱点(不足之处)在于无法动态增加数据库节点,因为数据源配置都是写在配置中的,新增数据库意味着新加一个数据源,必然改配置,并重启应用。当然,好处就是相对简单。
简要原理
配置多个数据源,连接不同的数据库,当执行修改数据的操作时,用master数据源,然后master 将数据同步到从库slave,当 查询时,切换到从库数据源,这个过程时靠aop 的方式控制的。
准备工作
- 配置多个数据库
https://blog.youkuaiyun.com/houdezaiwu1/article/details/90260359 - 配置主从复制
https://blog.youkuaiyun.com/houdezaiwu1/article/details/90481530
配置多个数据源
1. properties文件
## 主数据源配置
spring.datasource.master.driver-class-name=@datasource.driver@
spring.datasource.master.url=@datasource.url@
spring.datasource.master.username=@datasource.username@
spring.datasource.master.password=@datasource.password@
spring.datasource.master.max-idle=10
spring.datasource.master.max-wait=10000
spring.datasource.master.min-idle=5
spring.datasource.master.initial-size=5
## slqve1 第二个数据源配置
spring.datasource.slave1.driver-class-name=@slave1.driver@
spring.datasource.slave1.url=@slave1.url@
spring.datasource.slave1.username=@slave1.username@
spring.datasource.slave1.password=@slave1.password@
spring.datasource.slave1.max-idle=10
spring.datasource.slave1.max-wait=10000
spring.datasource.slave1.min-idle=5
spring.datasource.slave1.initial-size=5
## slqve1 第三个数据源配置
spring.datasource.slave2.driver-class-name=@slave2.driver@
spring.datasource.slave2.url=@slave2.url@
spring.datasource.slave2.username=@slave2.username@
spring.datasource.slave2.password=@slave2.password@
spring.datasource.slave2.max-idle=10
spring.datasource.slave2.max-wait=10000
spring.datasource.slave2.min-idle=5
spring.datasource.slave2.initial-size=5
2 pom文件
<!-- mysql 主库 -->
<datasource.driver>com.mysql.jdbc.Driver</datasource.driver>
<datasource.url>jdbc:mysql://127.0.0.1:3306/master1</datasource.url>
<datasource.username>root</datasource.username>
<datasource.password>123456</datasource.password>
<!-- slave1 从库-->
<slave1.driver>com.mysql.jdbc.Driver</slave1.driver>
<slave1.url>jdbc:mysql://127.0.0.1:3310/master1</slave1.url>
<slave1.username>root</slave1.username>
<slave1.password>123456</slave1.password>
<!-- slave2 从库-->
<slave2.driver>com.mysql.jdbc.Driver</slave2.driver>
<slave2.url>jdbc:mysql://127.0.0.1:3320/master1</slave2.url>
<slave2.username>root</slave2.username>
<slave2.password>123456</slave2.password>
3. DatabaseType
列出所有的数据源的key—key
package com.example.config.datasource;
/**
* 1.定义枚举类数据源类型 key
*/
public enum DatabaseType {
master,slave1,slave2
}
4 DatabaseContextHolder
创建线程安全的类,作为databaseType容器,放master,slave1,slave2,并提供了向其中设置和获取DatabaseType的方法,由于从库有两个,这里简单设置了一个负载均衡。
package com.example.config.datasource;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 2. 创建线程安全的类,作为databaseType容器,放master,slave1,slave2
*/
public class DatabaseContextHolder {
private static final ThreadLocal<DatabaseType> contextHolder