1. sharding sphere 4.0.0-RC1版本 按年分表(自動建表)
1.1. 概述
上篇文章留了個坑,sharding sphere本身沒有提供自動建表功能,但我想了想,我們可以繞過它本身的設定,它本身的數據分片是通過分片算法實現,如下繼承一些接口PreciseShardingAlgorithm
、RangeShardingAlgorithm
等,在范圍查詢的時候,原本我們需要從availableTargetNames
參數去判斷已存在的表,從而做到不查不存在的表,插入時也是同樣的道理
但是事實上我們可以不需要使用availableTargetNames
參數,在系統初始化的時候自行去查詢已存在的表再緩存起來,當然過程中我也踩了些坑,因為LogShardingAlgorithm
的加載過程和我讀數據庫的順序不好控制,理論上我可以隨時連接數據庫讀,但我又需要讀到spring加載的配置環境再決定連哪個數據庫,不斷的測試后還是不好安排,最后采取了如下的方式,在第一次調用分片算法的時候讀取並緩存表
這樣做后實現的效果就是在做插入數據的時候,我判斷日期為2020年的表是否在緩存中,在則說明數據庫存在該表,否則我先創建該表,再把該表加入緩存;而做范圍查詢的時候,我們容易請求參數超范圍,則從緩存中的表挑選,這些表才是存在的,比如你數據庫存在2018,2019,2020年的表,你查詢條件是2017~2021,那么也只會查18,19,20三張表
/**
* @author: laoliangliang
* @description: 日志分片
* @create: 2020/1/2 10:19
**/
@Slf4j
public class LogShardingAlgorithm implements PreciseShardingAlgorithm, RangeShardingAlgorithm<Integer> {
/**
* 緩存存在的表
*/
private List<String> tables;
private final String systemLogHead = "system_log_";
private Boolean isLoad = false;
@Override
public String doSharding(Collection availableTargetNames, PreciseShardingValue shardingValue) {
if (!isLoad) {
tables = DBUtil.getAllSystemLogTable();
isLoad = true;
}
String target = shardingValue.getValue().toString();
String year = target.substring(target.lastIndexOf("_") + 1, target.lastIndexOf("_") + 5);
if (!tables.contains(systemLogHead + year)) {
DBUtil.createLogTable(year);
tables.add(year);
}
return shardingValue.getLogicTableName() + "_" + year;
}
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Integer> shardingValue) {
if (!isLoad) {
tables = DBUtil.getAllSystemLogTable();
isLoad = true;
}
Collection<String> availables = new ArrayList<>();
Range valueRange = shardingValue.getValueRange();
for (String target : tables) {
Integer shardValue = Integer.parseInt(target.substring(target.lastIndexOf("_") + 1, target.lastIndexOf("_") + 5));
if (valueRange.hasLowerBound()) {
String lowerStr = valueRange.lowerEndpoint().toString();
Integer start = Integer.parseInt(lowerStr.substring(0, 4));
if (start - shardValue > 0) {
continue;
}
}
if (valueRange.hasUpperBound()) {
String upperStr = valueRange.upperEndpoint().toString();
Integer end = Integer.parseInt(upperStr.substring(0, 4));
if (end - shardValue < 0) {
continue;
}
}
availables.add(target);
}
return availables;
}
}
1.2. 技巧
- 這里要讀取
system_log_2019
,system_log_2020
表的表名只需查詢mysql的information_schema.TABLES
,如:
select TABLE_NAME from information_schema.`TABLES`
where TABLE_NAME like 'system_log_%'
歡迎關注公眾號,回復“教學視頻”一起學習進步