业务系统对ID的要求有:

  • 全局唯一;
  • 趋势递增:Mysql InnoDB采用的是聚合索引,多数RDBMS使用B-tree的数据结构来存储索引数据,在主键的选择上尽量使用有序的主键保证写入性能;
  • 单调递增:保证下一个ID一定大于上一个ID;
  • 信息安全:ID无规则;

1.数据库自增字段

最常见的方式,利用数据库,全库唯一。

优点:

  • 简单,代码方便,性能可接受;
  • 数字ID天然排序,对分页或者需要排序的结果很有帮助。

缺点:

  • 不同数据库语法和实现不同,数据库迁移的时候或多数据库支持的时候需要单独处理。
  • 单个数据库读写分离或者一主多从的情况下,只有一个煮开可以生成。单点故障风险。
  • 性能达不到要求的情况下,比较难扩展。
  • 分库分表有点麻烦。

PS:

数据库自增Id需要锁表。

优化:

  • 针对主库单点,如果有多个Master库,则每个Master库的起始数字不同,步长一样。可以降低ID生成数据库操作的负载。水平扩展比较麻烦,如果有100台机器怎么办。

2. UUID(Universally Unique Identifier)

常见的方式。

优点:

  • 简单。代码方便。
  • 生成ID性能非常好,基本不会有性能问题。
  • 全球唯一。数据迁移,系统数据合并等情况下,从容应对。

缺点:

  • 没有排序,无法保证递增;
  • 往往是字符串存储,查询效率低。
  • 存储空间比较大,海量数据库的话,要考虑存储量的问题。
  • 传输数据量大;
  • 不可读。
  • 信息不安全:基于MAC地址生成UUID的算法可能会造成MAC地址泄露,这个漏洞曾被用于寻找梅丽莎病毒的制作者位置。

3.snowflake算法

twitter生成全局ID的算法策略。

第1位留空,41位表示时间,10位表示工作机器ID,12位的序列号。通过部署多个ID生成器,为各个业务系统生成全局唯一的Long型ID。

优点:

  • 易操作,有序。

缺点:

  • 需要独立部署ID生成器。增加维护成本。
  • 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。

4.Redis 生成Id

主要依赖Redis是单线程的。

优点:

  • 不依赖数据库,灵活方便,且性能好。
  • 数字ID天然排序。

缺点:

  • 编码和配置的工作量比较大。

6.MongoDB的ObjectId

和snowflake的算法类似。

时间戳+机器ID+进程ID+序列号-》ObjectId对象

优点:

  • 本地生成,有序,成本低;

缺点:

  • 使用机器ID和进程Id,64位Long无法存储,只能生成特殊的ObjectId对象。

7.Segment方案

使用proxy server批量获取,每次获取一个segment(step决定大小)号段的值。区分业务bizType。

优点:

  • 方便的线性扩展,性能OK;
  • ID是递增的8byte的64位数字。满足数据库存储的主键要求。
  • 容灾性高:服务内部有号段缓存,即使DB宕机,短时间依然可以对外正常提供服务;
  • 可以自定义maxId大小,方便从原有ID上迁移;

缺点:

  • ID不够随机;
  • TP 999 数据波动量大,当号段使用完之后还会hang在更新数据库的IO上。
  • DB宕机会导致整个系统不可用。

优化:

当号段即将用完的时候,启动线程去更新下一个号段。

解决时钟回拨问题,可以直接不提供服务直接返回ErrorCode。等时钟追上即可。或者报警。

参考文章:

https://maimai.cn/article/detail?fid=203755745&from=single_feed

https://yuerblog.cc/2017/11/21/golang-id-alloc/

https://tech.meituan.com/MT_Leaf.html