本文章介紹:使用Sharding-JDBC實現數據庫分庫分表,數據庫分片策略,實現數據庫按月分表
一、Sharding-JDBC使用
1.1.準備環境
步驟一:分庫分表sql腳本導入
創建了兩個數據庫:chongba_schedule0 和chongba_schedule1
每個數據庫中任務表和任務日志表各自兩張:taskinfo_0,taskinfo_1,taskinfo_logs_0,taskinfo_logs_1
DROP database if exists `chongba_schedule0`;
CREATE DATABASE `chongba_schedule0` DEFAULT CHARACTER SET utf8;
USE `chongba_schedule0`;
CREATE TABLE `taskinfo_0` (`task_id` bigint(20) NOT NULL comment '任務id',`execute_time` datetime(3) NOT NULL comment '執行時間',`parameters` longblob comment '參數',`priority` int(11) NOT NULL comment '優先級',`task_type` int(11) NOT NULL comment '任務類型',PRIMARY KEY (`task_id`),KEY `index_taskinfo_time` (`task_type`,`priority`,`execute_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
?
CREATE TABLE `taskinfo_1` (`task_id` bigint(20) NOT NULL comment '任務id',`execute_time` datetime(3) NOT NULL comment '執行時間',`parameters` longblob comment '參數',`priority` int(11) NOT NULL comment '優先級',`task_type` int(11) NOT NULL comment '任務類型',PRIMARY KEY (`task_id`),KEY `index_taskinfo_time` (`task_type`,`priority`,`execute_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
?
CREATE TABLE `taskinfo_logs_0` (`task_id` bigint(20) NOT NULL comment '任務id',`execute_time` datetime(3) NOT NULL comment '執行時間',`parameters` longblob comment '參數',`priority` int(11) NOT NULL comment '優先級',`task_type` int(11) NOT NULL comment '任務類型',`version` int(11) NOT NULL comment '版本號,用樂觀鎖',`status` int(11) DEFAULT '0' COMMENT '狀態 0=初始化狀態 1=EXECUTED 2=CANCELLED',PRIMARY KEY (`task_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
?
CREATE TABLE `taskinfo_logs_1` (`task_id` bigint(20) NOT NULL comment '任務id',`execute_time` datetime(3) NOT NULL comment '執行時間',`parameters` longblob comment '參數',`priority` int(11) NOT NULL comment '優先級',`task_type` int(11) NOT NULL comment '任務類型',`version` int(11) NOT NULL comment '版本號,用樂觀鎖',`status` int(11) DEFAULT '0' comment '狀態 0=初始化狀態 1=EXECUTED 2=CANCELLED',PRIMARY KEY (`task_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
?
drop database if exists `chongba_schedule1`;
CREATE DATABASE `chongba_schedule1` DEFAULT CHARACTER SET utf8;
USE `chongba_schedule1`;
CREATE TABLE `taskinfo_0` (`task_id` bigint(20) NOT NULL comment '任務id',`execute_time` datetime(3) NOT NULL comment '執行時間',`parameters` longblob comment '參數',`priority` int(11) NOT NULL comment '優先級',`task_type` int(11) NOT NULL comment '任務類型',PRIMARY KEY (`task_id`),KEY `index_taskinfo_time` (`task_type`,`priority`,`execute_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
?
CREATE TABLE `taskinfo_1` (`task_id` bigint(20) NOT NULL comment '任務id',`execute_time` datetime(3) NOT NULL comment '執行時間',`parameters` longblob comment '參數',`priority` int(11) NOT NULL comment '優先級',`task_type` int(11) NOT NULL comment '任務類型',PRIMARY KEY (`task_id`),KEY `index_taskinfo_time` (`task_type`,`priority`,`execute_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
?
CREATE TABLE `taskinfo_logs_0` (`task_id` bigint(20) NOT NULL comment '任務id',`execute_time` datetime(3) NOT NULL comment '執行時間',`parameters` longblob comment '參數',`priority` int(11) NOT NULL comment '優先級',`task_type` int(11) NOT NULL comment '任務類型',`version` int(11) NOT NULL comment '版本號,用樂觀鎖',`status` int(11) DEFAULT '0' comment '狀態 0=初始化狀態 1=EXECUTED 2=CANCELLED',PRIMARY KEY (`task_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
?
CREATE TABLE `taskinfo_logs_1` (`task_id` bigint(20) NOT NULL comment '任務id',`execute_time` datetime(3) NOT NULL comment '執行時間',`parameters` longblob comment '參數',`priority` int(11) NOT NULL comment '優先級',`task_type` int(11) NOT NULL comment '任務類型',`version` int(11) NOT NULL comment '版本號,用樂觀鎖',`status` int(11) DEFAULT '0' comment '狀態 0=初始化狀態 1=EXECUTED 2=CANCELLED',PRIMARY KEY (`task_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
步驟二:在chongba_schedule_service 模塊的pom文件中引入依賴:
<dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-jdbc-spring-boot-starter</artifactId><version>4.0.0-RC1</version>
</dependency>
<dependency><!-- druid數據源--><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.19</version>
</dependency>
1.2.基本概念
邏輯表
是對于水平拆分的數據庫(表)的同一類表的總稱。例如:訂單數據根據主鍵尾數拆分為10張表,分別是t_order_0到t_order_9,他們的邏輯表可以表示為t_order,在應用程序中操作的是邏輯表。
@TableName("taskinfo")
public class TaskInfoEntity implements Serializable {.........
}
真實表
在分片的數據庫中真實存在的物理表。即上個示例中的t_order_0到t_order_9。
數據節點
數據分片的最小單元, 由數據源名稱和數據表組成
例如:db_0.t_order_0。 表示 數據庫db_0下名稱為t_order_0的表
1.3.數據源整合
下面我們進行數據源整合:
分庫分表之后,就不用之前的數據源了,改用新的數據源,因為有多個數據庫,所以需要配置多數據源。
對于程序中的代碼我們無需修改,只需要通過配置支持多數據源即可,官方文檔中有提供好的配置參考。
為了讓配置更加的清晰和有條理,我們使用新的配置,操作步驟如下:
步驟一:在chongba_schedule_service模塊中的bootstrap.yml配置文件中修改加載的配置:schedule-sharding
spring:application: name: schedule-serviceprofiles:active: devcloud:consul:host: 127.0.0.1port: 8500discovery:serviceName: ${spring.application.name}config:enabled: trueformat: yaml prefix: configdefaultContext: schedule-sharding # 只修改此處,其他和之前一樣data-key: data
步驟二:在Consul中建立新的key/value配置,Consul中的key為:config/schedule-sharding,dev/data,注意在Consul中新建key/value時要在最外層
然后在該key下配置對應的value如下:(可以先在applicatoin-dev.yml文件中配置好再配置到Consul)
chongba: preLoad: 1 #自定義預加載時間selectMasterZookeeper: 192.168.200.129:2181
spring:redis:host: 192.168.200.129password: chongbaport: 6379sleuth:sampler:probability: 1 #這是收集比例,1表示100% ,全部收集zipkin:#base-url: http://localhost:9411sender:type: rabbitrabbitmq:host: 192.168.200.129port: 5672username: guestpassword: guest shardingsphere:datasource:ds0:driver-class-name: com.mysql.cj.jdbc.Driverpassword: 123456type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://192.168.200.129:3306/chongba_schedule0?serverTimezone=Asia/Shanghaiusername: rootds1:driver-class-name: com.mysql.cj.jdbc.Driverpassword: 123456type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://192.168.200.129:3306/chongba_schedule1?serverTimezone=Asia/Shanghaiusername: rootnames: ds0,ds1
步驟三:在chongba_schedule_service模塊中啟動:ScheduleApplication,查看控制臺輸出
3.1)項目基于SpringBoot,啟動時會自動配置,其中有一個關于數據源的自動配置類如下:
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
@Configuration
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@EnableConfigurationProperties({DataSourceProperties.class})
@Import({DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class})
public class DataSourceAutoConfiguration {......
}@ConfigurationProperties(prefix = "spring.datasource"
)
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {private Class<? extends DataSource> type;private String driverClassName;private String url;private String username;private String password;
}
默認去加載spring的數據源,而我們使用sharding-jdbc后用的不再是spring默認提供的數據源,通過配置可以發現我們使用的是:spring.shardingsphere.datasource
解決方案:
在啟動類:ScheduleApplication 上將系統默認spring的數據源自動配置類排除
//@SpringBootApplication
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@MapperScan("com.chongba.schedule.mapper")
@ComponentScan({"com.chongba.cache","com.chongba.schedule"})
@EnableScheduling
@EnableAsync
public class ScheduleApplication {..........
}
3.2)sqlSessionFactory屬于mybatis的工廠類,該工廠類沒有創建好
原因:默認情況下spring整合mybtis會自動創建sqlSessionFactory,而創建sqlSessionFactory需要用到數據源dataSource,采用的也是spring默認提供的數據源,通過我們上面的分析,此時的數據源是由shardingsphere來提供的,因此創建sqlSessionFactory不成功!
解決方案:
在chongba_schedule_service模塊中的com.chongba.schedule.conf包下創建
MybatisPlusShardingConfiguration 配置類,將shardingDataSource數據源集成到mybatis-plus中去
/***mybatis-plus 使用sharding-jdbc數據源問題*/
@Configuration
@AutoConfigureAfter(DataSource.class)
public class MybatisPlusShardingConfiguration {//獲取sharding 數據源@Autowiredprivate DataSource dataSource;@Beanpublic MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean(){MybatisSqlSessionFactoryBean mysqlplus = new MybatisSqlSessionFactoryBean();mysqlplus.setDataSource(dataSource);return mysqlplus;}
}
3.3)現在雖然數據源的問題解決了,但是數據庫表的問題還沒解決,因為報錯信息是數據庫chongba_schedule1中的taskinfo表不存在,我們現在的表是taskinfo_0,taskinfo_1,如何解決?
二、分片路由配置
2.1.taskinfo表分片路由配置
配置好了數據源,對于數據庫表并沒有做任何處理,我們要對數據庫表進行分片路由配置,當向數據庫表中寫入數據的時候通過特定的路由規則來判定數據寫入到哪個庫及哪個表中
例如對taskinfo 表根據用戶task_type字段分庫,priority字段分表,修改Consul中config/schedule-sharding,dev/data下的value如下
chongba: preLoad: 1 #自定義預加載時間selectMasterZookeeper: 192.168.200.129:2181
spring:redis:host: 192.168.200.129password: chongbaport: 6379zipkin:#base-url: http://localhost:9411sender:type: rabbitrabbitmq:host: 192.168.200.129port: 5672username: guestpassword: guest shardingsphere:datasource:ds0:driver-class-name: com.mysql.cj.jdbc.Driverpassword: 123456type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://192.168.200.129:3306/chongba_schedule0?serverTimezone=Asia/Shanghaiusername: rootds1:driver-class-name: com.mysql.cj.jdbc.Driverpassword: 123456type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://192.168.200.129:3306/chongba_schedule1?serverTimezone=Asia/Shanghaiusername: rootnames: ds0,ds1sharding:tables:taskinfo:actual-data-nodes: ds$->{0..1}.taskinfo_$->{0..1}key-generator: #主鍵生成策略column: task_idtype: SNOWFLAKEdatabase-strategy: #分庫策略inline:sharding-column: task_typealgorithm-expression: ds$->{task_type % 2}table-strategy: #分表策略inline:sharding-column: priority algorithm-expression: taskinfo_$->{priority % 2}
注意:
數據節點含義 采用 e x p r e s s i o n 或 {expression}或 expression或->{expression}
也是 s p r i n g b o o t 讀取變量的規則,為了防止沖突,建議 {}也是springboot 讀取變量的規則,為了防止沖突,建議 也是springboot讀取變量的規則,為了防止沖突,建議->
?
db0
├── taskinfo_0
└── taskinfo_1
?
db1
├── taskinfo_1
└── taskinfo_1
?
以上對于數據源和數據表的配置都是連續,下面看不連續的配置,比如要配置成:
db0
├── t_order0
└── t_order1
db1
├── t_order2
├── t_order3
└── t_order4
這種情況對于actual-data-nodes的配置:
actual-data-nodes: db0.t_order ? > 0..1 , d b 1. t o r d e r ->{0..1},db1.t_order ?>0..1,db1.to?rder->{2…4}
2.2.taskinfo表分片測試
測試:
在chongba_schedule_service工程中找到測試類:TaskInfoMapperTest,運行測試方法:test1(),
TaskInfoEntity taskInfoEntity = new TaskInfoEntity();
taskInfoEntity.setExecuteTime(new Date());
taskInfoEntity.setPriority(1);
taskInfoEntity.setTaskType(1001);
taskInfoEntity.setParameters("test".getBytes());
taskInfoMapper.insert(taskInfoEntity);
測試案例1:按照分片路由規則:task_type % 2 == 1該數據將被分配到ds1數據源上,priority % 2 ==1,該數據將被分配到taskinfo_1表中存儲。
**測試案例2:**可以更改TaskType =1000 priority =10,繼續測試
2.3.taskinfo_logs表分片路由配置
taskinfo_logs分片策略跟taskinfo表一致,所以直接修改Consul中config/schedule-sharding,dev/data下的value如下:chongba: preLoad: 1 #自定義預加載時間selectMasterZookeeper: 192.168.200.129:2181
spring:redis:host: 192.168.200.129password: chongbaport: 6379zipkin:#base-url: http://localhost:9411sender:type: rabbitrabbitmq:host: 192.168.200.129port: 5672username: guestpassword: guest shardingsphere:datasource:ds0:driver-class-name: com.mysql.cj.jdbc.Driverpassword: 123456type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://192.168.200.129:3306/chongba_schedule0?serverTimezone=Asia/Shanghaiusername: rootds1:driver-class-name: com.mysql.cj.jdbc.Driverpassword: 123456type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://192.168.200.129:3306/chongba_schedule1?serverTimezone=Asia/Shanghaiusername: rootnames: ds0,ds1sharding:tables:taskinfo:actual-data-nodes: ds$->{0..1}.taskinfo_$->{0..1}key-generator: #主鍵生成策略column: task_idtype: SNOWFLAKEdatabase-strategy: #分庫策略inline:sharding-column: task_typealgorithm-expression: ds$->{task_type % 2}table-strategy: #分表策略inline:sharding-column: priority algorithm-expression: taskinfo_$->{priority % 2}taskinfo_logs:actual-data-nodes: ds$->{0..1}.taskinfo_logs_$->{0..1}database-strategy: #分庫策略inline:sharding-column: task_typealgorithm-expression: ds$->{task_type % 2}key-generator:column: task_idtype: SNOWFLAKEtable-strategy: #分表策略inline:sharding-column: priorityalgorithm-expression: taskinfo_logs_$->{priority % 2}
2.4.taskinfo_logs表分片測試:
在chongba_schedule_service工程中,找到測試類:TaskInfoLogsMapperTest,運行測試方法:test()
測試案例1:task_type=1003,priority=3,數據會被路由到:ds1.taskinfo_logs_1中
結果:
1:數據能夠正常入庫
2:樂觀鎖出現問題
對應樂觀鎖我們希望:
但是實際情況:
樂觀鎖出現問題的原因:
1:關于樂觀鎖我們之前是在啟動類ScheduleApplication中進行了如下配置,將樂觀鎖插件放入IOC容器中然后自動的注冊到SqlSessionFactory
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){return new OptimisticLockerInterceptor();
}
但是現在我們使用的是mybatis-plus集成了sharding-jdbc數據源后的MybatisSqlSessionFactoryBean,我們的樂觀鎖插件并沒有自動的注冊進去。
在chongba_schedule_service模塊中的com.chongba.schedule.conf包下找到配置類:
MybatisPlusShardingConfiguration并做出修改
@Beanpublic MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean(){MybatisSqlSessionFactoryBean mysqlplus = new MybatisSqlSessionFactoryBean();mysqlplus.setDataSource(dataSource);mysqlplus.setPlugins(new Interceptor[]{new OptimisticLockerInterceptor()}); //注冊插件return mysqlplus;}
注意:注釋掉啟動類中關于樂觀鎖插件的配置!
最后再次進行測試!
三、Sharding-JDBC分片策略
3.1.分片策略介紹
Sharding分片策略繼承自ShardingStrategy,提供了5種分片策略:
1.StandardShardingStrategy
標準分片策略。提供對SQL語句中的=, IN和BETWEEN AND的分片操作支持。
StandardShardingStrategy只支持單分片鍵,提供PreciseShardingAlgorithm和RangeShardingAlgorithm兩個分片算法。
1:PreciseShardingAlgorithm是必選的,用于處理=和IN的分片。
2:RangeShardingAlgorithm是可選的,用于處理BETWEEN AND分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND將按照全庫路由處理。
1566457504884
2.ComplexShardingStrategy
復合分片策略。提供對SQL語句中的=, IN和BETWEEN AND的分片操作支持。
ComplexShardingStrategy支持多分片鍵,由于多分片鍵之間的關系復雜,因此Sharding-JDBC并未做過多的封裝,而是直接將分片鍵值組合以及分片操作符交于算法接口,完全由應用開發者實現,提供最大的靈活度。
3.InlineShardingStrategy
Inline表達式分片策略。使用Groovy的Inline表達式,提供對SQL語句中的=和IN的分片操作支持。
InlineShardingStrategy只支持單分片鍵,對于簡單的分片算法,可以通過簡單的配置使用,從而避免繁瑣的Java代碼開發,如: t_user_${user_id % 8} 表示t_user表按照user_id按8取模分成8個表,表名稱為t_user_0到t_user_7。
1566457463389
4.HintShardingStrategy
通過Hint而非SQL解析的方式分片的策略。
對于分片字段非SQL決定,而由其他外置條件決定的場景,可使用SQL Hint靈活的注入分片字段。例:內部系統,按照員工登錄主鍵分庫,而數據庫中并無此字段。SQL Hint支持通過Java API和SQL注釋(待實現)兩種方式使用。
5.NoneShardingStrategy
不分片的策略。
6.自定義分片策略介紹
由于分片算法和業務實現緊密相關,因此Sharding-JDBC是通過分片策略將各種場景提煉出來,提供更高層級的抽象,并提供接口讓應用開發者自行實現分片算法。
為了方便獲取定義的參數及集成,自定義的算法要實現Sharding-jdbc 提供的接口,
Sharding提供了以下4種算法接口:
1):精確分片算法–PreciseShardingAlgorithm
用于處理使用單一鍵作為分片鍵的=與IN進行分片的場景。需要配合StandardShardingStrategy使用。
2):范圍分片算法–RangeShardingAlgorithm
用于處理使用單一鍵作為分片鍵的BETWEEN AND進行分片的場景。需要配合StandardShardingStrategy使用。
3):復合分片算法–ComplexKeysShardingAlgorithm
用于處理使用多鍵作為分片鍵進行分片的場景,包含多個分片鍵的邏輯較復雜,需要應用開發者自行處理其中的復雜度。需要配合ComplexShardingStrategy使用。
注 : 我們在業務開發中,經常有根據用戶id 查詢某用戶的記錄列表,又有根據某個業務主鍵查詢該用戶的某記錄的需求,這就需要用到復合分片算法。比如,訂單表中,我們既需要查詢某個userId的某時間段內的訂單列表數據,又需要根據orderId查詢某條訂單數據。這里,orderId與userId就屬于復合分片鍵。
4):Hint分片算法–HintShardingAlgorithm
Hint分片指的是對于分片字段非SQL決定,而由其他外置條件決定的場景,可以通過使用SQL Hint靈活注入分片字段。
Hint分片策略是繞過SQL解析的,因此能夠通過實現該算法來實現Sharding-JDBC不支持的語法限制。
用于處理使用Hint行分片的場景。需要配合HintShardingStrategy使用。
四、任務日志表按月分表
需求:
對于數據庫表taskinfo,我們消費完任務或者取消完任務后數據都會從該表中去刪除,而對于taskinfo_logs日志表數據是一直累加的,對于數據量大的情況下只分兩張表仍然會存在性能瓶頸,我們現在要對taskinfo_logs根據任務的執行時間execute_time進行按月分表
實現:
步驟1:導入數據表
步驟2:在Consul中創建新的配置并使用
2.1:在chonba_sechedule_service的bootstrap.yml配置文件中修改加載的配置:schedule-sharding-month
2.2:在Consul中添加新的key:config/schedule-sharding-month,dev/data ,并配置如下值:
chongba: preLoad: 1 #自定義預加載時間selectMasterZookeeper: 192.168.200.129:2181
spring:redis:host: 192.168.200.129password: chongbaport: 6379zipkin:#base-url: http://localhost:9411sender:type: rabbitrabbitmq:host: 192.168.200.129port: 5672username: guestpassword: guest shardingsphere:datasource:ds0:driver-class-name: com.mysql.cj.jdbc.Driverpassword: 123456type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://192.168.200.129:3306/chongba_schedule0?serverTimezone=Asia/Shanghaiusername: rootds1:driver-class-name: com.mysql.cj.jdbc.Driverpassword: 123456type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://192.168.200.129:3306/chongba_schedule1?serverTimezone=Asia/Shanghaiusername: rootnames: ds0,ds1sharding:tables:taskinfo:actual-data-nodes: ds$->{0..1}.taskinfo_$->{0..1}key-generator: #主鍵生成策略column: task_idtype: SNOWFLAKEdatabase-strategy: #分庫策略inline:sharding-column: task_typealgorithm-expression: ds$->{task_type % 2}table-strategy: #分表策略inline:sharding-column: priority algorithm-expression: taskinfo_$->{priority % 2}taskinfo_logs: #邏輯表actual-data-nodes: ds$->{0..1}.taskinfo_logs_20$->{19..22}_$->{1..12}key-generator: #主鍵生成策略column: task_idtype: SNOWFLAKEdatabase-strategy: #分庫策略inline:sharding-column: task_typealgorithm-expression: ds$->{task_type % 2}table-strategy: #分表策略 按月分庫standard:precise-algorithm-class-name: com.chongba.schedule.conf.ShardingAlgorithmMonthsharding-column: execute_time
步驟3:自定義分片算法實現
在chongba_schedule_service工程中的包com.chongba.schedule.conf下創建分片算法類:
ShardingAlgorithmMonth,實現PreciseShardingAlgorithm接口,實現根據任務的執行時間進行按月分片。
@Slf4j
public class ShardingAlgorithmMonth implements PreciseShardingAlgorithm<Date> {
?/*** 執行分片策略 * @param collection 候選表集合 * @param preciseShardingValue 精確分片值:任務的執行時間* @return 數據路由到的表名稱*/@Overridepublic String doSharding(Collection<String> collection, PreciseShardingValue<Date> preciseShardingValue) {String node = null;try {DateFormat dateFormat = new SimpleDateFormat("yyyy_M");String dateStr = dateFormat.format(preciseShardingValue.getValue());for (String nodeCandidate : collection) {if(nodeCandidate.endsWith(dateStr)){node = nodeCandidate;break;}}} catch (Exception e) {log.error("sharding-sphere doSharding exception {}",e.getMessage());}return node;}
}
步驟4:找到測試類:TaskInfoLogsMapperTest,運行測試方法:test(),查看控制臺輸出
報錯信息:無法將數據路由到表
解決方案:debug運行測試方法,查看問題,發現是由于時間格式的問題,修改分片算法實現,然后再次測試!
五、分庫分表優化介紹
5.1.優化1
優化1:任務表和任務日志表我們做了分庫分表,對于有一些表,數據量很小,我們無需進行分庫分表,有哪些解決方案?
廣播表:指所有的分片數據源中都存在的表,表結構和表中的數據在每個數據庫中均完全一致。適用于數據量不大且需要與海量數據的表進行關聯查詢的場景,例如:字典表。
不指定分庫分表策略:如果某張表不需要分庫分表,那我們可以不指定分庫分表策略,讓這張表的數據直接落到指定的數據源中即可
spring:shardingsphere:datasource:df:driver-class-name: com.mysql.cj.jdbc.Driverpassword: 123456type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://192.168.200.129:3306/chongba_schedule?serverTimezone=Asia/Shanghaiusername: rootds0:driver-class-name: com.mysql.cj.jdbc.Driverpassword: 123456type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://192.168.200.129:3306/chongba_schedule0?serverTimezone=Asia/Shanghaiusername: rootnames: df,ds0sharding:tables:ok: actual-data-nodes: df.okkey-generator: #主鍵生成策略column: idtype: SNOWFLAKE
5.2.優化2
優化2:在延遲任務系統中我們有一個啟動后進行數據恢復,然后定時的預加載數據庫中的數據到緩存,我們查詢數據的時候是按照任務類型和優先級進行的分組查詢,
QueryWrapper qryWrapper = new QueryWrapper();
qryWrapper.select("task_type", "priority");
qryWrapper.groupBy("task_type","priority");
List<Map<String, Object>> result = taskMapper.selectMaps(qryWrapper);
問題:分庫分表后要根據任務類型和優先級進行分組查詢,勢必要檢索所有數據源和所有表才能得到分組的結果,我們如何處理?
業務改造:對于任務的類型和優先級可以在后臺做成可配置的,建立一張表任務配置表taskConfig,存儲所有的任務類型和優先級分組關系,添加任務的時候,類型和優先級不得隨意指定,必須是配置表中已配置好的,這樣子,在做數據恢復的時候我們就無需從所有數據源中去分組檢索,而只需要從配置表中查詢數據即可
reloadData優化:
//改造成讀取字典表
List<TaskConfig> configList= taskConfigMapper.selectAll();
?
// QueryWrapper qryWrapper = new QueryWrapper();
// qryWrapper.select("task_type", "priority");
// qryWrapper.groupBy("task_type","priority");
// List<Map<String, Object>> result = taskMapper.selectMaps(qryWrapper);
log.info("分組 {}",configList);