概述
读写分离功能作为shardingsphere的可插拔能力之一
包结构分析
api:对外暴露的接口,包括算法spi、配置的对象类
core:核心层,包含默认提供的读分配的负载均衡的算法、以及SQL改写,选取数据源的核心逻辑
distSQL:distsql解析以及管理的相关的流程
spring:spring-boot-starter以及spring-namespace相关的实现。
具体实现
在api层面,对外提供的主要是配置对象以及,读的负载均衡。
在这里,官方提供了两种方式的负载。
1、ROUND_ROBIN(默认算法)
核心实现:
private static final ConcurrentHashMap<String, AtomicInteger> COUNTS = new ConcurrentHashMap<>();
@Override
public String getDataSource(final String name, final String writeDataSourceName, final List<String> readDataSourceNames) {
AtomicInteger count = COUNTS.containsKey(name) ? COUNTS.get(name) : new AtomicInteger(0);
COUNTS.putIfAbsent(name, count);
count.compareAndSet(readDataSourceNames.size(), 0);
return readDataSourceNames.get(Math.abs(count.getAndIncrement()) % readDataSourceNames.size());
}
2、RANDOM
@Override
public String getDataSource(final String name, final String writeDataSourceName, final List<String> readDataSourceNames) {
return readDataSourceNames.get(ThreadLocalRandom.current().nextInt(readDataSourceNames.size()));
}
核心路由逻辑
通过下述逻辑获取对应的读或者写的数据源。
/**
* Route.
*
* @param sqlStatement SQL statement
* @return data source name
*/
public String route(final SQLStatement sqlStatement) {
if (isPrimaryRoute(sqlStatement)) {
String autoAwareDataSourceName = rule.getAutoAwareDataSourceName();
if (Strings.isNullOrEmpty(autoAwareDataSourceName)) {
return rule.getWriteDataSourceName();
}
Optional<DataSourceNameAware> dataSourceNameAware = DataSourceNameAwareFactory.getInstance().getDataSourceNameAware();
if (dataSourceNameAware.isPresent()) {
return dataSourceNameAware.get().getPrimaryDataSourceName(autoAwareDataSourceName);
}
}
String autoAwareDataSourceName = rule.getAutoAwareDataSourceName();
if (Strings.isNullOrEmpty(autoAwareDataSourceName)) {
return rule.getLoadBalancer().getDataSource(rule.getName(), rule.getWriteDataSourceName(), rule.getReadDataSourceNames());
}
Optional<DataSourceNameAware> dataSourceNameAware = DataSourceNameAwareFactory.getInstance().getDataSourceNameAware();
if (dataSourceNameAware.isPresent()) {
Collection<String> replicaDataSourceNames = dataSourceNameAware.get().getReplicaDataSourceNames(autoAwareDataSourceName);
return rule.getLoadBalancer().getDataSource(rule.getName(), rule.getWriteDataSourceName(), new ArrayList<>(replicaDataSourceNames));
}
return rule.getLoadBalancer().getDataSource(rule.getName(), rule.getWriteDataSourceName(), rule.getReadDataSourceNames());
}
private boolean isPrimaryRoute(final SQLStatement sqlStatement) {
return containsLockSegment(sqlStatement) || !(sqlStatement instanceof SelectStatement) || HintManager.isWriteRouteOnly() || TransactionHolder.isTransaction();
}