架构师究竟要不要懂细节?分布式 ID 生成的六种方法

开发 架构
几乎所有的业务系统,都有生成一个唯一记录标识的需求,例如:消息ID,订单ID,帖子ID,那如何高效生成趋势有序的全局唯一ID呢?

几乎所有的业务系统,都有生成一个唯一记录标识的需求,例如:消息ID,订单ID,帖子ID...

这个ID,在数据库中往往用作主键,且有排序与分页的查询需求。这也是分布式ID生成算法的两大核心需求:

  • 全局唯一;
  • 趋势递增;

如何高效生成趋势有序的全局唯一ID,是每一个工程师都会遇到的问题。

方法一:数据库auto-inc-id法

借助数据库的auto_increment来生成全局唯一递增ID。

优点:

  • 简单,使用数据库已有的功能;
  • 能够保证唯一性;
  • 能够保证递增性;
  • 步长固定;

不足:

  • 可用性难以保证,需要依赖数据库的高可用;
  • 扩展性差,性能有上限,数据库主库的写性能决定ID的生成性能上限;

改进方法:

  • 冗余主库,避免写入单点;
  • 数据水平切分,保证各主库生成的ID不重复;

改进后,数据库的写压力依然很大,每次生成ID都要访问数据库。为了解决这个问题,引出了第二个常见的方案。

方法二:批量ID生成服务

数据库写压力大,是因为每次生成ID都访问了数据库,可以使用批量的方式降低数据库写压力。

ID生成服务假设每次批量拉取6个ID,服务访问数据库,将当前ID的最大值修改为5,这样应用访问ID生成服务索要ID,ID生成服务不需要每次访问数据库,就能依次派发0,1,2,3,4,5这些ID了。

当ID发完后,再将ID的最大值修改为11,就能再次派发6,7,8,9,10,11这些ID了,于是数据库的压力就降低到原来的1/6。

优点:

  • 保证了ID生成的绝对递增有序;
  • 大大的降低了数据库的压力,ID生成可以做到每秒生成几万几十万个;

同时,服务也可以做集群化,只是稍微要注意数据一致性问题,具体CAS优化方案在《巧用CAS实现分布式ID生成器!》中有详细介绍,不再展开。

方法三:uuid/guid法

不管是通过数据库,还是通过服务来生成ID,业务方都需要进行一次远程调用,比较耗时。有没有一种本地生成ID的方法,即高性能,又时延低呢?

uuid是一种常见的方案:

string ID =GenUUID();
  • 1.

优点:

  • 本地生成ID,不需要进行远程调用,时延低;
  • 扩展性好,基本可以认为没有性能上限;

不足:

  • 无法保证趋势递增
  • uuid过长,往往用字符串表示,作为主键建立索引查询效率低,常见优化方案为“转化为两个uint64整数存储”。

方法四:取当前毫秒数

uuid是一个本地算法,生成性能高,但无法保证趋势递增,且作为字符串ID检索效率低,有没有一种能保证递增的本地算法呢?

取当前毫秒数是一种常见方案:

uint64 ID = GenTimeMS();
  • 1.

优点:

  • 本地生成ID,不需要进行远程调用,时延低;
  • 生成的ID趋势递增;
  • 生成的ID是整数,建立索引后查询效率高;

缺点:如果并发量超过1000,会生成重复的ID。

当然,使用微秒可以降低冲突概率,但每秒最多只能生成1000000个ID,再多的话就一定会冲突了,所以使用微秒并不从根本上解决问题。

方法五:类snowflake算法

snowflake是twitter开源的分布式ID生成算法,其核心思想为,一个long型的ID:

  • 41bit作为毫秒数;
  • 10bit作为机器(服务)编号;
  • 12bit作为毫秒内序列号;

算法单机每秒内理论上最多可以生成1000*(2^12),也就是400W的ID,1024台机器(服务)每秒能生活40Y的ID,完全能满足业务的需求。

借鉴snowflake的思想,结合公司的业务逻辑和并发量,可以实现自己的分布式ID生成算法。

举例,假设某公司ID生成的需求如下:

  • 单机高峰并发量小于1W,预计未来10年单机高峰并发量小于10W;
  • 有2个机房,预计未来10年机房数量小于4个;
  • 每个机房机器数小于100台;
  • 目前有5个业务线有ID生成需求,预计未来业务线数量小于10个;

我们应该怎么来设计公司独特的ID生成算法呢?

其一,毫秒位数考虑。

假设系统至少运行10年,那至少需要10年*365天*24小时*3600秒*1000毫秒=320*10^9,差不多预留39bit给毫秒数。

其二,1毫秒内序列号考虑。

每秒的单机高峰并发量小于10W,即平均每毫秒的单机高峰并发量小于100,差不多预留7bit给每毫秒内序列号。

其三,机房数少于4个,预留2bit给机房标识。

其四,每个机房机器小于100台,预留7bit给每个机房内的服务器标识。

其五,业务线小于10个,预留4bit给业务线标识。

这样设计的64bit标识,可以保证:

  • 每个业务线、每个机房、每个机器生成的ID都是不同的;
  • 同一个机器,每个毫秒内生成的ID都是不同的;
  • 同一个机器,同一个毫秒内,以序列号区区分保证生成的ID是不同的;
  • 将毫秒数放在最高位,保证生成的ID是趋势递增的;

以上,希望大家有收获。

知其然,知其所以然。思路比结论更重要。

扩展阅读:https://github.com/twitter-archive/snowflake

责任编辑:赵宁宁 来源: 架构师之路
相关推荐

2018-01-24 07:58:47

框架组件技术栈开源

2020-11-17 09:17:58

框架组件基础服务

2015-07-15 10:25:44

SDN物理交换机

2024-09-30 05:38:48

2019-10-23 20:19:26

Python 开发编程语言

2021-11-24 22:39:03

手机系统功能

2025-01-03 08:48:20

列表推导式Python编程

2011-02-24 10:56:34

人才

2010-10-08 11:13:22

MySQL修改密码

2016-11-29 09:12:21

数据库分布式ID

2023-09-06 08:00:00

ChatGPT数据分析

2025-01-02 08:21:32

2020-03-23 07:30:57

数据库运维架构

2017-10-30 08:52:27

vSAN架构RAID

2024-04-18 09:24:32

分布式ID分库分表

2017-07-01 16:02:39

分布式ID生成器

2021-12-06 06:58:50

List重复数据

2021-06-25 10:35:58

分布式代码Java

2022-03-14 10:12:22

架构网关技术
点赞
收藏

51CTO技术栈公众号