分布式ID生成策略

前言

主键生成策略可以讲是一个系统最基本的问题,目前生成策略有多种,下面讲下我遇到的几种。

1.数据库自增

比如MySQL的auto_increment或者Oracle的Sequence

优点:代码简单,无需额外的实现

缺点:

  1. 如果遇见多个系统需要合并或者数据迁移会很难受
  2. 如果以后有分表分库的打算,后面很麻烦

扩展:多说一句,也有一些根据NoSQL数据库,比如Redis、MongoDB去做的,可以了解,我个人不建议

2.UUID

最近接触的项目就是这个策略,真的不推荐用这个= =

优点:本地生成,代码简单方便,性能方面也稳,而且不同的系统之间也基本不会重复

缺点: UUID一般字符串存储,数据存储占用空间大,查询效率低

3.snowflake算法

Twitter开源的分布式ID生成算法,结构如下:

分布式ID生成策略

第一位为未使用,接下来的41位为毫秒级时间(41位的长度可以使用69年),然后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点) ,最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)

一共加起来刚好64位,为一个Long型。(转换成字符串后长度最多19)

对代码有兴趣的可以参看:https://github.com/twitter/snowflake

优点:

  1. 整体上按时间趋势递增,后续插入性能较好
  2. 整个分布式系统内不会产生ID碰撞(由数据中心标识ID、机器标识ID作区分)
  3. 本地生成,且不依赖数据库(或第三方组件),没有网络消耗,所以效率高(每秒能够产生26万ID左右)

缺点:

  1. 分布式集群部署需要指定数据中心标识ID、机器标识ID
  2. 因为snowflake算法是强依赖机器的系统时间的,会有时钟回拨问题,时钟回拨时很可能会引起ID重复、ID乱序、服务会处于不可用状态等问题。

优化方案:

  1. 将ID生成交给少量服务器,并关闭时钟同步
  2. 如果回拨时间较短,在耗时要求内,比如5ms,那么等待回拨时长后再进行生成。
  3. 如果回拨时间很长,那么无法等待,可以匀出少量位(1~2位)作为回拨位,一旦时钟回拨,将回拨位加1,可得到不一样的ID,2位回拨位允许标记3次时钟回拨,基本够使用。如果超出了,可以再选择抛出异常。

4.snowflake扩展

4.1百度UidGenerator

UidGenerator是百度开源的分布式ID生成器,是基于snowflake算法的实现。也是最近我给接触的项目接入的方案。

优点:

  1. 借助数据库表自增主键解决数据中心标识ID和机器标识ID需要配置的问题,每次重启使用自增主键作为workid
  2. 通过AtomicLong的incrementAndGet()方法获取下一次的时间,脱离了对服务器时间的依赖,也就不会有时钟回拨的问题了

缺点:启动需要依赖数据库表自增生成一个workid,且workid暂时是无法复用的

具体使用方式可以参考官网说明:https://github.com/baidu/uid-generator/blob/master/README.zh_cn.md

4.2美团Leaf

Leaf 是美团开源的分布式ID生成器,有Snowflake和Segment两种模式,能保证全局唯一性、趋势递增、单调递增、信息安全,里面也提到了几种分布式方案的对比,但也需要依赖关系数据库或者Zookeeper中间件。Snowflake模式好像没有很好的解决时钟回拨问题,但是workid是复用的,有兴趣的小伙伴可以参考一下官网说明:https://github.com/Meituan-Dianping/Leaf/blob/master/README_CN.md