分片带来的主键唯一问题
当一个表的数据被切分到多个表的时候,那么单表的唯一主键就无法通过数据库来控制了,因为需要保证同一个表在多个真实表的主键id的唯一性。
注意:5.x 版本和4.x版本在配置上有略微的差别
内置解决方案
shardingjdbc内置了2种主键算法来供选择;
uuid
uuid 就是利用了jdk生成的uuid 字符串,来保证主键id 的不重复的特点
内置算法类是 UUIDKeyGenerateAlgorithm
使用方式也很简单
1 2 3 4 5 6
| keyGenerateStrategy: column: order_id keyGeneratorName: uuid keyGenerators: uuid: type: UUID
|
缺点:1.主键必须是 字符串类型;
2. 主键非连续的,对于b-tree索引插入数据时,建立索引的性能比连续数据的性能比较差;
优点: 比较简单
不是很推荐的一种方式;
雪花算法
雪花算法是Twitter 开源的一种分布式序列算法。生成的是一个64bit的整数。并且生成的是根据时间连续的;
整体结构 是 时间戳 + workid 机器id + 毫秒的序列化
具体的详细介绍这里不在赘述,请自行百度或google
使用方式:
配置主键id 列,配置类型,配置属性可以配置一个work-id 也就是中间的机器id能够有效的避免重复;
1 2 3
| keyGenerateStrategy: column: order_id keyGeneratorName: snowflak
|
在最后面定义snowflow
优点: 生成的序列 是根据时间有序的;
缺点:依赖时钟,如果时钟回拨可能会导致重复;
注意生成的id 是64位的整数,在返给前端的时候记得做一些处理方案。 比如处理json序列化将long 按照string来序列化
自定义算法
当然如果内置的2种算法无法满足需求,shardingjdbc支持spi拓展分布式序列算法;
- 首先定义一个序列算法类,这里仅仅使用AtomicLong 做一个全局自增的操作,当然实际业务中可自定扩展;
实现getType 方法,返回此算法的类型名称
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| package com.example.shardingjdbcdemo.spi;
import org.apache.shardingsphere.sharding.spi.KeyGenerateAlgorithm; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import java.util.Properties; import java.util.concurrent.atomic.AtomicLong;
@Component public final class IncrementKeyGenerate implements KeyGenerateAlgorithm {
private AtomicLong atomicLong;
@Override public Properties getProps() { return KeyGenerateAlgorithm.super.getProps(); }
@Override public void setProps(Properties props) { KeyGenerateAlgorithm.super.setProps(props); }
@Override public String getType() { return "INCREMENT"; }
@Override public Comparable<?> generateKey() { return atomicLong.getAndIncrement(); }
@Override public void init() { atomicLong = new AtomicLong(1); }
@Override public boolean isDefault() { return true; } }
|
定义spi 使自定义的类能被扫描到;
resoruce 下创建 META-INF 文件夹,下面再创建services 文件夹,严格安装java spi 标准来定义。

文件名称为来实现的接口名称 org.apache.shardingsphere.sharding.spi.KeyGenerateAlgorithm
内部写入具体实现类

- 接下来 在定义主键算法的类型的时候就可以使用自己定义的类型了;
完整配置如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| spring: application: name: shardingjdbcDemo main: allow-bean-definition-overriding: true
shardingsphere: #数据源信息 datasource: #名称为dbsource-0的数据源 dbsource-0: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://127.0.0.1:3306/db1?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: 123456 names: dbsource-0 #规则的配置 rules: sharding: tables: #order表的规则 t_order: actual-data-nodes: dbsource-0.t_order_$->{0..1} #分表策略 table-strategy: #标准策略 standard: sharding-column: user_id sharding-algorithm-name: order-alg keyGenerateStrategy: column: order_id keyGeneratorName: order-incr #user表的配置 t_user: actual-data-nodes: dbsource-0.t_user_$->{0..1} table-strategy: #标准策略 standard: sharding-column: user_id sharding-algorithm-name: user-alg keyGenerateStrategy: column: user_id keyGeneratorName: user-incr
#分片算法 sharding-algorithms: order-alg: type: INLINE props: algorithm-expression: t_order_$->{user_id % 2} user-alg: type: INLINE props: algorithm-expression: t_user_$->{user_id % 2} binding-tables: - t_order,t_user keyGenerators: user-incr: type: INCREMENT order-incr: type: INCREMENT
props: sql-show: true sql-comment-parse-enabled: true
|
简单说明:在 keyGenerators 中定义算法名称 和算法类型和一些属性信息;
然后在具体的表的 keyGenerateStrategy 中使用 keyGeneratorName 来写入定义的名称,就完成了一个主键生成算法的关联定义;
插入数据后,就会安装顺序递增的方式对每个表写入id;
注意:这里在下面定义2个算法是为了不同的表使用不同的实例类,避免不同的表使用一个自增id
注意:如果使用mybatis-plus 的时候要指定id 的type = IdType.AUTO 才能生效;
@TableId(type = IdType.AUTO)
一些其他的分布式序列方案
- redis全局自增,使用redis 的incr命令维护全局自增顺序
- mysql维护自增,使用一个单独的数据表记录每个业务表的自增位置等信息;
- leaf 算法,美团的自增算法