本文章介绍:使用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.torder->{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);