雪花算法

  • Twitter 公布的分布式主键生成算法,保证不同表的主键的不重复性 & 相同表的主键的有序性
  • 核心思想:
    • 长度共 64bit(一个 long 型)。
    • 1bit 符号位,由于 long 基本类型在 Java 中是带符号的,最高位是符号位,正数是 0,负数是 1,所以 id 一般是正数,最高位是 0。
    • 41bit 时间截(毫秒级),存储的是时间截的差值(当前时间截 - 开始时间截),结果约等于 69.73 年。
    • 10bit 作为机器的 ID(5 个 bit 是数据中心,5 个 bit 的机器 ID,可以部署在 1024 个节点)。
    • 12bit 作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID)。
  • 优点:整体上按照时间自增排序,并且整个分布式系统内不会产生 ID 碰撞,并且效率较高
  • 缺点:Snowflake 集群环境下需要保证时钟同步,对运维能力有一定要求;一旦时钟错乱,又刚好是高并发时,会导致大量异常序号。

其他开源分布式 ID

  1. 百度开源的 UidGenerator(仅支持单机部署)使用 Snowflake 算法,单机 QPS 可达 600 万。项目说明: https://github.com/baidu/uid-generator/blob/master/README.zh_cn.md
  2. 美团 Leaf(分布式 ID 生成系统),QPS 近 5 万。项目地址: https://tech.meituan.com/2017/04/21/mt-leaf.html
  3. 微信序列号生成器,文档地址: https://www.infoq.cn/article/wechat-serial-number-generator-architecture
    • 递增但不连续的数字序列解决方案。
    • 设计目标 QPS1000 万以上。
    • 通过在递增过程中使用“步长”将每秒磁盘写入由 1000 万级降至 1 万。
    • 设计原理相对于 Snowflake 更通俗易懂。
    • 可以使用 hash 的负载均衡策略组建集群。
    • 缺点:需要自己实现集群中机器增减后更新负载均衡策略的逻辑。

如果我们发现系统时钟不准,就可以让发号器暂时拒绝发号,直到时钟准确为止。我们的程序本身就是运行在系统中的,如何来判断系统中的时间是否准确呢?
作者回复: 可以暂时记录上次发好的时间,然后和这次的时间比较

想了解部署一套 snowflake,性能怎么样?还有一个问题是,发号器虽然可以保证递增发号,但写入数据库时(假设有两个事务要写同一个表),那对于底层 B+树也不一定顺序写入,无法利用磁盘顺序写的性能优化吧?
作者回复: 性能在单实例单核可以达到 2 亿万次每秒发号器一般是改的 redis

snowflake 方案中现在一般公司都有容器虚拟化,所以每个实例都有自己的实例 ID,以此作为唯一 ID 即可,另外保险起见在服务启动的时候可以向其他启动的服务发送 check 请求,确保 ID 全局唯一,这样可以不引入 zk,让系统更简单些~
作者回复: 容器 ID 太长了吧,比较占发号器的位数

1.数据库自增的全局唯一键。可以在设计出按一定步进生成 id。比如分库为 3 台,每台的主键 id 初始值分别为 0、1、2 自增步进为 3。这样也可以唯一。不过数据库作为整个系统的吊车尾。还是别拿它搞事了。 2.如果业务没有 id 带有实时字段的要求,那么可以用预生成备用的方式。客户端服务每次按一定步进来拉取 id 集合,并缓存到客户端本地内存。如此也能有效率的提升。(哪怕有实时业务段,也可以将非业务的其他部分生成好,到客户端用时再拼接)

参考链接