摘要
关注公众号回复关键字【 基于Spring的数据库读写分离 】获取完整实现代码。
1.数据库读写分离
数据库读写分离的实现主要有两种方式:
1.1 基于中间件
提供一个统一的中间件,程序连接到中间件,中间件帮我们做读写分离,例如MyCat。
基于中间件的实现在数据库作扩容增加负载节点时,业务应用无感知,不需要修改任何代码都可以获取连接到新的节点,当然实现起来相对复杂。
1.2 基于程序自实现
每个业务应用实现自己的读写分离,优点实现简单,但如果读写的负载节点发生变化时,必须要修改业务应用代码。
本文的实现我们基于Spring的AbstractRoutingDataSource来实现。
2. 主从数据库搭建
# 假设你现在已经在dbrouter模块(目录)下
cd master-slave-db
./start.sh
使用start.sh可以一键搭建MySQL主从数据库。该脚本的主逻辑如下:
2.1 创建测试表
create table t_user(
id bigint auto_increment primary key,
name varchar(32) default null,
create_time timestamp default current_timestamp,
update_time timestamp default current_timestamp
);
3. 基于AbstractRoutingDataSource实现多数据源切换
整个实现大约有以下核心类:
4. @Transactional
通过@Transactional开启事务以后,在获取到数据源建立连接后,后面不会再对数据源进行切完,直至整个事务完成。
一般我们在开启事务的时候往往是因为业务逻辑中包含多个写操作,需要一起失败或者一起成功,既然需要写操作,我们必须保证我们建立的数据库连接 是与Master库建立。核心实现是:
保证我开启事务时拿到的必须是Master数据源,不管方法上是否有自定义的@DataSourceName注解,代码如下:
public static class DataSourceHolder {
private static final ThreadLocal<DataSourceName> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 此处假设没有设置数据源的话,默认为写数据源(Master)
* @return
*/
public static DataSourceName getDataSource() {
DataSourceName sourceName = CONTEXT_HOLDER.get();
return Objects.isNull(sourceName) ? DataSourceName.WRITE : sourceName;
}
}
/**
* 此处的Order保证了数据源的设置必定晚于事务与数据库建立连接
* 保证了@Transactional建立的数据库连接必定是默认的主库连接
*/
@Aspect
@Component
@Order
public class DynamicDataSourceAspect {
}
5. 测试
测试类位于dbroute模块下的test模块,类名为DataSourceRouteTest。
本期的数据库读写分离就介绍到这,我是shysh95,我们下期再见!