跳到主要内容
版本:4.6.0

自动读写分离

场景:

  1. 在纯的读写分离环境,写操作全部是 master,读操作全部是 slave。
  2. 不想通过注解配置完成以上功能。

答:在 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 管多数据源
不要双重配置读写分离

如果 ShardingSphere 已经承担了分片 + 读写分离,就不要再让本框架的插件做一次路由,避免「双重路由」导致预期之外的库被访问(#314、#690)。

相关文档​