Leaf:美团分布式ID生成服务开源

Leaf是美团基础研发平台推出的一个分布式ID生成服务,名字取自德国哲学家、数学家莱布尼茨的一句话:“There are no two identical leaves in the world.”Leaf具有高可靠、低延迟、全局惟一等特色。目前已经普遍应用于美团金融、美团外卖、美团酒旅等多个部门。具体的技术细节,可参考此前美团技术博客的一篇文章:《Leaf美团分布式ID生成服务》。近日,Leaf项目已经在Github上开源:https://github.com/Meituan-Dianping/Leaf,但愿能和更多的技术同行一块儿交流、共建。html

Leaf特性

Leaf在设计之初就秉承着几点要求:mysql

  1. 全局惟一,绝对不会出现重复的ID,且ID总体趋势递增。
  2. 高可用,服务彻底基于分布式架构,即便MySQL宕机,也能容忍一段时间的数据库不可用。
  3. 高并发低延时,在CentOS 4C8G的虚拟机上,远程调用QPS可达5W+,TP99在1ms内。
  4. 接入简单,直接经过公司RPC服务或者HTTP调用便可接入。

Leaf诞生

Leaf第一个版本采用了预分发的方式生成ID,便可以在DB之上挂N个Server,每一个Server启动时,都会去DB拿固定长度的ID List。这样就作到了彻底基于分布式的架构,同时由于ID是由内存分发,因此也能够作到很高效。接下来是数据持久化问题,Leaf每次去DB拿固定长度的ID List,而后把最大的ID持久化下来,也就是并不是每一个ID都作持久化,仅仅持久化一批ID中最大的那一个。这个方式有点像游戏里的按期存档功能,只不过存档的是将来某个时间下发给用户的ID,这样极大地减轻了DB持久化的压力。git

整个服务的具体处理过程以下:github

  • Leaf Server 1:从DB加载号段[1,1000]。
  • Leaf Server 2:从DB加载号段[1001,2000]。
  • Leaf Server 3:从DB加载号段[2001,3000]。

用户经过Round-robin的方式调用Leaf Server的各个服务,因此某一个Client获取到的ID序列多是:1,1001,2001,2,1002,2002……也多是:1,2,1001,2001,2002,2003,3,4……当某个Leaf Server号段用完以后,下一次请求就会从DB中加载新的号段,这样保证了每次加载的号段是递增的。算法

Leaf数据库中的号段表格式以下:sql

+-------------+--------------+------+-----+-------------------+-----------------------------+ | 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 Server加载号段的SQL语句以下:数据库

Begin UPDATE table SET max_id=max_id+step WHERE biz_tag=xxx SELECT tag, max_id, step FROM table WHERE biz_tag=xxx Commit 

总体上,V1版本实现比较简单,主要是为了尽快解决业务层DB压力的问题,而快速迭代出的一个版本。于是在生产环境中,也发现了些问题。好比:缓存

  1. 在更新DB的时候会出现耗时尖刺,系统最大耗时取决于更新DB号段的时间。
  2. 当更新DB号段的时候,若是DB宕机或者发生主从切换,会致使一段时间的服务不可用。

Leaf双Buffer优化

为了解决这两个问题,Leaf采用了异步更新的策略,同时经过双Buffer的方式,保证不管什么时候DB出现问题,都能有一个Buffer的号段能够正常对外提供服务,只要DB在一个Buffer的下发的周期内恢复,就不会影响整个Leaf的可用性。架构

这个版本代码在线上稳定运行了半年左右,Leaf又遇到了新的问题:并发

  1. 号段长度始终是固定的,假如Leaf原本能在DB不可用的状况下,维持10分钟正常工做,那么若是流量增长10倍就只能维持1分钟正常工做了。
  2. 号段长度设置的过长,致使缓存中的号段迟迟消耗不完,进而致使更新DB的新号段与前一次下发的号段ID跨度过大。

Leaf动态调整Step

假设服务QPS为Q,号段长度为L,号段更新周期为T,那么Q * T = L。最开始L长度是固定的,致使随着Q的增加,T会愈来愈小。可是Leaf本质的需求是但愿T是固定的。那么若是L能够和Q正相关的话,T就能够趋近一个定值了。因此Leaf每次更新号段的时候,根据上一次更新号段的周期T和号段长度step,来决定下一次的号段长度nextStep:

  • T < 15min,nextStep = step * 2
  • 15min < T < 30min,nextStep = step
  • T > 30min,nextStep = step / 2

至此,知足了号段消耗稳定趋于某个时间区间的需求。固然,面对瞬时流量几10、几百倍的暴增,该种方案仍不能知足能够容忍数据库在一段时间不可用、系统仍能稳定运行的需求。由于本质上来说,Leaf虽然在DB层作了些容错方案,可是号段方式的ID下发,最终仍是须要强依赖DB。

MySQL高可用

在MySQL这一层,Leaf目前采起了半同步的方式同步数据,经过公司DB中间件Zebra加MHA作的主从切换。将来追求彻底的强一致,会考虑切换到MySQL Group Replication

现阶段因为公司数据库强一致的特性还在演进中,Leaf采用了一个临时方案来保证机房断网场景下的数据一致性:

  • 多机房部署数据库,每一个机房一个实例,保证都是跨机房同步数据。
  • 半同步超时时间设置到无限大,防止半同步方式退化为异步复制。

Leaf监控

针对服务自身的监控,Leaf提供了Web层的内存数据映射界面,能够实时看到全部号段的下发状态。好比每一个号段双buffer的使用状况,当前ID下发到了哪一个位置等信息均可以在Web界面上查看。

Leaf Snowflake

Snowflake,Twitter开源的一种分布式ID生成算法。基于64位数实现,下图为Snowflake算法的ID构成图。

  • 第1位置为0。
  • 第2-42位是相对时间戳,经过当前时间戳减去一个固定的历史时间戳生成。
  • 第43-52位是机器号workerID,每一个Server的机器ID不一样。
  • 第53-64位是自增ID。

这样经过时间+机器号+自增ID的组合来实现了彻底分布式的ID下发。

在这里,Leaf提供了Java版本的实现,同时对Zookeeper生成机器号作了弱依赖处理,即便Zookeeper有问题,也不会影响服务。Leaf在第一次从Zookeeper拿取workerID后,会在本机文件系统上缓存一个workerID文件。即便ZooKeeper出现问题,同时刚好机器也在重启,也能保证服务的正常运行。这样作到了对第三方组件的弱依赖,必定程度上提升了SLA。

将来规划

  • 号段加载优化:Leaf目前重启后的第一次请求仍是会同步加载MySQL,之因此这么作而非服务初始化加载号段的缘由,主要是MySQL中的Leaf Key并不是必定都被这个Leaf服务节点所加载,若是每一个Leaf节点都在初始化加载全部的Leaf Key会致使号段的大量浪费。所以,将来会在Leaf服务Shutdown时,备份这个服务节点近一天使用过的Leaf Key列表,这样重启后会预先从MySQL加载Key List中的号段。
  • 单调递增:简易的方式,是只要保证同一时间、同一个Leaf Key都从一个Leaf服务节点获取ID,便可保证递增。须要注意的问题是Leaf服务节点切换时,旧Leaf 服务用过的号段须要废弃。路由逻辑,可采用主备的模型或者每一个Leaf Key 配置路由表的方式来实现。

关于开源

分布式ID生成的方案有不少种,Leaf开源版本提供了两种ID的生成方式:

  • 号段模式:低位趋势增加,较少的ID号段浪费,可以容忍MySQL的短期不可用。
  • Snowflake模式:彻底分布式,ID有语义。

读者能够按需选择适合自身业务场景的ID下发方式。但愿美团的方案能给予你们一些帮助,同时也但愿各位可以一块儿交流、共建。

Leaf项目Github地址:https://github.com/Meituan-Dianping/Leaf 。

若有任何疑问和问题,欢迎提交至Github issues

相关文章
相关标签/搜索