面试官:说说你了解的分布式 ID 生成方案

news2024/9/22 19:24:15
  1. 为什么需要分布式 ID

对于单体系统来说,主键 ID 常用主键自动的方式进行设置。这种 ID 生成方法在单体项目是可行的,但是对于分布式系统,分库分表之后就不适应了。比如订单表数据量太大了,分成了多个库,如果还采用数据库主键自增的方式,就会出现在不同库 id 一致的情况,虽然是不符合业务的。

2. 业务系统对分布式 ID 有什么要求

  • 全局唯一性:ID 是作为唯一的标识,不能出现重复;

  • 趋势递增:互联网比较喜欢 MySQL 数据库,而 MySQL 数据库默认使用 InnoDB 存储引擎,其使用的是聚集索引,使用有序的主键 ID 有利于保证写入的效率;

  • 单调递增:保证下一个 ID 大于上一个 ID,这种情况可以保证事务版本号,排序等特殊需求实现;

  • 信息安全:前面说了 ID 要递增,但是最好不要连续。如果 ID 是连续的,容易被恶意爬取数据,指定一系列连续的,所以 ID 递增但是不规则是最好的。

3. 分布式 ID 生成方案

  • UUID

  • 数据库自增

  • 号段模式

  • Redis 实现

  • 雪花算法(SnowFlake)

  • 百度 Uidgenerator

  • 美团 Leaf

  • 滴滴 TinyID

3.1 UUID

UUID (Universally Unique Identifier),通用唯一识别码的缩写。

UUID 的标准型式包含 32 个 16 进制数字,以连字号分为五段,形式为 8-4-4-4-12 的 36 个字符,示例 863e254b-ae34-4371-87da-204b71d46a7b。

UUID 理论上的总数为 1632=2128,约等于 3.4 x 10^38。

  • 优点:性能非常高,本地生成的,不依赖于网络;

  • 缺点:不易存储。16 字节 128 位,36 位长度的字符串信息不安全,基于 MAC 地址生成 UUID 算法可能会造成 MAC 地址泄露,暴露使用者的位置。UUID 的无序性可能会引起数据位置频繁变动,影响性能。

3.2 数据库自增

在分布式环境也可以使用 MySQL 自增实现分布式 ID 的生成。如果分库分表了,当然不是简单地设置好 auto_increment_increment 和 auto_increment_offset 就行。在分布式系统中,我们可以多部署几台机器,每台机器设置不同的初始值,且步长和机器数相等。

比如有两台机器,设置步长 step 为 2。Server1 的初始值为 1(1, 3, 5, 7, 9, 11…),Server2 的初始值为 2(2, 4, 6, 8, 10…)。

这是 Flickr 团队在 2010 年撰文介绍的一种主键生成策略(Ticket Servers: Distributed Unique Primary Keys on the Cheap )。

假设有 N 台机器,step 就要设置为 N,如下图进行设置:

这种方案看起来是可行的。但是如果要扩容,步长 step 等要重新设置。
假如只有一台机器,步长就是 1,比如 1,2,3,4,5,6。这时候如果要进行扩容,就要重新设置。
机器 2 可以挑一个偶数的数字,这个数字在扩容时间内,数据库自增达不到这个数的,然后步长就是 2。机器 1 要重新设置 step 为 2,然后还是以一个奇数开始进行自增。
这个过程看起来不是很杂,但是如果机器很多的话,那就要花很多时间去维护重新设置。

这种实现的缺陷:

  • ID 没有了单调递增的特性,只能趋势递增。有些业务场景可能不符合;

  • 数据库压力还是比较大,每次获取 ID 都需要读取数据库,只能通过多台机器提高稳定性和性能。

3.3 号段模式

这种模式也是现在生成分布式 ID 的一种方法。实现思路是,会从数据库获取一个号段范围,比如 [1,1000],生成 1 到 1000 的自增 ID 加载到内存中。

建表结构如下:

CREATE TABLE id_generator (  id int(10) NOT NULL,  max_id bigint(20) NOT NULL COMMENT '当前最大id',  step int(20) NOT NULL COMMENT '号段的布长',  biz_type  int(20) NOT NULL COMMENT '业务类型',  version int(20) NOT NULL COMMENT '版本号',  PRIMARY KEY (`id`))

  • biz_type:不同业务类型;

  • max_id :当前最大的 id;

  • step:代表号段的步长;

  • version :版本号,就像 MVCC 一样,可以理解为乐观锁。

等 ID 都用完了,再去数据库获取,然后更改最大值:

update id_generator set max_id = #{max_id+step}, version = version + 1 where version = # {version} and biz_type = XXX

  • 优点:有比较成熟的方案,像百度 Uidgenerator,美团 Leaf;

  • 缺点:依赖于数据库实现。

3.4 Redis 实现

Redis 分布式 ID 实现主要是通过提供像 INCR 和 INCRBY 这样的自增原子命令。由于 Redis 单线程的特点,可以保证 ID 的唯一性和有序性。

这种实现方式,如果并发请求量上来后,就需要集群。不过集群后,又要和传统数据库一样,设置分段和步长。

  • 优点:Redis 性能相对比较好,而且可以保证唯一性和有序性;

  • 缺点:需要依赖 Redis 来实现,系统需要引入 Redis 组件。

3.5 雪花算法(SnowFlake)

雪花算法(Snowflake)是由 Twitter 开源的分布式 ID 生成算法,以划分命名空间的方式将 64-bit 位分割成多个部分,每个部分代表不同的含义。在 Java 中 Long 类型是 64 位的,所以 Java 程序中一般使用 Long 类型存储。

  • 第一部分:第一位占用 1 bit,始终是 0,是一个符号位,不使用;

  • 第二部分:第 2 位开始的 41 位是时间戳。41-bit 位可表示 241 个数,每个数代表毫秒,那么雪花算法可用的时间年限是 (241)/(1000606024365)=69 年的时间;

  • 第三部分:10-bit 位可表示机器数,即 2^10 = 1024 台机器。通常不会部署这么多台机器;

  • 第四部分:12-bit 位是自增序列,可表示 2^12 = 4096 个数。觉得一毫秒个数不够用也可以调大点。

优缺点:

  • 优点:雪花算法生成的 ID 是趋势递增,不依赖数据库等第三方系统。生成 ID 的效率非常高,稳定性好,可以根据自身业务特性分配 bit 位,比较灵活;

  • 缺点:雪花算法强依赖于机器时钟。如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。如果恰巧回退前生成过一些 ID,而时间回退后,生成的 ID 就有可能重复。

3.6 百度 Uidgenerator

百度 UidGenerator 是百度开源基于 Java 语言实现的唯一 ID 生成器,是在雪花算法 Snowflake 的基础上做了一些改进。


引用官网的解释:

UidGenerator 是 Java 实现的, 基于 Snowflake 算法的唯一 ID 生成器。UidGenerator 以组件形式工作在应用项目中,支持自定义 workerId 位数和初始化策略,从而适用于 docker 等虚拟化环境下实例自动重启、漂移等场景。在实现上, UidGenerator 通过借用未来时间来解决 sequence 天然存在的并发限制;采用 RingBuffer 来缓存已生成的 UID,并行化 UID 的生产和消费,同时对 CacheLine 补齐。避免了由 RingBuffer 带来的硬件级「伪共享」问题. 最终单机 QPS 可达 600 万。

Snowflake 算法描述:指定机器 & 同一时刻 & 某一并发序列,是唯一的。据此可生成一个 64 bits 的唯一 ID(long)。默认采用上图字节分配方式:

  • sign(1bit):固定 1bit 符号标识,即生成的 UID 为正数;

  • delta seconds (28 bits):当前时间,相对于时间基点"2016-05-20"的增量值,单位:秒,最多可支持约8.7年;

  • worker id (22 bits):机器 id,最多可支持约 420 万次机器启动。内置实现为在启动时由数据库分配,默认分配策略为用后即弃,后续可提供复用策略;

  • sequence (13 bits):每秒下的并发序列,13 bits 可支持每秒 8192 个并发。

详细介绍可参考官网说明:

ttps://github.com/baidu/uid-generator/blob/master/README.zh_cn.md

3.7  美团 Leaf

Leaf 这个名字是来自德国哲学家、数学家莱布尼茨的一句话:
There are no two identical leaves in the world “世界上没有两片相同的树叶”

Leaf 提供两种生成的 ID 的方式:号段模式(Leaf-segment)和 Snowflake 模式(Leaf-snowflake)。你可以同时开启两种方式,也可以指定开启某种方式,默认两种方式为关闭状态。

Leaf­segment 数据库方案

其实就是前面介绍的号段模式的改进,可以引用美团技术博客的介绍:

第一种 Leaf-segment 方案,在使用数据库的方案上,做了如下改变:

  • 原方案每次获取 ID 都得读写一次数据库,造成数据库压力大。改为利用 proxy server 批量获取,每次获取一个 segment(step 决定大小)号段的值。用完之后再去数据库获取新的号段,可以大大减轻数据库的压力;

  • 各个业务不同的发号需求用 biz_tag 字段来区分,每个 biz-tag 的 ID 获取相互隔离,互不影响。如果以后有性能需求需要对数据库扩容,不需要上述描述的复杂的扩容操作,只需要对 biz_tag 分库分表就行。

表结构设计:

>+-------------+--------------+------+-----+-------------------+-----------------------------+| 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­snowflake 方案

Leafsnowflake 是在雪花算法上改进来的,引用官网技术博客介绍:

Leaf-snowflake 方案完全沿用 Snowflake 方案的 bit 位设计,即是“1+41+10+12”的方式组装 ID 号。对于 workerID 的分配,当服务集群数量较小的情况下,完全可以手动配置。Leaf 服务规模较大,手动配置成本太高。所以使用 Zookeeper 持久顺序节点的特性自动对 Snowflake 节点配置 wokerID。

Leaf-snowflake 是按照下面几个步骤启动的:

  • 启动 Leaf-snowflake 服务,连接 Zookeeper,在 leaf_forever 父节点下检查自己是否已经注册过(是否有该顺序子节点);

  • 如果有注册过直接取回自己的 workerID(Zookeeper 顺序节点生成的int类型ID号),启动服务;

  • 如果没有注册过,就在该父节点下面创建一个持久顺序节点。创建成功后取回顺序号当做自己的 workerID 号,启动服务。

这种方案解决了前面提到的雪花算法的缺陷。官网没解释,不过 Leaf­snowflake 对其进行改进,官网的流程图。

详细介绍请看官网:https://tech.meituan.com/2017/04/21/mt-leaf.html

3.8 滴滴 Tinyid

Tinyid 是用 Java 开发的一款分布式 ID 生成系统,基于数据库号段算法实现。Tinyid 扩展了 leaf-segment 算法,支持了多数据库和 tinyid-client。

Tinyid 也是基于号段算法实现,系统实现图如下:

  • 优点:方便集成,有成熟的方案和实现;

  • 缺点:依赖 DB 稳定性,需要采用集群主从备份的方式提高 DB 可用性。

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

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

相关文章

创业可以做什么项目,六个轻资产创业项目推荐

​2022年已经接近尾声了,你有为下一年做好计划嘛,今年并不是平静的一年,口罩总是反反复复的出现,很多人萌生了创业的想法,那有没有不怕口罩的创业项目呢?虽然创业并不容易,但当你有了正确的方向…

【优化充电】粒子群算法电动汽车充电动态优化策略【含Matlab源码 2163期】

⛄一、粒子群算法电动汽车充电优化 1 电动汽车充电负荷估算 电动汽车的充电负荷主要与电动汽车起始充电时刻和充电时长相关,而起始充电时刻是由电动汽车用户的到家时间决定的,充电时长主要与电动汽车的行驶里程和充电倍率相关。 目前电动汽车还没有大规模运营, 只能通过统计燃…

笔试强训day1

一、选择题 第一题&#xff1a; 题解&#xff1a;y123返回值为123&#xff0c;是非零数&#xff0c;所以y123总是正确&#xff0c;因此循环条件由x<4控制&#xff0c;答案 为C 第二题&#xff1a; %5表示输出总共占据了五列&#xff0c;%.3表示只取字符串左边三个字符并且…

傻白入门芯片设计,wafer/die/chip/cell(一)

1.wafer&#xff1a; 晶圆&#xff0c;指一整个圆形的晶圆硅片。如果问及CPU的原料是什么&#xff0c;大家都会轻而易举的给出答案—是硅。这是不假&#xff0c;但硅又来自哪里呢&#xff1f;其实就是那些最不起眼的沙子。不过不是随便抓一把沙子就可以做原料的&#xff0c;一定…

HTTP 协议

1 HTTP 协议的介绍 HTTP&#xff08;Hyper Transfer Protocol&#xff09;&#xff1a;超文本传输协议HTTP 协议是基于 TCP/IP 协议的超文本&#xff1a;比普通文本更加强大传输协议&#xff1a;客户端和服务器的通信规则&#xff08;握手规则&#xff09; 注意&#xff1a; J…

【计算机毕业设计】21.二手拍卖系统maven源码

一、系统截图&#xff08;需要演示视频可以私聊&#xff09; 引言 近年来&#xff0c;电子商务发展的愈趋成熟使得人们的消费方式以及消费观念发生巨大改变&#xff0c;网上竞拍的拍卖模式随之发展起来。大学拍卖网旨在为湘大学生提供一个线上拍卖的交易平台。平台展示的商品大…

STM32CubeMX时钟树(72MHZ主频配置)

目录 一些基础概念 时钟树配置图 第一步 第二步 这里我只是配置常用的72MHZ主频&#xff0c;很多时候新手都在时钟树这里被劝退了。其实不知道没关系&#xff0c;我用STM32这么久了&#xff0c;也只知道大概。我们绝大多数时候不需要配置这个时钟&#xff0c;记住72MHZ主频…

LeetCode-795-区间子数组个数

1、双指针 根据题意&#xff0c;我们可以将数组中的数分为这三类&#xff1a;1、小于leftleftleft的数&#xff1b;2、大于等于leftleftleft且小于等于rightrightright的数&#xff1b;3、大于rightrightright的数。因此若我们使用双指针来维护区间[last2,last1][last2,last1]…

Nacos与Eureka中的高性能读写并发架构设计

Nacos、Eureka都是微服务领域内熟知、常用的注册中心组件。只不过呢&#xff0c;Nacos还多了个功能身份就是配置中心。从目前流行与随着Spring Cloud Alibaba发展来看&#xff0c;Nacos使用得更加多&#xff0c;也是趋势所在。 注册中心原理 注册中心原理其实很简单&#xff…

【18】Java常见的面试题汇总(Spring/Spring MVC)

目录 1. 为什么要使用 spring&#xff1f; 2. 解释一下什么是 aop&#xff1f; 3. 解释一下什么是 ioc&#xff1f; 4. spring 有哪些主要模块&#xff1f; 5. spring 常用的注入方式有哪些&#xff1f; 6. spring 中的 bean 是线程安全的吗&#xff1f; 7. spring 支持…

【Java八股文总结】之外卖平台项目整理

文章目录一、项目介绍1.1 项目整体介绍1.2 主要模块介绍二、项目开发2.1 后台管理系统开发2.1.1 员工管理employee1、员工后台登录2、员工退出3、过滤器4、新增员工5、员工信息分页查询↑&#xff08;参加上面&#xff09;6、修改员工信息7、根据id查询员工信息&#xff0c;回显…

后台开发的学习日记

后台开发的学习日记 Java后台开发的日记&#xff1a;Push一下自己每天都要学习后台 后台开发学习日志-Day1后台开发的学习日记Day1: 路线及资料的汇总一、应该选择什么语言&#xff1f;二、学习路线的规划及资料的汇总整理Day1: 路线及资料的汇总 第一天主要是路线的准备及资料…

Pytorch学习笔记(二)官方60min入门教程之自动微分

目录 一.相关包及函数介绍 二.雅各比向量积 三.练习代码 一.相关包及函数介绍 autograd 包是 PyTorch 中所有神经网络的核心。首先让我们简要地介绍它&#xff0c;然后我们将会去训练我们的第一个神经网络。该 autograd 软件包为 Tensors 上的所有操作提供自动微分。它是一…

显卡天梯图2022年11月新版 显卡性能排行榜天梯图

1 RTX 3090Ti 2 RTX 3090 3 RX 6900 XT水冷版 我用的显卡就是活动时8折抢购的太划算了 http://www.adiannao.cn/dq 4 RTX 3080 Ti 5 RX 6900 XT 6 Titan RTX 7 RTX 3080 8 RX 6800 XT 9 RX 6800 10 RTX 3070 Ti

C++数据结构X篇_01_数据结构的基本概念

从本篇开始学习数据结构相关概念。 数据结构的基本概念1 数据结构的相关概念1.1 为什么要学习数据结构1.2 数据结构中的基本概念2 算法2.1 算法的概念2.2 算法和数据结构的区别2.3 算法特性2.4 算法效率的度量2.4.1 事后统计法2.4.2 事前分析估算2.4.3 大O表示法2.4.3.1采用大O…

从事先进计算的工程师对此都有什么感想?

电子计算机最初诞生于二十世纪&#xff0c;体积庞大的初代机型运算能力有限&#xff0c;随着计算技术的升级完善&#xff0c;现在多样小巧的计算机及手机的计算能力呈指数级增长&#xff0c;更是成为人们生活密不可分的综合性助手。 先进计算是在计算的基础上诞生的全新概念&a…

Python3安装及基础语法

Python 官网&#xff1a;Welcome to Python.org Python安装&#xff1a;进入官网Download找到对应版本安装包&#xff0c;下载后双击安装&#xff0c;一直下一步即可&#xff1b;注意&#xff1a;安装最后一步勾选&#xff08;Add Python to PATH&#xff09;&#xff0c;默认…

用nginx作反向代理时,请求头中含波浪线无法转发请求的解决方法

请求头如下 POST /CDGServer3/s/rs/uni HTTP/1.1 Content-Type: text/html; charsetUTF-8 method~name: upgradePatchService user~userId: admin.local user~clientId: 343834353230344334424431 user~SessionID: 0 data~packageNo: 618 data~packageState: 1 User-Agent: Ra…

Android -- 每日一问:怎么理解 Activity 的生命周期?

典型回答 如果一个 Activity 在用户可见时才处理某个广播&#xff0c;不可见时注销掉&#xff0c;那么应该在哪两个生命周期的回调方法去注册和注销 BroadcastReceiver 呢&#xff1f; Activity 的可见生命周期发生在 onStart调用与 onStop调用之间。在这段时间&#xff0c;用户…

nginx(六十四)proxy模块(五)接收上游响应

一 接收上游的响应 前提&#xff1a; nginx与上游建立连接,把nginx生成的请求(line、header、body)信息发送给上游补充&#xff1a; 上游解析处理完之后,会发送响应​核心&#xff1a; nginx如何接收、解析、处理上游响应行、响应头、响应体 下载大文件失败 &#xff08;…