作者:微信小助手
发布时间:2021-12-20T15:43:59
点击“ 程序员内点事 ”关注,选择“ 设置星标 ” 坚持学习,好文每日送达! 《sharding-jdbc 分库分表的 4种分片策略》 中我们介绍了 引入任何一种技术都是存在风险的,分库分表当然也不例外,除非库、表数据量持续增加,大到一定程度,以至于现有高可用架构已无法支撑,否则不建议大家做分库分表,因为做了数据分片后,你会发现自己踏上了一段踩坑之路,而分布式主键 不同数据节点间生成全局唯一主键是个棘手的问题,一张逻辑表 尽管我们可以通过严格约束,各个分片表自增主键的 目前已经有了许多第三放解决方案可以完美解决这个问题,比如基于 而 前边介绍过在 sharding-jdbc 中要想为某个字段自动生成主键 ID,只需要在 在使用 sharding-jdbc 分布式主键时需要注意两点: 下面我们从源码上分析下 sharding-jdbc 内置主键生成方案 打开 UUID 虽然可以做到全局唯一性,但还是不推荐使用它作为主键,因为我们的实际业务中不管是 它的存储以及查询对 Java 中 Long 型的最高位是符号位,正数是0,负数是1,一般生成ID都为正数,所以默认为0 41位的时间戳可以容纳的毫秒数是 2 的 41次幂,而一年的总毫秒数为 表示一个唯一的工作进程id,默认值为 0,可通过 同一毫秒内生成不同的ID。 了解了雪花算法的主键 ID 组成后不难发现,这是一种严重依赖于服务器时间的算法,而依赖服务器时间的就会遇到一个棘手的问题: 为什么会出现时钟回拨呢? 互联网中有一种网络时间协议 sharding-jdbc
4种分片策略的使用场景,可以满足基础的分片功能开发,这篇我们来看看分库分表后,应该如何为分片表生成全局唯一的主键 ID
。ID
就是遇到的第一个坑。t_order
拆分成多个真实表 t_order_n
,然后被分散到不同分片库 db_0
、db_1
... ,各真实表的自增键由于无法互相感知从而会产生重复主键,此时数据库本身的自增主键,就无法满足分库分表对主键全局唯一的要求。 db_0--
|-- t_order_0
|-- t_order_1
|-- t_order_2
db_1--
|-- t_order_0
|-- t_order_1
|-- t_order_2初始值
和 步长
的方式来解决 ID
重复的问题,但这样会让运维成本陡增,而且可扩展性极差,一旦要扩容分片表数量,原表数据变动比较大,所以这种方式不太可取。 步长 step = 分表张数
db_0--
|-- t_order_0 ID: 0、6、12、18...
|-- t_order_1 ID: 1、7、13、19...
|-- t_order_2 ID: 2、8、14、20...
db_1--
|-- t_order_0 ID: 3、9、15、21...
|-- t_order_1 ID: 4、10、16、22...
|-- t_order_2 ID: 5、11、17、23...UUID
、SNOWFLAKE
算法 、segment
号段,使用特定算法生成不重复键,或者直接引用主键生成服务,像美团(Leaf
)和 滴滴(TinyId
)等。sharding-jdbc
内置了两种分布式主键生成方案,UUID
、SNOWFLAKE
,不仅如此它还抽离出分布式主键生成器的接口,以便于开发者实现自定义的主键生成器,后续我们会在自定义的生成器中接入 滴滴(TinyId
)的主键生成服务。application.properties
文件中做如下配置:# 主键字段
spring.shardingsphere.sharding.tables.t_order.key-generator.column=order_id
# 主键ID 生成方案
spring.shardingsphere.sharding.tables.t_order.key-generator.type=UUID
# 工作机器 id
spring.shardingsphere.sharding.tables.t_order.key-generator.props.worker.id=123key-generator.column
表示主键字段,key-generator.type
为主键 ID 生成方案(内置或自定义的),key-generator.props.worker.id
为机器ID,在主键生成方案设为 SNOWFLAKE
时机器ID 会参与位运算。
insert
插入操作的实体对象中主键字段已经赋值,那么即使配置了主键生成方案也会失效,最后SQL 执行的数据会以赋的值为准。
SNOWFLAKE
方式生成。比如:用
mybatis plus
的
@TableId
注解给字段
order_id
设置了自增主键,那么此时配置哪种方案,总是按雪花算法生成。
UUID
、SNOWFLAKE
是怎么实现的。UUID
UUID
类型的主键生成实现类 UUIDShardingKeyGenerator
的源码发现,它的生成规则只有 UUID.randomUUID()
这么一行代码,额~ 心中默默来了一句卧槽。user_id
还是 order_id
主键多为整型,而 UUID 生成的是个 32 位的字符串。MySQL
的性能消耗较大,而且 MySQL
官方也明确建议,主键要尽量越短越好,作为数据库主键 UUID 的无序性还会导致数据位置频繁变动,严重影响性能。public final class UUIDShardingKeyGenerator implements ShardingKeyGenerator {
private Properties properties = new Properties();
public UUIDShardingKeyGenerator() {
}
public String getType() {
return "UUID";
}
public synchronized Comparable<?> generateKey() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
public Properties getProperties() {
return this.properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}SNOWFLAKE
SNOWFLAKE
(雪花算法)是默认使用的主键生成方案,生成一个 64bit的长整型(Long
)数据。sharding-jdbc
中雪花算法生成的主键主要由 4部分组成,1bit
符号位、41bit
时间戳位、10bit
工作进程位以及 12bit
序列号位。
符号位(1bit位)
时间戳位(41bit)
1000L * 60 * 60 * 24 * 365
,计算使用时间大概是69年,额~,我有生之间算是够用了。Math.pow(2, 41) / (365 * 24 * 60 * 60 * 1000L) = = 69年
工作进程位(10bit)
key-generator.props.worker.id
属性设置。spring.shardingsphere.sharding.tables.t_order.key-generator.props.worker.id=0000
序列号位(12bit)
时钟回拨
时钟回拨
。ntp
全称 (