Sharding-jdbc中的很多地方涉及到算法,比如主從配置這塊、分庫分表這塊。本文主要從源碼角度介紹下,目前主要包含哪些算法,以及這些算法的內容。
一、讀寫分離(主從配置)
這塊的代碼主要在core模塊中的api/algorithm/masterslave下面,這塊的算法的主要目的,其實是在讀的時候,如何選擇從數據庫。按照常規的理解,可以有以下幾種(參考dubbo):
- 隨機算法
- 輪詢
- 最小活躍
- 一致性hash
目前在sharding-jdbc中,實現了前兩種,我們看下是如何實現的。
首先是一個接口,定義了獲取slave的方法,即:
public interface MasterSlaveLoadBalanceAlgorithm {
/**
* Get data source.
*
* @param name master-slave logic data source name
* @param masterDataSourceName name of master data sources
* @param slaveDataSourceNames names of slave data sources
* @return name of selected data source
*/
String getDataSource(String name, String masterDataSourceName, List<String> slaveDataSourceNames);
}
最終需要返回的是slave的名稱,后續根據這個名稱可以獲取到連接池。
下面我們看下兩種實現:
- RandomMasterSlaveLoadBalanceAlgorithm
public String getDataSource(final String name, final String masterDataSourceName, final List<String> slaveDataSourceNames) {
return slaveDataSourceNames.get(new Random().nextInt(slaveDataSourceNames.size()));
}
可以看到,隨機了一個slave集群大小的隨機數,然后從slave list中獲取對應的slave name。
- RoundRobinMasterSlaveLoadBalanceAlgorithm
private static final ConcurrentHashMap<String, AtomicInteger> COUNT_MAP = new ConcurrentHashMap<>();
@Override
public String getDataSource(final String name, final String masterDataSourceName, final List<String> slaveDataSourceNames) {
AtomicInteger count = COUNT_MAP.containsKey(name) ? COUNT_MAP.get(name) : new AtomicInteger(0);
COUNT_MAP.putIfAbsent(name, count);
count.compareAndSet(slaveDataSourceNames.size(), 0);
return slaveDataSourceNames.get(count.getAndIncrement() % slaveDataSourceNames.size());
}
這塊用了一個緩存,COUNT_MAP,緩存的內容是name-count的鍵值對,而count是一個原子類,它的值一直是0,1,...,slaves.size()-1,0,1..這樣循環,所以每個slave都會被輪詢到。
二、分庫分表
分庫分表的算法目前支持的內容包括:
- 精確分庫分表PreciseShardingValue
- 按照范圍分庫分表RangeShardingValue
- 按照列表分庫分表ListShardingValue
這塊主要是定義了一些接口,具體的實現還是要看自己來實現。我們來看下example中的一些已經實現的算法。
public final class PreciseModuloDatabaseShardingAlgorithm implements PreciseShardingAlgorithm<Integer> {
@Override
public String doSharding(final Collection<String> availableTargetNames, final PreciseShardingValue<Integer> shardingValue) {
for (String each : availableTargetNames) {
if (each.endsWith(shardingValue.getValue() % 2 + "")) {
return each;
}
}
throw new UnsupportedOperationException();
}
}
這塊就是實現了一個精准分片的算法,我們主要看下doSharding這個方法,里面包含兩個參數availableTargetNames和shardingValue。那么這兩個是什么呢?我們debug一下,跟蹤一下代碼,可以看到,shardingValue其實就是分片項,也就是比如order_id、user_id等等字段值,而availableTargetNames就是所謂的實際數據庫表節點。這邊遍歷的也是實際節點,當分片項(或分片字段)滿足一定的條件時,返回實際數據庫表節點,用於組裝sql。
總的來說,分片算法這塊其實根據自己的業務需求自己進行擴展的,總的來說還是要根據實際的機器部署情況來。另外讀寫分離這塊是否需要進行擴展,也是看后續的需要。