分布式Id生成策略-美团Leaf

之前在做物流相关的项目时候,需要在分布式系统生成运单的id。

1.需求:

1.全局唯一性:不能出现重复的ID。(基本要求)

2.递增:大多数关系型数据库(如 MySQL)使用 B+ 树作为索引结构。如果 ID 是递增的,新数据总是追加到索引的末尾,这样索引的维护成本较低,因为数据库不需要频繁调整树的结构。相反,如果 ID 是随机的,数据插入时可能需要频繁调整索引结构,导致写入性能下降。

3.业务性:对具体的场景的ID要具备业务的特性,比如顺丰运单ID为类似SF1000000000760

4.精简性:某些场景下的ID不宜过长,所以对位数/长度有所限制。

在分布式系统中我们还应该考虑:生成方案能子啊多节点正常工作,能够有一定的解决故障问题,有高可用性,生成ID的速率要快,能够随业务扩展进行水平拓展,比如在分库分表后也能兼容原来的ID。

2.方案

有几个方案可以考虑:

本地的UUID生成:它的优点的生成速度很快,产生的值也几乎达到不重复的要求,但是产生的ID值比较长,可以达到36个字符,可读性还非常差,而且ID完全随机,没有任何的顺序。因此这个方案不考虑。

依靠数据库自增特性:为不同业务模块建立一张自增表维护递增序列。这种方法比较简单,靠数据库保证自增机制。缺点也很明显,当数据库异常整个系统不可用,而且ID生成的性能瓶颈也限制在单台MYSQL数据库。单台数据库性能问题可以同过部署多态机器,每个机器设置不同初始值,并且步长和机器数相同来优化。但是也带来很多问题,比如扩容很是麻烦。

Redis实现:通过给不同业务设计不同的key,通过INCR命令,对key自增,得到全局唯一并有序的ID。Redis每秒支持10W的读写,所以性能问题得到解决,但是Redis依靠内存,虽然有持久化机制,但是它持久化是先写内存再异步刷盘,遇到没来得及持久化就宕机也是会出大问题的。

雪花算法: 是 Twitter 于 2010 年开源的一种分布式唯一 ID 生成算法,它可以在分布式系统中生成高效、有序的唯一 ID。雪花算法生成的 ID 是一个 64 位的长整型long),在保证唯一性的同时,也确保了生成的 ID 按时间顺序递增。

在这里插入图片描述

  1. 全局唯一性:生成的 ID 保证全局唯一,雪花算法结合时间戳、机器 ID 和序列号确保在分布式系统中不会产生重复的 ID。

  2. 高效生成:雪花算法不依赖数据库,因此生成 ID 的过程非常高效,可以在本地的内存中生成,具有极高的性能。每台机器每秒钟可以生成上百万个 ID。

  3. 趋势递增:生成的 ID 是按时间顺序递增的,尤其是基于时间戳的部分,使得 ID 具有递增的特性。这对数据库插入数据时索引的维护非常友好(例如 B+ 树结构索引的维护成本较低)。

  4. 灵活可扩展:通过调整数据中心 ID 和机器 ID 的位数分配,可以根据业务的需要适当扩大集群规模或提升单机 ID 生成的并发能力。

    雪花算法依赖 机器码 来保证不同机器生成的 ID 唯一性。如果在分布式环境中多台机器未能准确区分它们的机器码,可能导致多个机器在同一时间生成相同的 ID,造成 ID 冲突。因此,在分布式系统中,每台服务器、虚拟机或容器必须手动指定一个唯一的机器标识符。**

雪花算法的不足

  • 依赖机器时间:由于 ID 的递增性依赖时间戳,一旦服务器的系统时钟发生回拨,可能会引发 ID 冲突或无法生成 ID 的问题。虽然有一些解决方案(如等待或借助其他算法生成 ID),但还是可能影响生成 ID 的稳定性。

3.美团Leaf

下面就将引入我选取的美团Leaf这个id生成策略。

其源码托管于GitHub:https://github.com/Meituan-Dianping/Leaf

这里有个美团的技术播客,专门介绍了Leaf:https://tech.meituan.com/2017/04/21/mt-leaf.html

目前Leaf覆盖了美团点评公司内部金融、餐饮、外卖、酒店旅游、猫眼电影等众多业务线。在4C8G VM基础上,通过公司RPC方式调用,QPS压测结果近5w/s,TP999 1ms。

Leaf 提供两种生成的ID的方式(segment模式和snowflake模式),我们采用segment模式(号段)来生成运单号。

号段模式

号段模式采用的是基于MySQL数据生成id的,它并不是基于MySQL表中的自增长实现的,因为基于MySQL的自增长方案对于数据库的依赖太大了,性能不好,Leaf的号段模式是基于一张表来实现,每次获取一个号段,生成id时从内存中自增长,当号段用完后再去更新数据库表,如下:

在这里插入图片描述

字段说明:

  • biz_tag:业务标签,用来区分业务
  • max_id:表示该biz_tag目前所被分配的ID号段的最大值
  • step:表示每次分配的号段长度。如果把step设置得足够大,比如1000,那么只有当1000个号被消耗完了之后才会去重新读写一次数据库。读写数据库的频率从1减小到了1/step
  • description:描述
  • update_time:更新时间

架构图如下:

在这里插入图片描述

图片来源:https://tech.meituan.com/2017/04/21/mt-leaf.html

说明:test_tag在**第一台Leaf机器上是11000的号段**,当这个号段用完时,会去加载另一个长度为step=1000的号段,假设另外两台号段都没有更新,这个时候第一台机器新加载的号段就应该是30014000。同时数据库对应的biz_tag这条数据的max_id会从3000被更新成4000,更新号段的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

Leaf 取号段的时机是在号段消耗完的时候进行的,也就意味着号段临界点的ID下发时间取决于下一次从DB取回号段的时间,并且在这期间进来的请求也会因为DB号段没有取回来,导致线程阻塞。如果请求DB的网络和DB的性能稳定,这种情况对系统的影响是不大的,但是假如取DB的时候网络发生抖动,或者DB发生慢查询就会导致整个系统的响应时间变慢。Leaf服务内部有号段缓存,即使DB宕机,短时间内Leaf仍能正常对外提供服务。

双buffer优化

Leaf为此做了优化,增加了双buffer优化。

当号段消费到某个点时就异步的把下一个号段加载到内存中。而不需要等到号段用尽的时候才去更新号段。这样做就可以很大程度上的降低系统的TP999指标。
在这里插入图片描述

双buffer原理,来自:https://tech.meituan.com/2017/04/21/mt-leaf.html

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

  • 每个biz-tag都有消费速度监控,通常推荐segment长度设置为服务高峰期发号QPS(秒处理事务数)的600倍(10分钟),这样即使DB宕机,Leaf仍能持续发号10-20分钟不受影响。
  • 每次请求来临时都会判断下个号段的状态,从而更新此号段,所以偶尔的网络抖动不会影响下个号段的更新。

4.项目使用

我们只用到了号段的方式,并没有使用雪花方式,所以只需要创建数据库表即可

将其镜像运行:

docker run \
-d \
-v /hujx/meituan-leaf/leaf.properties:/app/conf/leaf.properties \
--name meituan-leaf \
-p 28838:8080 \
--restart=always \
registry.cn-hangzhou.aliyuncs.com/itheima/meituan-leaf:1.0.1

leaf.properties

leaf.name=leaf-server
leaf.segment.enable=true
leaf.jdbc.url=jdbc:mysql://192.168.150.101:3306/hjx_leaf?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false
leaf.jdbc.username=root
leaf.jdbc.password=123leaf.snowflake.enable=false
#leaf.snowflake.zk.address=
#leaf.snowflake.port=

创建sl_leaf数据库脚本:

CREATE TABLE `leaf_alloc` (`biz_tag` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',`max_id` bigint NOT NULL DEFAULT '1',`step` int NOT NULL,`description` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`biz_tag`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;-- 插入运单号生成规划数据
INSERT INTO `leaf_alloc` (`biz_tag`, `max_id`, `step`, `description`, `update_time`) VALUES ('transport_order', 1000000000001, 100, 'Test leaf Segment Mode Get Id', '2023-07-07 11:32:16');

封装服务

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.sl.transport.common.enums.IdEnum;
import com.sl.transport.common.exception.SLException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;/*** id服务,用于生成自定义的id*/
@Service
public class IdService {@Value("${sl.id.leaf:}")private String leafUrl;/*** 生成自定义id** @param idEnum id配置* @return id值*/public String getId(IdEnum idEnum) {String idStr = this.doGet(idEnum);return idEnum.getPrefix() + idStr;}private String doGet(IdEnum idEnum) {if (StrUtil.isEmpty(this.leafUrl)) {throw new SLException("生成id,sl.id.leaf配置不能为空.");}//访问leaf服务获取idString url = StrUtil.format("{}/api/{}/get/{}", this.leafUrl, idEnum.getType(), idEnum.getBiz());//设置超时时间为10sHttpResponse httpResponse = HttpRequest.get(url).setReadTimeout(10000).execute();if (httpResponse.isOk()) {return httpResponse.body();}throw new SLException(StrUtil.format("访问leaf服务出错,leafUrl = {}, idEnum = {}", this.leafUrl, idEnum));}}
public enum IdEnum implements BaseEnum {TRANSPORT_ORDER(1, "运单号", "transport_order", "segment", "SL");private Integer code;private String value;private String biz; //业务名称private String type; //类型:自增长(segment),雪花id(snowflake)private String prefix;//id前缀IdEnum(Integer code, String value, String biz, String type, String prefix) {this.code = code;this.value = value;this.biz = biz;this.type = type;this.prefix = prefix;}@Overridepublic Integer getCode() {return this.code;}@Overridepublic String getValue() {return this.value;}public String getBiz() {return biz;}public String getType() {return type;}public String getPrefix() {return prefix;}@Overridepublic String toString() {final StringBuffer sb = new StringBuffer("IdEnum{");sb.append("code=").append(code);sb.append(", value='").append(value).append('\'');sb.append(", biz='").append(biz).append('\'');sb.append(", type='").append(type).append('\'');sb.append(", prefix='").append(prefix).append('\'');sb.append('}');return sb.toString();}
}

使用步骤:

  • 在配置文件中进行配置sl.id.leaf为: 地址:你的服务端口 如:http://192.168.150.101:28838
    pend(“, type='”).append(type).append(‘’‘);
    sb.append(", prefix=’").append(prefix).append(‘’‘);
    sb.append(’}');
    return sb.toString();
    }
    }

使用步骤:

  • 在配置文件中进行配置sl.id.leaf为: 地址:你的服务端口 如:http://192.168.150.101:28838
  • 在Service中注入IdService,调用getId()方法即可,例如:idService.getId(IdEnum.TRANSPORT_ORDER)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/147108.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

三菱FX3U-4DA(4通道模拟量输出)使用说明

FX3U-4DA连接在FX3G/FX3GC/FX3U/FX3UC可编程控制器上,是将来自可编程控制器的4个通道的数字值转换成模拟量值(电压/电流)并输出的模拟量特殊功能模块。 1、FX3G/FX3GC/FX3U/FX3UC可编程控制器上最多可以连接8台*1(包括其它特殊功能模块的连接台数。) 2、可以对各通道…

Global Attention Decoder for Chinese Spelling Error Correction(ACL2021)

Global Attention Decoder for Chinese Spelling Error Correction(ACL2021) 一.概述 作者认为现有的纠错方法大多是基于局部上下文信息进行纠错,没有考虑句子中错词的影响。将注意力放在错误上下文信息上可能会误导并降低CSC(Chinese Spelling Correction)的整体性…

shopro前端 短信登录只显示模板不能正常切换

删掉 换成下面的代码 // 打开授权弹框 export function showAuthModal(type smsLogin) {const modal $store(modal);setTimeout(() > {modal.$patch((state) > {state.auth type;});}, 100); }

数据集 InterHand2.6M 双手交互 三维手势建模 >> DataBall

数据集 InterHand2.6M 双手交互 三维手势建模 人工智能 深度学习 >> DataBall 数据集 InterHand2.6M,双手/单手交互 ---------------------------------------------------------------------------------------------------------- Train set * Train (H):…

MybatisPlus代码生成器使用

一、前言 Mybatis逆向工程也可以生成代码,但配置太过复杂,不便于后期维护,Mybatis Plus 主动集成了代码的自动生成,用起来也很方便,两种代码自动生成我都用过,没有好坏之分,如果非要我推荐哪一…

跨游戏引擎的H5渲染解决方案(腾讯)

本文是腾讯的一篇H5 跨引擎解决方案的精炼。 介绍 本文通过实现基于精简版的HTML5(HyperText Mark Language 5)来屏蔽不同引擎,平台底层的差异。 好处: 采用H5的开发方式,可以将开发和运营分离,运营部门自…

一个安卓鸿蒙化工具

DevEco插件,为已有安卓项目鸿蒙化加速。 目前支持: 1、安卓Vector Assets转svg; 2、json转ets model; 3、kotlin model转ets model; 下载地址:andtoharplugin1.1.0 安装: deveco插件安装选硬…

傻白甜萌妹爆改成长型女主!男频番的花瓶也有高光?

“师父,师妹不是任何人的依附,也不是小琼峰的一个摆件。” 能说出这句话的男主,堪称人间清醒。 男频作品的女性塑造向来是备受瞩目的话题。“镶边”、“挂件”、“花瓶”…总有这样的标签一个个打在“她们”身上,看似暗讽&#…

seL4 Untyped(二)

链接: Untyped Untyped 这篇主要是针对seL4物理内存管理的介绍。 物理内存 在seL4系统中,除了内核占用的一小部分静态内存之外,其他的所有的物理内存都是用户一级管理的。seL4在启动时创建的对象能力,以及seL4管理的其余物理资源&#xf…

tensorflow底层架构

tensorflow底层架构 架构图 Training libraries 和 Inference libs(训练库和推理库) Training libraries:用于模型的训练过程,包括定义模型、计算梯度、更新模型权重等。这些库提供了在训练过程中所需的所有功能。Inference lib…

推荐几本值得阅读的书籍!

大家好,这里是大话硬件。 初次关注我公众号的朋友第一反应基本都是认为内容太专业! 其实不然,大话硬件公众号除了有硬件设计方面的内容,还包含书籍推荐,个人反思总结模块等内容。 今天这篇文章继上篇荐书《相见恨晚的…

学习IEC 62055付费系统标准

1.IEC 62055 国际标准 IEC 62055 是目前关于付费系统的唯一国际标准,涵盖了付费系统、CIS 用户信息系统、售电系统、传输介质、数据传输标准、预付费电能表以及接口标准等内容。 IEC 62055-21 标准化架构IEC 62055-31 1 级和 2 级有功预付费电能表IEC 62055-41 STS…

如何快速恢复误删除的文件?教你一招,不花一分钱,三步就可以搞定!

电脑文件越存越多,我们都会不定时的进行清理,但有时候,我们难免会误删除一些重要文件。当遇到类似情况的时候,不要着急,很多时候,删除的文件是有办法恢复的。 市面上有很多文件恢复软件,就可以帮…

常用工具推荐!分享7款AI论文修改软件工具网站

在当今学术研究和写作领域,AI论文修改软件工具已经成为了不可或缺的助手。这些工具不仅能够帮助研究人员提高写作效率,还能确保论文的质量和原创性。以下是七款值得推荐的AI论文修改软件工具网站,其中特别推荐千笔-AIPassPaper。 1. 千笔-AI…

YOLOv8改进 | 特征融合篇,YOLOv8添加iAFF(多尺度通道注意力模块),并与C2f结构融合,提升小目标检测能力

摘要 特征融合,即来自不同层或分支的特征的组合,是现代网络架构中无处不在的一部分。虽然它通常通过简单的操作(如求和或拼接)来实现,但这种方式可能并不是最佳选择。在这项工作中,提出了一种统一且通用的方案,即注意力特征融合(Attentional Feature Fusion),适用于…

心理辅导系统:Spring Boot技术驱动

3 系统分析 3.1可行性分析 在进行可行性分析时,我们通常根据软件工程里方法,通过四个方面来进行分析,分别是技术、经济、操作和法律可行性。因此,在基于对目标系统的基本调查和研究后,对提出的基本方案进行可行性分析。…

攻防世界---->Windows_Reverse1(补)

做题笔记。 做题回顾。 假设,我们不知道地址随机怎么办?不能动调,只能静态分析。 下载 查壳 upx脱壳。 32ida打开。 动调报错。 重新打开,静态分析。 跟进关键函数。 不明白可以反汇编和汇编一起看。 溯源。 *decode 取值等于 by…

robomimic应用教程(一)——模型训练

Robomimic使用集中式配置系统来指定所有级别的(超)参数 本文介绍了配置(推荐)和启动训练运行的两种方法 目录 一、使用config json(推荐) 二、在代码中构造一个配置对象 三、查看运行结果 1. 实验结果会存在一个固定文件夹中…

嵌入式入门小工程

此代码基于s3c2440 1.点灯 //led.c void init_led(void) {unsigned int t;t GPBCON;t & ~((3 << 10) | (3 << 12) | (3 << 14) | (3 << 16));t | (1 << 10) | (1 << 12) | (1 << 14) | (1 << 16);GPBCON t; }void le…

如何精准高效做好网站安全防护?一文解读

企业数字化转型过程中&#xff0c;难免会受到多种网络安全威胁带来的负面影响。比如攻击者通过利用Web服务程序以及网站系统的安全漏洞&#xff0c;对企业进行数据窃取等破坏活动&#xff0c;严重损害企业利益。如何精准高效做好网站安全防护&#xff1f;相信本文会为你带来启发…