在分布式环境中,MySQL的主键有几种方法,包括自增主键,UUID,雪花算法。以下分贝讲述一下,每种方法的的优缺点。
一、 在MySQL的分布式环境中,为什么不推荐使用自增主键?
该问题的前提是在分布式环境下,在单库的情况下,使用自增主键是没有任何问题的,只需要
在创建表的使用,给主键字段使用AUTO INCREMENT,就可以实现自增。
但是在分布式环境下,就不适用了。当使用数据库自身带的auto_increment,就会有一些问题。
当在分布式环境下,我们有A库、B库、C库,三个数据库。
当我们使用自增主键时,就会有几种情况:
(1)按照数据的分布范围来进行区分
A库:主键范围1-300,B库:主键范围300-600,C库600-900
这种情况下,因为数据库的主键通过自增来处理的,当A库的1-300没有存满时,那B库和C库时不会存储任何数据的,当A库存满后,B库才开始存入数据, C库仍然是放空状态。也就表示,在=某一段时间内,我们的数据库压力依然是集中在某一个库上面的,并没有做到负载均衡的效果。显然,这是非常不合理的。
同时,在我们插入数据的时候,需要实时的将使用过的ID,同步到其他数据库,如A库使用了ID1,则需要同步到B库和C库。这个过程是非常损耗性能的,因为在同步的过程中,需要进行加锁来处理的。
(2)当A、B、C三个库都维护一份自己的自增序列
这种情况下,存储数据时,是没有问题的。如:A、B、C库同时存储主键1、2、3。但是当进行跨库查询的时候,就会出现数据冲突的问题,每一个库都存在一个ID为1的记录。
所以,在分布式环境下,是不推荐自增ID主键的。
二、UUID可以用来做主键吗?会存在什么问题?
首先,UUID的全局唯一性是做的最好的,但是在MySQL中做主键,会有一些问题。
当UUID作为主键是,会生成一个以UUID构成的索引树。索引树就是表示这是排好序的数据结构,虽然UUID是无序的,但是当它构成B+树后,在树的内部,他就是有序的。
在B+树中,是以页为存储单位的,每个页默认大小为16kb,每个页存储一条数据。当UUID是中间值时,会导致这个B+树重构。
但是在mysql8.0中,有一个bin_to_uuid()方法,会保证uuid的生成是有序的,则不会产生上述问题。
三、雪花算法可以做主键吗?原理?优缺点?如何解决?
雪花算法是可以做主键的,它有几个特征的:
- 全局唯一
- 趋势递增
- 信息安全
- 但是不保证单调递增
雪花算法结构:
- 雪花算法生成的id.本质上是一个二进制64位的长整形,一共是有四个模块。
- 第一个模块是高位,为0 ,表示正数
- 第二个模块是41位时间戳,单位是毫秒,可以代表69年的时间。但这个时间戳是按1970年开始计算,也就是1970+69=2039年就结束了。这时需要减去当前的系统的上线时间的时间戳。
- 第三个模块是10位的机器码,可以部署1024个服务。
- 第四个模块就是表示可以取4096个序列号。
因为雪花算法强依赖时间戳的,所以会存在时间回拨的问题。
时间回拨问题:linux时间和真实时间是不匹配的,可能会存在几秒的时间差异。会出现取号重复。
解决方案:
1、直接抛出异常:取号时,比对一下当前时间小于上一个取号的时间,那就表示出现了时间回拨的问题
2、延迟等待:当出现时间回拨问题,可以等待一段时间,再重新取号。当再次出现时间回拨问题,再抛出异常。
3、改造雪花算法的构造:拆分第三个模块,10位机器码拆分为3位时间序列+7位机器码。7位机器码表示可以取号128个服务,3位时钟序列可以取8个号,唯一的影响就是在分布式中部署的服务从1024个服务变为128个。
当出现时间回拨问题时,就可以递增时钟序列,在同一毫秒,同一个机器上一共可以递增8次,也就是可以解决8次时间回拨问题。