Leaf-美团分布式id生成系统

2023-11-18

概述

        分布式id生成已经有业界较为成熟的方案。现在公司使用的是美团的Leaf的号码段模式。之所以不用雪花算法模式还是因为雪花算法的自身缺陷,即时间回拨问题。本文就从源码角度剖析leaf项目的两种id生成模式。

        Leaf这种分布式id生成系统是美团自研发,具有全局唯一、高可用、高明发、低延迟、接入灵活(支持http、rpc)等优点。

        之前我们做全局唯一id的时候使用过雪花算法和UUID.这两者的缺点很明显。

        1、雪花算法无法解决始终回拨,当然leaf的雪花算法一定程度上克服了这一个缺点。

        2、UUID算法,uuid的值结构不够友好,杂乱无章,对mysql这种需要顺序id的情况不够友好。

        tips:Leaf的性能在4C8G的机器上QPS能压测到近5万/s,TP999 1ms

看完本文,您能够收获:

  • 代码级别的理解两种生成模式
  • 两种模式的优缺点。

主要内容

1、 号段模式

1.1、号码段模式的概念

Leaf项目的号码段模式强依赖DB,在数据库上添加了一个库表。结构如下。

CREATE TABLE `leaf_alloc` (
  `biz_tag` varchar(128) NOT NULL DEFAULT '',
  `max_id` bigint(20) NOT NULL DEFAULT 1,
  `step` int(11) NOT NULL,
  `description` varchar(256) DEFAULT NULL,
  `update_time` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  PRIMARY KEY (`biz_tag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

基于这个数据库表,我们要在leaf下配置两个参数:

leaf.name=zjtest
leaf.zk.list=172.1X.48.XX

号码段的含义大体就是各个服务实例在请求leaf项目的时候leaf会加载一批号码加载到leaf当中。leaf项目维护了以下结构。

        每一个biz_tag都会被包装成SegmentBuffer结构,而每一个SegementBuffer里面包含两个Segment.Segment也叫双Buffer模式。当前使用一个Buffer,如果当前Buffer使用到某个阈值就会开启一个线程,该线程请求数据库请求一批号码段缓存到新的Buffer当中。双Buffer能够解决号码耗尽加载数据库造成业务卡顿的现象。

        除了双Buffer优化,号码段模式还对缓存的号码多少做了优化。具体的根据更新时间感知当前leaf服务的请求频繁程度调整每次请求个数。根据上一次的更新周期T和号段长度step来决定这一次更新的号段长度,T<15分钟则step=step*2,15<T<30,step不变,t>30则step=step/2。

1.2代码步骤

1.2.1、初始化阶段

1、获取库表中所有的biz_tag字段集合。

2、将其保存在内存insertTagsSet中。并将tag为key,包装成Segment放入缓存当中。

3、启动定时任务updateCacheFromDb,每隔1分钟执行一次

        3.1、获取库表中所有的biz_tag字段集合。

        3.2、将其保存在内存insertTagsSet中。并将tag为key,包装成Segment放入缓存当中。

1.2.2、获取号码节点

1、判断初始化是否成功,不成功抛异常。

2、判断缓存是否有该请求key。如果没有该key则抛异常

3、如果有该key则执行以下步骤

        初次请求:从库表中缓存号码段缓存

        3.1、更新库表该key的maxId,用step值给maxId增加一个step

        3.2、将结果返回到内存,更新号码段缓存SegmentBuffer

        再次请求:从本地缓存的号码段获取值

        3.1、为buffer加读锁。

        3.2、根据状态确认是否添加一个线程从db中获取新号段

        3.3、释放读锁

        3.4、为buffer再加写锁

        3.5、获取segment的value字段,并+1,如果小于segment的max字段,则将value作为结果返回。

        3.6、释放写锁

在从号码段缓存获取号码的过程中,每次都在判断以下几个状态

        1、nextReady=false。如果这样说明本地没有可用号码段

        2、segment的剩余号码量是否小于一个step的90%,说明这个剩余号码量不足了

        3、获取该SegmentBuffer的threadRunning=false,如果为true说明该号码段有线程已经执行,不能重复启动线程。如果为false说明该SegmentBuffer可以启动一个线程。

如果满足以上三个条件,则启动一个线程。该线程逻辑如下:

1、获取双buffer中没有使用的segment 的下标。

2、更新数据库最大值,让然是当前max+1个step

3、然后将结果犯规给步骤1获得的闲置segment当中。

4、设置buffer的nextReady=true

5、threadRunning=false

1.2.3、优点

  • leaf的segment方式可以很方便的进行水平扩展,轻松支撑比较大的业务场景。
  • id号码趋势是递增的,对mysql的主键兼容性较好。
  • 号段都缓存到了本地一部分,在本地号段消耗完毕之前,如果db挂了还可以继续提供服务。
  • 从源码我们看出每一个服务器的初始值都是max_id开始的一段,所以如果迁移老业务,可以方面用该特性调整max_id的值,平滑迁移db行数据。

1.2.4、缺点

  • ID 号码不够随机,能够泄露发号数量的信息,不太安全。
  • DB 长时间宕机会造成整个系统不可用。

2、SNOWFLAKE 模式

2.1、SNOWFLAKE 模式的概念

        leaf中在普通的雪花算法上做了优化,对少量的时钟回拨做了兼容。同时workId字段是通过zk上维护。同时snowFlake模式对wokId缓存到了本地内存,如果zk宕机也会正常使用,对zk形成了弱依赖。

2.2、初始化阶段

1、连接zk

2、获取/forever节点,判断其是否存在

        2.1、如果不存在,

                2.1.1、创建/forever/ip:port节点,并在其上写入数据(Endpoint对象),包括:ip,port和当前时间撮。

                2.1.2、在本地创建文件workerID.properties,并写入数据workerID=0

                2.1.3、在线程池(schedule-upload-time)设置一个周期任务,每三秒钟同步时间到2.1创建的节点下。

        2.2、如果存在

                2.2.1、远程获取zk的目录/forever下所有数据

                2.2.2、从zk上获取取出本实例注册的workid,如果存在

则:

1、通过2.2.2获取的workId,更新本地workId信息.

2、检查从zk获取的时间撮是否小于当前时间。

        2.1、如果不是,抛异常。服务无法启动

3、启动周期任务,每三秒钟同步时间到zk.

4、将远程获取的workId同步到本地文件workerID.properties中。

        2.2.2、从zk上获取取出本实例注册的workid,如果不存在

        1.创建/forever/ip:port节点到zk,返回/sonwflake/${leaf.name}/snowflake/zjtest/forever/10.8.0.132:2181-0000000000

        2、获取远程workId(workId>=0 && workId<=2013)

        3、启动周期任务,每三秒钟同步时间到zk.

        4、将远程获取的workId同步到本地文件workerID.properties中。

2.3、获取id阶段(初次获取)

1、获取当前时间

2、如果是新开始,从1-100随机获取一个值(例如27)

3、lastTimestamp字段更新为当前时间

4、包装id

long id = ((timestamp - twepoch) << timestampLeftShift) | (workerId << workerIdShift) | sequence;

举个例子1584855141251547163 (19位)

5、包装成Result返回给业务。

2.4、获取id阶段(并发获取)

1、如果lastTimestamp和当前时间相同,说明发生并发获取

2、并发度为4095,在这个范围内获取上一次sequence,并+1

3、如果获取sequence==0,

        3.1、调整sequence为1-100之内随机数

        3.2、时间撮调整为lastTimestamp之后的时间。

获取id阶段(时间回拨)

1、比较当前时间和上一次获取时间

2、如果当前时间比上一次获取时间小,并且在5秒范围内

        2.1、等待一倍的时间

        2.2、如果仍然小于上一次获取时间抛异常

3、如果回拨严重,直接抛异常。 new Result(-3, Status.EXCEPTION);

2.5、特点:

存内存算法

单台机器并发为4096

发生时钟回拨的时候,代码手动调整了时间,如果时间差异小于5秒,则代码自动阻塞了最大10秒的时间。如果回拨严重会给业务直接抛异常。

总结

leaf的两个分布式生成id模式各有自己的优缺点,在选择的时候可以根据实际情况去选择。

segment模式依赖数据库,雪花算法需要引入zk。

segment模式是缓存号码段,号码的顺序性强,雪花算法直接计算号码,号码顺序性不强。

segment模式使用了很多优化手段例如双buffer做预缓存,加载时间调整缓存批次中号码数量;雪花模式在雪花算法的基础上做了兼容少量时间回拨问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Leaf-美团分布式id生成系统 的相关文章

随机推荐

  • 【JavaScript】npm、Yarn 和 pnpm 的区别

    npm Yarn 和 pnpm 都是用于管理和构建 JavaScript 项目的包管理工具 以下是它们之间的一些区别和特点 npm Node Package Manager npm 是 Node js 官方提供的包管理工具 是 JavaSc
  • 什么是数据中心IP,优缺点是什么?

    如果根据拥有者或者说发送地址来分类的话 可以将代理分为三类 数据中心ip 住宅ip 移动ip 本文我们来了解数据中心ip的原理以及他们的优势劣势 才能选择适合自己的代理 一 什么是数据中心ip代理 数据中心ip是由数据中心拥有和管理的IP的
  • Air780E

    目录 基础资料 探讨重点 实现功能 硬件准备 软件版本 在雁飞格物平台上创建产品 1 创建产品 2 查看雁飞格物平台接入协议 组成ClientId 组成userName 3 设备鉴权信息 鉴权参数计算 python版 4 MQTT直连雁飞格
  • 目标检测之选择性搜索算法实现(符动图演示)

    定义 选择性搜索是在对象检测中使用的区域提议算法 它的设计速度很快 召回率很高 它基于基于颜色 纹理 大小和形状兼容性的相似区域的分层分组计算 操作步骤 首先使用 Felzenszwalb 和 Huttenlocher 基于图像的分割方法
  • clouddrive挂载阿里云盘之后文件不显示

    clouddrive挂载阿里云盘之后文件不显示 此问题是小概率版本bug 待更新后解决 目前解决方案 重新挂载登录刷新
  • R手册(Visualise)--GGally(ggplot2 extensions)

    本站已停止更新 查看最新内容请移至本人博客 Wilen s Blog 文章目录 GGally ggmatrix ggplot2矩阵 ggpairs ggplot2广义配对图 ggscatmat 纯粹定量变量的传统散点图矩阵 返回ggplot
  • canvas绘制并导出图片(画笔)

    绘制 var canvas document getElementById myCanvas canvas width document getElementById drawLine offsetWidth canvas height d
  • HTML中的& nbsp; & ensp; & emsp;等6种空格标记

    代码 1 2 3 4 5 6
  • 【工作笔记】web项目从Spring3.x升级到Spring4.x

    Spring3 x升级到Spring4 x Spring升级首先到https docs spring io spring framework docs官方文档查看Spring各个版本的信息 比较不同 确定要升级的版本和要改动的地方 从官网可
  • 【第01例】IPD进阶

    目录 简介 专栏目录 内容详解 作者简介 相关课程 简介 今天来讲讲 IPD 中涉及的几个评审点 先来看一下 CDCP CDCP 是英文 Concept Decision Check Point 首字母的简称 也就是概念决策评审点 具体讲解
  • 解决:com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure(真实有效)

    数据库连接失败 在数据库连接失败 经常会有蛮多一系列的问题导致的原因 这个时候一定要多去尝试一下各种方法 并且做好自己的梳理 一 例如我在SpringBoot项目中使用了阿里的数据库连接池Driud 有次在启动的时候 会报这样的错 Caus
  • if 与elif

    if condition1 代码段1 if condition2 代码段2 else 代码段3 执行顺序 gt 进入if condition1 condition1 为true gt 执行 代码段1 gt 进入 if condition2
  • JVM工具-jstack

    jstack 打印线程堆栈信息 支持支持本地java进程 core文件以及远程java进程 命令格式 jstack options pid jstack options executable core jstack options serv
  • Mac电脑硬件/软件运行状况查看工具

    iStat Menus是一款系统监控和管理工具 旨在帮助Mac用户实时监控电脑的各项硬件和软件信息 它以直观和定制化的方式提供了丰富的系统状态指标 让用户能够全面了解和管理自己的Mac电脑 iStat Menus提供了一系列的菜单栏指示项目
  • iphone文件访问ftp服务器,ipad ftp服务器 iPhone/iPad访问FTP服务器设置步骤

    ipad ftp服务器 iPhone iPad访问FTP服务器设置步骤 2013 02 27 15 35 39 脚本之家 有时候出门在外需要用到电脑中的文件 但是却没有携带电脑或者U盘 那么该怎么办呢 其实在IOS系统上 一些实用的应用软件
  • 云计算基础-基本概念(一)

    云计算基本概念 一 一 网络 1 VPC 虚拟专有网络 2 LB 负载均衡 3 vFW 虚拟防火墙 4 TOR 5 Region 6 AZ 可用区 7 Overlay和Underlay 8 VPC对等连接 9 BGW 10 NAT Gate
  • MySQL 8.0 修改密码 步骤详解(详细图解)

    1 以管理员身份打开cmd窗口 定位到MySQL安装目录下的bin目录 输入net stop mysql 回车 关闭MySQL数据库 2 输入mysqld console skip grant tables shared memory my
  • Python 爬虫进阶必备

    今日网站 aHR0cHM6Ly93d3cuemRheWUuY29tL0ZyZWVJUExpc3QuaHRtbA 这个网站来自咸鱼的技术交流群 抓包分析与加密定位 这个网站是某代理商的免费代理页面 我们想要实现的就是这个页面上免费代理的提取
  • Qt 支持HEIC/HEIF格式图片

    HEIF 格式简介 源于百度知道 heic的格式是苹果针对iOS11专门研发的一个照片格式 Heic是Apple iOS和macOS的文件格式 用于处理图像和视频 Heic是IOS 11系统中取代原始视频和照片的H 264和JEP格式 He
  • Leaf-美团分布式id生成系统

    概述 分布式id生成已经有业界较为成熟的方案 现在公司使用的是美团的Leaf的号码段模式 之所以不用雪花算法模式还是因为雪花算法的自身缺陷 即时间回拨问题 本文就从源码角度剖析leaf项目的两种id生成模式 Leaf这种分布式id生成系统是