原文:http://ericliang.info/what-kind-of-id-generator-we-need-in-business-systems/
ID 生成器在微博我们一直叫发号器,微博就是用这样的号来存储,而我微博里讨论的时候也都是以发号器为标签。它的主要目的确如平常大家理解的“为一个分布式系统的数据object产生一个唯一的标识”,但其实在一个真实的系统里可能也可以承担更多的作用。概括起来主要有以下几点:
1. 唯一性
2. 时间相关
3. 粗略有序
4. 可反解
5. 可制造
下面我会分别讲每个作用后面的考虑和权衡,也会对比介绍一下业界已知的几种 ID 设计。
1. 要唯一性,是否需要全局唯一?
说起全局唯一,通常大家都会在想到发号器服务,分布式的通常需要更大空间,中心式的则需要在一个合适的地方在会聚。这就可能涉及到锁,而锁意味着成本和性能的下降。所以当前的系统是否需要全局的唯一性,就是一个需要考虑的问题。
比如在通讯系统里,聊天消息可能就未必需要全局,因为一条消息只是某一个人发出,系统只要保证一个人维度的唯一性即可。本质上而言,这里利用了用户 ID 的唯一性,因为唯一性是可以依赖的,通常我们设计系统也都是基于类似的性质,比如后面降到的使用时间唯一性的方式。
2. 用时间来做什么?千万年太久,只争朝夕?
前面说到唯一性可以依赖,我们需要选择的是依赖什么。通常的做法可以选择数据库自增,这在很多数据库里都是可以满足ACID 的操作。但是用数据库有个缺点,就是数据库有性能问题,在多机房情况下也很难处理。当然,你可以通过调整自增的步长来设计,但对于一个发号器而言,操作和维护都略重了。
而时间是天然唯一的,因此也是很多设计的选择。但对于一个8Byte的 ID 而言,时间并没有那么多。你如果精确到秒级别,三十年都要使用30bit,到毫秒级则要再增加10bit,你也只剩下20bit 可以做其他事情了。之所以在8Byte 上捣鼓,因为8Byte 是一个Long,不管在处理器和编译器还是语言层面,都是可以更好地被处理。
然而三十年够么?对于一个人来说,可能不够,但对一个系统而言,可能足够。我们经常开玩笑,互联网里能活三十年的系统有多少呢?三十年过去,你的系统可能都被重写 N 遍了。这样的信心同样来自于摩尔定律,三十年后,计算性能早就提高了上千倍,到时候更多Byte 都不会是问题了。
3. 粗略有多粗略,秒还是毫秒?
每秒一个或者每毫秒一个ID明显是不够的,刚才说到还有20bit 可以做其他事情,就包括一个SequenceID。如果要达到精确的有序,就要对 Sequence 进行并发控制,性能上肯定会打折。所以经常会有的一个选择就是,在这个秒的级别上不再保证顺序,而整个 ID 则只保证时间上的有序。后一秒的 ID肯定比前一秒的大,但同一秒内可能后取的ID比前面的号小。这在使用时非常关键,你要理解,系统也要接受才可以。
那时间用秒还是毫秒呢?其实不用毫秒的时候就可以把空出来的10bit 送给 Sequence,但整个ID 的精度就下降了。峰值速度是更现实的考虑。Sequence 的空间决定了峰值的速度,而峰值也就意味着持续的时间不会太久。这方面,每秒100万比每毫秒1000限制更小。
4. 可反解,解开的是什么?
一个 ID 生成之后,就会伴随着信息终身,排错分析的时候,我们需要查验。这时候一个可反解的 ID 可以帮上很多忙,从哪里来的,什么时候出生的。 跟身份证倒有点儿相通了,其实身份证就是一个典型的分布式 ID 生成器。
如果ID 里已经有了时间而且能解开,在存储层面可能不再需要timestamp 一类的字段了。微博的 ID 还有很多业务信息,这个后面会细讲。
5. 可制造,为什么不用UUID?
互联网系统上可用性永远是优先指标。但由于分布式系统的脆弱,网络不稳定或者底层存储系统的不可用,业务系统随时面临着失败。为了给前端更友好的响应,我们需要能尽量容忍失败。比如在存储失败时,可能需要临时导出请求供后续处理,而后续处理时已经离开了当时的时间点,顺序跟其他系统错开了。我们需要制造出这样的ID 以便系统好像一直正常运行一样,可制造的 ID 让你可以控制生产日期(汗,有点儿假冒伪劣的意思了),然后继续下面的处理。
另一个重要场景就是数据清洗。这个属于较少遇到,但并不罕见的情况,可能是原来 ID 设计的不合理,也可能由于底层存储的改变,都可能出现。这样一个可制造的 ID 就会带来很多操作层面的便利。
这也是我们不用 UUID 的一个原因。UUID 标准可以保证在某时某地生成,但如果要控制生成一个特定时间的 UUID,可能需要底层库的改动。经验告诉我们,能在上层解决的问题不要透到下层,这种库的维护成本是非常高的。
#设计细节
UUID 就不说了, 其他公开出来的这里说下SnowFlake、Weibo以及 Ticktick 的设计。
1. SnowFlake
41bit留给毫秒时间,10bit给MachineID,也就是机器要预先配置,剩下12位留给Sequence。代码虽然露出来了,但其实已经不可用了,据说是内部改造中。
2. Weibo
微博使用了秒级的时间,用了30bit,Sequence 用了15位,理论上可以搞定3.2w/s的速度。用4bit来区分IDC,也就是可以支持16个 IDC,对于核心机房来说够了。剩下的有2bit 用来区分业务,由于当前发号服务是机房中心式的,1bit 来区分热备。是的,也没有用满64bit。
3. Ticktick
也就是当前在环信系统里要用到的。使用了30bit 的秒级时间,20bit 给Sequence。这里是有个考虑,第一版实现还是希望到毫秒级,所以20bit 的前10bit给了毫秒来用,剩下10bit给 Sequence。等到峰值提高的时候可以暂时回到秒级。
前面说到的三十年问题,因此我在高位留了2bit 做 Version,或者到时候改造使用更长字节数,用第一位来标识不同 ID,或者可以把这2bit 挪给时间用,可以给系统改造留出一定的时间。
剩下的10bit 留给 MachineID,也就是说当前 ID 生成可以直接内嵌在业务服务中,最多支持千级别的服务器数量。最后有2bit 做Tag 用,可能区分群消息和单聊消息。同时你也看出,这个 ID 最多支持一天10亿消息,也是怕系统增速太快,这2bit 可以挪给 Sequence,可以支持40亿级别消息量,或者结合前面的版本支持到百亿级别。
#后记
自己实现一个发号器非常简单,所以Ticktick 怎么实现并不重要。不过呐,我还是有 demo 源码的,见 https://github.com/ericliang/ticktick
分享到:
相关推荐
既然要sharding,那么不可避免的要讨论到sharding key问题,在有些业务系统中,必须保证sharding key全局唯一,比如存放商品的数据库等,那么如何生成全局唯一的ID呢,下文将从DBA的角度介绍几种常见的方案。...
d、分布式全局ID生成器,ID生成非绝对递增有序,是趋向有序,这一点如果能接受,可以直接copy使用 2、事务回滚机制说明 a、每个消费端的事务处理都由本地事务负责 b、基于下单队列消费端临时表,查询红包、...
此时一个能够生成全局唯一ID的系统是非常必要的。概括下来,那业务系统对ID号的要求有哪些呢?全局唯一性:不能出现重复的ID号,既然是唯一标识,这是最基本的要求。 趋势递增:在MySQLInnoDB引
全局唯一ID作为一种唯一标识来区分数据,可用作订单号、用户ID等。ID生成器是生成全局唯一ID的工具,可封装为一种基础服务为其他业务提供服务。因此此项目就是用springboot封装ID生成器,让各种业务系统调用
1.使业务字段作为主键,如说对于表来说,可以使机号,email或者份证号作为主键 2.使成的唯ID作为主键 1.时间戳不记录毫秒是记录秒,这样在个时间区间可以多
每次请求生成一个全局唯一的ID,通过这个ID将在分布在不同系统孤立的埋点数据串联起来,组合成调用链。cicada的埋点坐标,为App、Service、Method。App代表当前埋点所在的业务系统名称,通常代表一个RPC服务;...
用到全局唯一ID的场景过多,这里引用美团Leaf的场景介绍: 在复杂分布式系统中,经常需要对大量的数据和消息进行唯一标识。如在美团评论的金融,支付,餐饮,酒店,猫眼电影等产品的系统中,数据日渐增,对数据分库...
Vesta是一款通用的ID产生器,互联网俗称统一发号器,它具有全局唯一、粗略有序、可反解和可制造等特性,它支持三种发布模式:嵌入发布模式、中心服务器发布模式、REST发布模式,根据业务的性能需求,它可以产生最大...
全局唯一 可以利用时间的有序性, 并且在某个时间单元下采用自增序列 粗略有序 在分布式系统中, 难以做到绝对有序, 因此可以采用相对有序的方式 可反解 一个 ID 在生成后, 本身就带有很多信息量, 在存储层面可以省下...
库存管理系统课程设计 一、需求分析2 1.1 编写目的:2 1.2 工程背景:2 系统使用对象:3 1.4 功能需求:3 1.4.1 业务流程图:3 1.4.2 功能模块图:3 1.4.3 功能介绍:3 1.4.4 问题域描述:3 1.5 性能要求:3 1.6 ...
Vesta是一款通用的ID产生器,互联网俗称统一发号器,它具有全局唯一、粗略有序、可反解和可制造等特性,它支持三种发布模式:嵌入发布模式、中心服务器发布模式、REST发布模式,根据业务的性能需求,它可以产生最大...
• 不依赖数据库,业务调用方自己生成全局唯一ID是一个好方法 • shadow master保证写库高可用,只有一个写库提供服务,并不能完全保证一致性 • 内网DNS探测,可以实现在主库1出现问题后,延时一个时间,再进行主库...
,微服务架构之Spring Cloud Eureka 场景分析与实战,高性能必学之Mysql主从架构实践 ,架构师不得不知道的Spring事物不能回滚的深层次原因 ,分库分表之后分布式下如何保证ID全局唯一性 大型公司面试必答之数据结构...
图书销售系统的数据库设计 场景:某图书销售管理公司,随着业务的扩展,需要建立一个图书销售系统来进行图 书销售管理活动。 要建立一个图书销售系统,首先要设计其数据库,用来存取和管理图书信息。以下是 按数据库...
利用ZooKeeper的强一致性,能够保证在分布式高并发情况下节点创建的全局唯一性,即:同时有多个客户端请求创建 /currentMaster 节点,最终一定只有一个客户端请求能够创建成功。利用这个特性,就能很轻易的在分布式...
access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将...
拿redis举例,给消息分配一个全局id,只要是消费过该记录将<id,message>以k-v键值对的形式写入redis。在消费者开始消费 >消息之前,查看是否存在消费记录。 - 如何保证消费的可靠性传输 >换种说法就是保证消费不能...
TribeDB 通过全局唯一的表名,自动连接对应的数据库,并通过分表配置,将操作映射到涉及的分表,同时完成读写分离。 一切都由 TribeDB 自动完成,业务层不必关心数据的位置。当数据库负载过高需要添加服务器时,只...