分布式ID

参考

美团技术团队博客参考

分布式ID

在互联网的业务系统中,涉及到各种各样的ID,如在支付系统中就会有支付ID、退款ID等。那一般生成ID都有哪些解决方案呢?特别是在复杂的分布式系统业务场景中,我们应该采用哪种适合自己的解决方案是十分重要的。下面我们一一来列举一下,不一定全部适合,这些解决方案仅供你参考,或许对你有用。

一个ID一般来说有下面几种要素:

  • 唯一性:确保生成的ID是全网唯一的。

  • 有序递增性:确保生成的ID是对于某个用户或者业务是按一定的数字有序递增的。

  • 高可用性:确保任何时候都能正确的生成ID。

  • 带时间:ID里面包含时间,一眼扫过去就知道哪天的交易。 系统时间毫秒数

UUID

优点:

  • 简单易用

  • 生成取决于网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素。完全随机,不会重复

缺点

  • 可读性差

  • 不是递增

使用单表数据库主键

优点:

  • 非常简单,利用现有数据库系统的功能实现,成本小,有DBA专业维护。

  • ID号单调自增,可以实现一些对ID有特殊要求的业务。

缺点

  • 强依赖DB,当DB异常时整个系统不可用,属于致命问题。配置主从复制可以尽可能的增加可用性,但是数据一致性在特殊情况下难以保证。主从切换时的不一致可能会导致重复发号。

  • ID发号性能瓶颈限制在单台MySQL的读写性能。

Twitter的snowflake算法

41位的时间序列,精确到毫秒,可以使用69年 10位的机器标识,最多支持部署1024个节点 12位的序列号,支持每个节点每毫秒产生4096个ID序号,最高位是符号位始终为0。 这种方案性能好,在单机上是递增的,但是由于涉及到分布式环境,每台机器上的时钟不可能完全同步,也许有时候也会出现不是全局递增的情况。

而且这个项目在2010就停止维护了,但这个设计思路还是应用于其他各个ID生成器及变种。

优点:

  • 毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。

  • 不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。

  • 可以根据自身业务特性分配bit位,非常灵活。

缺点:

  • 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。

美团的leaf算法

leaf算法链接

Leaf-Segment

第一种Leaf-segment方案,在使用数据库的方案上,做了如下改变: - 原方案每次获取ID都得读写一次数据库,造成数据库压力大。改为利用proxy server批量获取,每次获取一个segment(step决定大小)号段的值。用完之后再去数据库获取新的号段,可以大大的减轻数据库的压力。 - 各个业务不同的发号需求用biz_tag字段来区分,每个biz-tag的ID获取相互隔离,互不影响。如果以后有性能需求需要对数据库扩容,不需要上述描述的复杂的扩容操作,只需要对biz_tag分库分表就行。

数据库表设计如下:

+-------------+--------------+------+-----+-------------------+-----------------------------+
| Field       | Type         | Null | Key | Default           | Extra                       |
+-------------+--------------+------+-----+-------------------+-----------------------------+
| biz_tag     | varchar(128) | NO   | PRI |                   |                             |
| max_id      | bigint(20)   | NO   |     | 1                 |                             |
| step        | int(11)      | NO   |     | NULL              |                             |
| desc        | varchar(256) | YES  |     | NULL              |                             |
| update_time | timestamp    | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-------------+--------------+------+-----+-------------------+-----------------------------+

优点:

  • Leaf服务可以很方便的线性扩展,性能完全能够支撑大多数业务场景。

  • ID号码是趋势递增的8byte的64位数字,满足上述数据库存储的主键要求。

  • 容灾性高:Leaf服务内部有号段缓存,即使DB宕机,短时间内Leaf仍能正常对外提供服务。

  • 可以自定义max_id的大小,非常方便业务从原有的ID方式上迁移过来。

缺点:

  • ID号码不够随机,能够泄露发号数量的信息,不太安全。

  • TP999数据波动大,当号段使用完之后还是会hang在更新数据库的I/O上,tg999数据会出现偶尔的尖刺。

  • DB宕机会造成整个系统不可用。

双buffer优化

  • 采用双buffer的方式,Leaf服务内部有两个号段缓存区segment。当前号段已下发10%时,如果下一个号段未更新,则另启一个更新线程去更新下一个号段。当前号段全部下发完后,如果下个号段准备好了则切换到下个号段为当前segment接着下发,循环往复。

  • 每个biz-tag都有消费速度监控,通常推荐segment长度设置为服务高峰期发号QPS的600倍(10分钟),这样即使DB宕机,Leaf仍能持续发号10-20分钟不受影响。

  • 每次请求来临时都会判断下个号段的状态,从而更新此号段,所以偶尔的网络抖动不会影响下个号段的更新。

Leaf-snowflake

最后更新于

这有帮助吗?