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

Leaf特性

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

  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持久化的压力。

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

  • 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数据库中的号段表格式如下:

+-------------+--------------+------+-----+-------------------+-----------------------------+
| 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

至此,满足了号段消耗稳定趋于某个时间区间的需求。当然,面对瞬时流量几十、几百倍的暴增,该种方案仍不能满足可以容忍数据库在一段时间不可用、系统仍能稳定运行的需求。因为本质上来讲,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

Leaf:美团分布式ID生成服务开源的更多相关文章

  1. 分布式ID生成服务,真的有必要搞一个

    目录 阐述背景 Leaf snowflake 模式介绍 Leaf segment 模式介绍 Leaf 改造支持RPC 阐述背景 不吹嘘,不夸张,项目中用到ID生成的场景确实挺多.比如业务要做幂等的时候 ...

  2. spring boot / cloud (十六) 分布式ID生成服务

    spring boot / cloud (十六) 分布式ID生成服务 在几乎所有的分布式系统或者采用了分库/分表设计的系统中,几乎都会需要生成数据的唯一标识ID的需求, 常规做法,是使用数据库中的自动 ...

  3. 美团分布式ID生成框架Leaf源码分析及优化改进

    本文主要是对美团的分布式ID框架Leaf的原理进行介绍,针对Leaf原项目中的一些issue,对Leaf项目进行功能增强,问题修复及优化改进,改进后的项目地址在这里: Leaf项目改进计划 https ...

  4. 9种分布式ID生成之 美团(Leaf)实战

    整理了一些Java方面的架构.面试资料(微服务.集群.分布式.中间件等),有需要的小伙伴可以关注公众号[程序员内点事],无套路自行领取 更多优选 一口气说出 9种 分布式ID生成方式,面试官有点懵了 ...

  5. Leaf——美团点评分布式ID生成系统 UUID & 类snowflake

    Leaf——美团点评分布式ID生成系统 https://tech.meituan.com/MT_Leaf.html

  6. 美团技术分享:深度解密美团的分布式ID生成算法

    本文来自美团技术团队“照东”的分享,原题<Leaf——美团点评分布式ID生成系统>,收录时有勘误.修订并重新排版,感谢原作者的分享. 1.引言 鉴于IM系统中聊天消息ID生成算法和生成策略 ...

  7. 分布式唯一ID生成服务

    SNService是一款基于分布式的唯一ID生成服务,主要用于提供大数量业务数据建立唯一ID的需要;服务提供最低10K/s的唯一ID请求处理.如果你部署服务的CPU资源达到4核的情况下那该服务最低可以 ...

  8. 图解Janusgraph系列-分布式id生成策略分析

    JanusGraph - 分布式id的生成策略 大家好,我是洋仔,JanusGraph图解系列文章,实时更新~ 本次更新时间:2020-9-1 文章为作者跟踪源码和查看官方文档整理,如有任何问题,请联 ...

  9. 分布式ID生成方案汇总

    1.目标 1.1.全局唯一 不能出现重复的ID,全局唯一是最基本的要求. 1.2.趋势有序 业务上分页查询需求,排序需求,如果ID直接有序,则不必建立更多的索引,增加查询条件. 而且Mysql Inn ...

随机推荐

  1. Python提示AttributeError 或者DeprecationWarning: This module was deprecated解决方法

    Python提示AttributeError 或者DeprecationWarning: This module was deprecated解决方法 在使用Python的sklearn库时,发现sk ...

  2. Mybatis进阶学习笔记——关系查询——一对多查询

    一个客户拥有多个订单 <resultMap type="User" id="UserOrderResultMap"> <id column=& ...

  3. python学习:绝对路径和相对路径

    python学习:绝对路径和相对路径 大牛们应该对路径都很了解了,这篇文章主要给像我这样的入门小白普及常识用的,啊哈 下面的路径介绍针对windows,其他平台的暂时不是很了解. 在编写的py文件中打 ...

  4. Python数据分析入门

    Python数据分析入门 最近,Analysis with Programming加入了Planet Python.作为该网站的首批特约博客,我这里来分享一下如何通过Python来开始数据分析.具体内 ...

  5. 编码器AE & VAE

    学习总结于国立台湾大学 :李宏毅老师 自编码器 AE (Auto-encoder)    & 变分自动编码器VAE(Variational Auto-encoder)             ...

  6. MR运动静止用户区分

    1.客户端打开菜单[MR]-[MR室内室外判定设置] 设置主小区是室外站且主小区信号比较强时RSRP门限 2.设置"上报数据用户临小区切换次数门限设置"值为15 mysql中t_m ...

  7. Android系统信息(内存、cpu、sd卡、电量、版本)获取

    Android系统信息(内存.cpu.sd卡.电量.版本)获取 /*APPInfo.java*/ public class AppInfo { private String appLable; pri ...

  8. RunLoop的应用场景---关于滑动tableView的时候NSTimer 暂停的问题

    1.我们经常会在应用中看到tableView 的header 上是一个横向ScrollView,一般我们使用NSTimer,每隔几秒切换一张图片.可是当我们滑动tableView的时候,顶部的scol ...

  9. IntelliJ IDEA使用教程

    注:本文来源:李学凯 的<IntelliJ IDEA使用教程 (总目录篇)> 一:(总目录篇)_1:硬件要求 IntelliJ IDEA 对硬件的要求看上去不是很高.可是实际在开发中其实并 ...

  10. python魔法函数(二)之__getitem__、__len__、__iter__

    魔法函数会增强python类的类型,独立存在 __getitem class Company: def __init__(self, employees): self.employees = empl ...