1、分布式ID生成方式
-
UUID
-
数据库自增ID
-
Flickr方案
-
Redis
-
雪花算法Snowflake
2、各自的优缺点
-
UUID
- 无序且字符串长,存入数据库会增大数据库压力,入数据库性能差。mysql官方推荐主键越短越好。
id是主键,然后主键是包含索引的,然后mysql的索引是通过b+树来实现的,每一次新的UUID数据的插入,为了查询的优化,都会对索引底层的b+树进行修改,因为UUID数据是无序的。
所以每一次UUID数据的插入都会对主键地的b+树进行很大的修改,这一点很不好。插入完全无序,不但会导致一些中间节点产生分裂,也会白白创造出很多不饱和的节点,这样大大降低了数据库插入的性能
- 隐私安全问题:UUID的生成方案,它有一种方案是跟机器的mac地址有关。可能会把机器的mac地址泄露出去
-
数据库自增 ID
-
系统水平扩展比较困难,比如定义好了步长和机器台数之后,如果要添加机器该怎么做? 假设现在只有一台机器发号是1,2.3.4.5(步长是1),这个时候需要扩容机器一台。可以这样做,把第二台机器的初始值设置得比第一台超过很多,貌似还好,现在想象一下如果我们线上有100台机器,这个时候要扩容该怎么做? 简直是噩梦。所以系统水平扩展方案复杂难以实现。
-
数据库压力还是很大,每次获取ID都得读写一次数据库,非常影响性能,不符合分布式ID里面的延迟低和要高QPS的规则(在高并发下,如果都去数据库里面获取id,那是非常影响性能的)
-
-
基于数据库Flickr方案
1.这个方案的思路时采用了MySQL自增长ID的机制(auto_increment+auto_increment_offset)。就是这里推荐使用MyISAM数据库引擎,而不是InnoDB,可以屏蔽掉一些数据库事务的相关影响
2.这种方式一般都会配合MySQL双机高可用方案,两个库设置不同的起始ID和相同的步长,类似Redis的那种方式,比如说库A起始ID是1,步长为2,那么生成的ID就是1,3,5…,库B起始ID是2,步长也为2,生成的ID就是2,4,6…
通过使用以下SQL获取不同的ID:
begin;
REPLACE INTO Tickets64 (stub) VALUES ('a');
SELECT LAST_INSERT_ID();
commit;
-
雪花算法Snowflake
1.Snowflake是Twitter开源的分布式唯一ID生成方案,核心思想是有64个bit位,最高位1个bit是0,41位放时间戳(到毫秒单位,最多使用69年),10位放机器标识(最多部署在1024台机器上,当然这个地方也可以分机房和机器,比如前5位代表机房标识,后5位代表机器标识),12位放序号(每毫秒,每台机器,可以顺序生成4096个ID) 我从网上找了一张图,看起来应该会更直观一点
410b853ef2fc46e5ae5a06559a4373e5.png
2.整个snowflake的核心逻辑也就是几十行,非常简单,使用起来也很方便,跟我们上面讲的那些方案比较起来算是非常简单的了,但是里面有一个很致命的问题,就是时钟回拨 时钟回拨就是多台机器进行了一个时间同步,比如机器A的时间比其他的机器都快了5s,那么它就需要进行时钟回拨来校准时间,snowflake算法中最长的那一部分就是代表的时间戳,那万一机器A回拨之后在同样的时间生成了唯一ID,那么就有可能会导致这个ID重复
要解决这个问题的话,首先得记录下上一次生成ID的时间,然后针对时钟回拨的时间长短分几种不同的情况:
-
如果时间回拨的时间比较短,比如说小于300ms的话,这个时间业务等得起,那么可以让获取唯一ID的线程稍微等一下,等到时间比上一次生成ID的时间大的话,此时再去获取ID就不会重复了
-
如果这个时间适中,比如说在300ms~1min之内,那么可以让获取唯一ID的线程不要访问这台机器了,去访问其他的机器来获取(毕竟不可能单机部署吧,这样太容易单点故障了)
-
如果这个时间太长了,比如说大于1min了,那么这个时候可以让这个机器先暂时下线(从服务注册中心里面剔除),让其他的线程也都先别去请求这台机器了,等过一段时间时钟回拨的影响消除了,再让服务注册中心能够发现这台机器,重新提供生成唯一ID的服务
如果时间回拨的时间很短(一般场景下都是很短的,几百毫秒之内)且实在不想等待的话,还有一个办法就是在最近几秒内每一毫秒生成的最大ID都记录下来,当发生时钟回拨时,直接在当前毫秒内生成的最大ID基础上再进行递增,也能保证ID唯一
-
-
Redis
- Redis本身有自增的方法incrby,可以指定要增加的步长,同时天然支持高并发。在使用的时候,我们可以进行Redis的集群部署,有多少台机器就可以指定多少的步长。比如3台机器组成的集群,步长就是3,第一台机器生成的主键ID就是1,4,7…,第二台机器生成的主键ID就是2,5,8…,第三台机器生成的主键ID就是3,6,9…,以此类推 这个方案看起来很美好,但是实际使用的时候也存在一些问题,比如说在机器伸缩的时候,这个步长需要动态进行调整,需要额外的开发量,同时Redis集群进行数据同步的时候,也可能存在一些极端的场景导致主键ID重复,比如leader生成了主键ID但是还未同步至slave就发生了宕机,此时slave就会产生重复的主键ID
网友评论