面试官:如何在分布式场景下生成全局唯一 ID?
在分布式系统中,有一些场景需要使用全局唯一 ID ,可以和业务场景有关,比如支付流水号,也可以和业务场景无关,比如分库分表后需要有一个全局唯一 ID,或者用作事务版本号、分布式链路追踪等等,好的全局唯一 ID 需要具备这些特点:
- 全局唯一:这是最基本的要求,不能重复;
- 递增:有些特殊场景是必须递增的,比如事务版本号,后面生成的 ID 一定要大于前面的 ID ;有些场景递增比不递增要好,因为递增有利于数据库索引的性能;
- 高可用:如果是生成唯一 ID 的系统或服务,那么一定会有大量的调用,那么保证其高可用就非常关键了;
- 信息安全:如果 ID 是连续的,那么很容易被恶意操作或泄密,比如订单号是连续的,那么很容易就被看出来一天的单量大概是多少;
- 另外考虑到存储压力,ID 当然是越短越好。
那么分布式场景下有哪些生成唯一 ID 的方案呢?
利用数据库生成
先说最容易理解的方案,利用数据库的自增长序列生成:数据库生成唯一主键,并通过服务提供给其他系统;如果是小型系统,数据总量和并发量都不是很大的情况下,这种方案足够支撑。
如果每次生成一个 ID 可能会对数据库有压力,可以考虑一次性生成 N 个 ID 放入缓存中,如果缓存中的 ID 被取光,再通过数据库生成下一批 ID 。
- 优点: 理解起来最容易,实现起来也最简单。
- 缺点: 也非常明显了,每种数据库的实现不同,如果数据库需要迁移的话比较麻烦;最大的问题是性能问题,并发量到一定级别的时候这个方法估计会很难满足性能需求;另外通过数据库自增生成的 ID 携带的信息太少,只能起到一个标识的作用,同时自增 ID 也是连续的。
利用其他组件/软件/中间件生成
利用 Redis / MongoDB / zookeeper 生成:Redis 利用 incr 和 increby ;MongoDB 的 ObjectId;zk 通过 znode 数据版本;都可以生成全局的唯一标识码。
我们用 MongoDB 的 ObjectId 来举例:
{"_id":ObjectId("5d47ca7528021724ac19f745")}
MongoDB 的 ObjectId 共占 12 个字节,其中:
- 3.2 之前的版本(包括 3.2): 4 字节时间戳 + 3 字节机器标识符 + 2 字节进程 ID + 3字节随机计数器
- 3.2 之后版本: 4 字节时间戳 + 5 字节随机值 + 3 字节递增计数器
不管是老版本还是新版本,MongoDB 的 ObjectId 至少都可以保证集群内的唯一,我们可以搭建一个全局唯一 ID 生成的服务,利用 MongoDB 生成 ObjectId 并对外提供服务(MongoDB 的各语言驱动都实现了 ObjectId 的生成算法)。
- 优点: 性能高于数据库;可以使用集群部署;ID 内自带一些含义,比如时间戳;
- 缺点: 和数据库一样,需要引入对应的组件/软件,增加了系统的复杂度;最关键的是,这两种方案都意味着生成全局唯一 ID 的系统(服务),会成为一个单点,在软件架构中,单独就意味着风险;如果这个服务出现问题,那么所有依赖于这个服务的系统都会崩溃掉。
UUID
这个是分布式架构中,生成唯一标识码最常用的算法。为了保证 UUID 的唯一性,生成因素包括了MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素;UUID 有多个版本,每个版本的算法不同,应用范围也不同:
- Version 1: 基于时间的 UUID,是通过时间戳 + 随机数 + MAC地址得到;如果应用直接局域网内使用,可以使用 IP 地址替代 MAC 地址;高度唯一(MAC 地址泄漏,也是一个安全问题)。
- Version 2: DCE 安全的 UUID,把 Version 1 中的时间戳前 4 位置换为 POSIX 的 UID 或 GID ;高度唯一。
- Version 3: 基于名字的 UUID(MD5),通过计算名字和名字空间的 MD5 散列值得到;一定范围内唯一。
- Version 4: 随机 UUID,根据随机数或伪随机数生成 UUID;有一定概率重复。
- Version 5: 基于名字的UUID(SHA1),和 Version 3 类似,只是散列值计算使用SHA1算法;一定范围内唯一。
- 优点: 本地生成,没有网络消耗,不需要第三方组件(也就没有单点的风险),生成比较简单,性能好。
- 缺点: 长度长,不利于存储,并且没有排序,相对来说还会影响性能(比如 MySQL 的 InnoDB 引擎,如果 UUID 作为数据库主键,其无序性会导致数据位置频繁变动)。
Snowflake
如果希望 ID 可以本地生成,但是又不要和 UUID 那样无序,可以考虑使用 Snowflake 算法(Twitter开源)。
SnowFlake 算法生成 ID 是一个 64 bit 的整数,包括:
- 1 bit : 不使用,固定是 0 ;
- 41 bit : 时间戳(毫秒),数值范围是:0 至 2的41次方 - 1 ;转换成年的话,大约是 69 年;
- 10 bit : 机器 ID ;5 位机房 ID + 5 位机器 ID ;(服务集群数量比较小的时候,可以手动配置,服务规模大的话,可以采用第三方组件进行自动配置,比如美团的 Leaf-snowflake,就是通过 Zookeeper 的持久顺序节点做为机器 ID)
- 12 bit : 序列号,用来记录同一个毫秒内生成的不同 ID 。
在Java中,SnowFlake 算法生成的 ID 正好可以用 long 来进行存储。
- 优点: 本地生成,没有网络消耗,不需要第三方组件(也就没有单点的风险),一定范围内唯一(基本可以满足大部分场景),性能好,按时间戳递增(趋势递增);
- 缺点: 依赖于机器时钟,同一台机器如果把时间回拨,生成的 ID 就会有重复的风险。
image
此外,还有很多优秀的互联网公司也提供了唯一 ID 生成的方案或框架,比如美团开源的 Leaf ,百度开源的 UidGenerator 等等。
面试官:如何在分布式场景下生成全局唯一 ID?的更多相关文章
- 如何在高并发分布式系统中生成全局唯一Id
月整理出来,有兴趣的园友可以关注下我的博客. 分享原由,最近公司用到,并且在找最合适的方案,希望大家多参与讨论和提出新方案.我和我的小伙伴们也讨论了这个主题,我受益匪浅啊…… 博文示例: 1. ...
- 如何在高并发分布式系统中生成全局唯一Id(转)
http://www.cnblogs.com/heyuquan/p/global-guid-identity-maxId.html 又一个多月没冒泡了,其实最近学了些东西,但是没有安排时间整理成博文, ...
- (转)如何在高并发分布式系统中生成全局唯一Id
又一个多月没冒泡了,其实最近学了些东西,但是没有安排时间整理成博文,后续再奉上.最近还写了一个发邮件的组件以及性能测试请看 <NET开发邮件发送功能的全面教程(含邮件组件源码)> ,还弄了 ...
- 常见的生成全局唯一id有哪些?他们各有什么优缺点?
分布式系统中全局唯一id是我们经常用到的,生成全局id方法由很多,我们选择的时候也比较纠结.每种方式都有各自的使用场景,如果我们熟悉各种方式及优缺点,使用的时候才会更方便.下面我们就一起来看一下常见的 ...
- 生成全局唯一ID
在实际业务处理中,有时需要生成全局唯一ID来区别同类型的不同事物,介绍一下几种方式及其C++实现 //获取全局唯一ID //server_id为服务的id,因当同一个服务部署在多个服务器上时,需要区别 ...
- 高并发分布式环境中获取全局唯一ID[分布式数据库全局唯一主键生成]
需求说明 在过去单机系统中,生成唯一ID比较简单,可以使用MySQL的自增主键或者Oracle中的sequence, 在现在的大型高并发分布式系统中,以上策略就会有问题了,因为不同的数据库会部署到不同 ...
- 游戏服务器生成全局唯一ID的几种方法
在服务器系统开发时,为了适应数据大并发的请求,我们往往需要对数据进行异步存储,特别是在做分布式系统时,这个时候就不能等待插入数据库返回了取自动id了,而是需要在插入数据库之前生成一个全局的唯一id,使 ...
- 雪花算法生成全局唯一ID
系统中某些场景少不了全局唯一ID的使用,来保证数据的唯一性.除了通过数据库自带的自增id来保证 id 的唯一性,通常为了保证的数据的可移植性会选择通过程序生成全局唯一 id.百度了不少php相关的生成 ...
- 高并发分布式系统中生成全局唯一Id汇总
数据在分片时,典型的是分库分表,就有一个全局ID生成的问题.单纯的生成全局ID并不是什么难题,但是生成的ID通常要满足分片的一些要求: 1 不能有单点故障. 2 以时间为序,或者ID里包含时间 ...
随机推荐
- 详解Tomcat核心配置、http协议
Tomcat服务器 Tomcat配置与部署(IDEA) https://www.cnblogs.com/gonghr/p/14731266.html Tomcat手工创建和打包第一个Web工程 在ap ...
- File & Directory
新开一节IO(Input/Output)的用法. 这节主要讲一下操作文件和文件目录的两个静态类:File 和 Directory. 在进入正题之前,先理解一下相对路径和绝对路径这两个概念: 绝对路径, ...
- MFC对话框不能使用菜单更新宏ON_UPDATE_COMMAND_UI
菜单更新宏的原理 更新处理宏的工作原理是基于框架窗口类的.MFC中对话框菜单更新宏的原理是:当我们使用从CFrameWnd框架窗口类中派生的类创建窗口时,当我们单击菜单且菜单还未弹出前会产生WM_IN ...
- 改善c++程序的150个建议(读后总结)-------19-26
19. 明白在c++中如何使用c c++可以兼容c的绝大部分代码,但是还是有一部分不能兼容. c语言的编译器在调用函数时会把函数翻译成 : "_函数名",例如: int nasa( ...
- String相关介绍
String 字符串是常量,创建后不可改变. 字符串字面值存储在字符串池中,可以共享. String s1 = "Runoob"; // String 直接创建 String s2 ...
- [Qt] 事件机制(三)
在主窗口Widget中增加几个小功能 1.点击左键,在左上角label中显示"haha",点击右键,显示"lala" 在widget.h中添加: 1 #incl ...
- 【转载】Linux查看PCIe版本及速率【方法】PCIE的X4X8X16 查看 数量 怎么看
Linux查看PCIe版本及速率 PCIE有四种不同的规格,通过下图来了解下PCIE的其中2种规格 查看主板上的PCI插槽 # dmidecode | grep --color "P ...
- Rsync忽略文件夹或目录
使用Rsync同步的时候往往会要求对某个文件夹或者文件进行忽略,客户端可以使用--exclude参数来实现对,目录或者文件的忽略 rsync -rltvz --port=873 --exclude & ...
- 76-Java安装Eclipse并创建第一个HelloWorld.md
76-Java安装Eclipse并创建第一个HelloWorld.md 首先确定已经安装Java系统环境,若未安装,请参考博客Java环境windows搭建 访问Eclipse官网 下载完成直接发送快 ...
- 云计算OpenStack环境搭建(4)
准备工作: 准备3台机器,确保yum源是可用的,分别为控制节点(192.168.11.3).计算节点(192.168.11.4)和存储节点(192.168.11.5) 控制节点:OpenStack日常 ...