自动读写分离
场景:
- 在纯的读写分离环境,写操作全部是 master,读操作全部是 slave。
- 不想通过注解配置完成以上功能。
答:在 mybatis 环境下可以基于 mybatis 插件结合本数据源完成以上功能。
手动注入插件。
@Bean
public MasterSlaveAutoRoutingPlugin masterSlaveAutoRoutingPlugin(){
return new MasterSlaveAutoRoutingPlugin();
}
默认主库名称 master,从库名称 slave。
暂不支持更多,源码简单可参考重写。
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
@Slf4j
public class MasterSlaveAutoRoutingPlugin implements Interceptor {
private static final String MASTER = "master";
private static final String SLAVE = "slave";
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
try {
DynamicDataSourceContextHolder.push(SqlCommandType.SELECT == ms.getSqlCommandType() ? SLAVE : MASTER);
return invocation.proceed();
} finally {
DynamicDataSourceContextHolder.clear();
}
}
@Override
public Object plugin(Object target) {
return target instanceof Executor ? Plugin.wrap(target, this) : target;
}
@Override
public void setProperties(Properties properties) {
}
}
行为要点
SELECT→slave(组);INSERT/UPDATE/DELETE→master(组)——增删改不可能路由到从库,这是设计约束(#687);- 位于 Spring 事务内时,事务连接已绑定(通常就是主库),插件的切换不会改变已绑定的连接——事务内读写都走同一库,天然规避「事务内读写不一致」;
- 只在 MyBatis 层生效,
JdbcTemplate等其他访问方式不受把控。
插件固定使用 master / slave 两个 key;把成员命名为 master_1、slave_1、slave_2 即可获得组内负载均衡,详见自定义负载均衡策略。
三种实现方式
| 方式 | 适用场景 | 机制 |
|---|---|---|
@DS("slave") 显式注解 | 想精确控制每一次查询走哪个库 | AOP 切面 |
MasterSlaveAutoRoutingPlugin | 纯读写分离、不想改代码 | MyBatis 拦截器:SELECT 自动走 slave,其余自动走 master |
| 与 ShardingSphere 组合 | 同时需要分库分表 | ShardingSphere 管分片,dynamic 管多数据源 |
相关文档
- 自定义负载均衡策略 —— 组名规则与故障转移 FAQ
- 集成 ShardingJDBC
- @DS 与线程 —— 拦截器里统一切换时的还原要求