开始
从零开始利用spring-data-jpa与sharding-jdbc进行动态月表,直接上手。
需求说明
数据量按照分片键(入库时间)进入对应的月表,查询时根据分片键的值查询指定表;但是每次查询都必须带上分片键,这就不是很友好,所以另外后面也有说明在没有指定分片键时如何查询最近的两个月。
前期准备
建表语句
1 | 复制代码-- 逻辑表,每个月表都根据逻辑表生成 |
实体类
1 | 复制代码@Entity |
repo
1 | 复制代码import java.util.Date; |
Maven依赖
经过测试,支持springboot 2.0.X+与1.5.X+。
1 | 复制代码 <dependency> |
分片算法实现
由于选择的分片策略是StandardShardingStrategy
(在后面的配置文件中会配置),所以需要试下下面两个分片算法:
- 精确分片算法
1 | 复制代码import java.util.Collection; |
- 范围分片算法
1 | 复制代码public class TimeRangeShardingAlgorithm implements RangeShardingAlgorithm<Date> { |
application.yml配置
1 | 复制代码spring: |
测试
1 | 复制代码 |
启动后就会看到日志如下:
数据库:
- 表:
- 数据
后记
虽然这样实现了基于时间的动态划分月表查询与插入,但在实际使用中却还有着许多小问题,比如:save方法在指定了主键的情况下依然会进行INSERT
而不是UPDATE
、查询时必须带上分片键、还需要手动创建后续的月表。
针对这三个问题,需要做进一步的优化。
问题产生的原因
- 为什么save方法在指定了主键的情况下依然会进行
INSERT
而不是UPDATE
JPA的SAVE在指定的主键不为空时会先去表里查询该主键是否存在,但是这样查询的条件是只有主键而没有分片键的,Sharding-JDBC的策略是在没有指定分片键时会去查询所有的分片表。
但是这里就是有一个误区,Sharding-JDBC主动查询所有的分片表指的是固定分片的情况。比如这里有另外一张表,根据ID奇偶分片,分出来有两张表。那么所有的数据都会在者两张表中,我们在配置的时候也是直接配置者两张表。
对于我们现在的需求来说就不适用,因为我们的分表规则是根据时间来的,每年每月都有一张新表,所以对于没有指定分片键值得查询,Sharding-JDBC默认值查询了逻辑表。此时返回空,JPA就会认为该主键没有数据,所以对应的SQL是INSERT
而不是UPDATE
。
- 为什么查询时必须带上分片键
理由和上述是一样的,Sharding-JDBC在没有指定分片键时值查询了逻辑表。
- 还需要手动创建后续的月表
首先,每个月都需要创建对应的月表这个是肯定的,当然也可以直接一次性县创建几年的表,但我感觉没意义,这种重复的事情应该让程序来做,定时创建月表。
解决方案
针对问题1与问题2,我直接重写Sharding-JDBC的路由规则,可以完美解决。
- 重写路由规则
需要修改类io.shardingsphere.core.routing.type.standard.StandardRoutingEngine
的routeTables
方法,并且声明了一个静态变量记录需要分表的逻辑表,具体代码如下:
1 | 复制代码// 时间格式化 |
1 | 复制代码 private Collection<DataNode> routeTables(final TableRule tableRule, final String routedDataSource, |
针对问题3,利用程序定时建表,我这里没有选择通用的建表语句:
1 | 复制代码-- ****** 日期,在程序里动态替换 |
主要原因有以下两点
- 在一般的项目里的表字段一般都不会这么少,建表语句会很长
- 而且后期的维护也不好,对于表任何改动都需要在程序里也需要维护
我选择了根据模板来创建表,SQL如下:
1 | 复制代码-- ****** 日期,在程序里动态替换 |
这样的好处就是建表语句相对精简、不需要关心表结构了,一切从模板新建月表。但是这也引出了一个新的问题,Sharding-JDBC不支持这样的语法。所以又需要修改源代码重写一下拦截规则。具体就是类io.shardingsphere.core.parsing.parser.sql.ddl.create.table.AbstractCreateTableParser
的parse
方法:
1 | 复制代码 public final DDLStatement parse() { |
总结
到此一个完整的动态划分月表就已经完成了,整体来说还比较简单,真正有一点难度的是在于遇到问题时对于源码的分析,能够合理的根据自身的业务需求去实现自己的分表逻辑。
本文转载自: 掘金