在分布式系统存在多个 Shard 的场景中, 同时在各个 Shard 插入数据时, 怎么给这些数据生成全局的 unique ID? 在单机系统中 (例如一个 MySQL 实例), unique ID 的生成是非常简单的, 直接利用 MySQL 自带的自增 ID 功能就可以实现.

但在一个存在多个 Shards 的分布式系统 (例如多个 MySQL 实例组成一个集群, 在这个集群中插入数据), 这个问题会变得复杂, 所生成的全局的 unique ID 要满足以下需求:

  • 唯一性,保证生成的 ID 全局唯一
  • 今后数据在多个 Shards 之间迁移不会受到 ID 生成方式的限制
  • 有序性,生成的 ID 中最好能带上时间信息, 例如 ID 的前 k 位是 Timestamp, 这样能够直接通过对 ID 的前 k 位的排序来对数据按时间排序
  • 生成的 ID 最好不大于 64 bits
  • 可用性,生成 ID 的速度有要求. 例如, 在一个高吞吐量的场景中, 需要每秒生成几万个 ID (Twitter 最新的峰值到达了 143,199 Tweets/s, 也就是 10万+/秒)
  • 整个服务最好没有单点

在要满足前面 6 点要求的场景中, 怎么来生成全局 unique ID 呢?

数据库自增ID

数据库单表,使用 auto increment 来生成唯一全局递增ID。

优势是无需额外附加操作,定长增长,单表结构中唯一性,劣势是高并发下性能不佳,生产的上限是数据库服务器单机的上限,水平扩展困难,分布式数据库下,无法保证唯一性。

UUID

如果没有上面这些限制, 问题会相对简单, 例如: 直接利用 UUID.randomUUID() 接口来生成 unique ID (http://www.ietf.org/rfc/rfc4122.txt). 但这个方案生成的 ID 有 128 bits, 另外, 生成的 ID 中也没有带 Timestamp 一般编程语言中自带 UUID 实现, Java 中 UUID.randomUUID().toString() 产生的ID 不依赖数据库实现。

优势是,本地生成ID,无需远程调用,全局唯一,水平扩展能力好。劣势是,ID 有 128 bits 长,占空间大,生成字符串类型,索引效率低,生成的 ID 中没有带 Timestamp 无法保证时间递增。

Flickr 全局主键

Flickr 的做法1 是使用 MySQL 的自增ID, 和 replace into 语法。但他这个方案 ID 中没有带 Timestamp, 生成的 ID 不能按时间排序

创建64位自增ID,首先创建表

CREATE TABLE `Tickets64` (
`id` bigint(20) unsigned NOT NULL auto_increment,
`stub` char(1) NOT NULL default '',
PRIMARY KEY (`id`),
UNIQUE KEY `stub` (`stub`)
) ENGINE=MyISAM

SELECT * from Tickets64 假设表中有一行

+-------------------+------+
| id | stub |
+-------------------+------+
| 72157623227190423 | a |
+-------------------+------+

那么如果需要产生一个新的全局 64 bits 的ID,只要执行 SQL:

REPLACE INTO Tickets64 (stub) VALUES ('a');
SELECT LAST_INSERT_ID();

SQL 返回的ID就是要产生的全局唯一ID。使用 REPLACE INTO 代替 INSERT INTO 的好处是避免表行数太多。 stub 要设为唯一索引。

Flickr 内部运行两台 ticket servers,通过两台机器做主备和负载均衡。

TicketServer1:
auto-increment-increment = 2
auto-increment-offset = 1 TicketServer2:

auto-increment-increment = 2

auto-increment-offset = 2

Twitter Snowflake

Twitter 利用 Zookeeper 实现一个全局的 ID 生成服务 Snowflake: https://github.com/twitter/snowflake

Snowflake 生成的 unique ID 的组成 (由高位到低位):

  • 41 bits: Timestamp 毫秒级
  • 10 bits: 节点 ID datacenter ID 5 bits + worker ID 5 bits
  • 12 bits: sequence number

一共 63 bits ,其中最高位是 0

unique ID 生成过程:

  • 41 bits 的 Timestamp: 每次要生成一个新 ID 的时候, 都会获取一下当前的 Timestamp, 然后分两种情况生成 sequence number:

    <div class="highlighter-rouge"><div class="highlight"><pre class="highlight prettyprint prettyprinted" style=""><code><span class="pln">  </span><span class="pun">-</span><span class="pln"> </span><span class="pun">如果当前的</span><span class="pln"> </span><span class="typ">Timestamp</span><span class="pln"> </span><span class="pun">和前一个已生成</span><span class="pln"> ID </span><span class="pun">的</span><span class="pln"> </span><span class="typ">Timestamp</span><span class="pln"> </span><span class="pun">相同</span><span class="pln"> </span><span class="pun">(在同一毫秒中),</span><span class="pln"> </span><span class="pun">就用前一个</span><span class="pln"> ID </span><span class="pun">的</span><span class="pln"> sequence number </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">作为新的</span><span class="pln"> sequence number </span><span class="pun">(</span><span class="lit">12</span><span class="pln"> bits</span><span class="pun">);</span><span class="pln"> </span><span class="pun">如果本毫秒内的所有</span><span class="pln"> ID </span><span class="pun">用完,</span><span class="pln"> </span><span class="pun">等到下一毫秒继续</span><span class="pln"> </span><span class="pun">(**这个等待过程中,</span><span class="pln"> </span><span class="pun">不能分配出新的</span><span class="pln"> ID</span><span class="pun">**)</span><span class="pln">

    - 如果当前的 Timestamp 比前一个 ID 的 Timestamp 大, 随机生成一个初始 sequence number (12 bits) 作为本毫秒内的第一个 sequence number

分布式系统中唯一 ID 的生成方法的更多相关文章

  1. 分布式系统中Unique ID 的生成方法

    http://darktea.github.io/notes/2013/12/08/Unique-ID 本文主要介绍在一个分布式系统中, 怎么样生成全局唯一的 ID 一, 问题描述 在分布式系统存在多 ...

  2. 分布式系统中 Unique ID 的生成方法

    http://darktea.github.io/notes/2013/12/08/Unique-ID Snowflake 生成的 unique ID 的组成 (由高位到低位): 41 bits: T ...

  3. 分布式系统全局唯一ID的生成

    分布式系统全局唯一ID的生成 一 .什么是分布式系统唯一ID ​ 在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识. ​ 如在金融.电商.支付.等产品的系统中,数据日渐增长,对数据分库分表后 ...

  4. 分表分库之二:唯一ID的生成方法

    一.为什么要全局唯一? 我们在对数据库集群作扩容时,为了保证负载的平衡,需要在不同的Shard之间进行数据的移动, 如果主键不唯一,我们就没办法这样随意的移动数据.起初,我们考虑采用组合主键来解决这个 ...

  5. 分布式系统中我们会对一些数据量大的业务进行分拆,分布式系统中唯一主键ID的生成问题

    分布式全局唯一ID生成策略​ https://www.cnblogs.com/vandusty/p/11462585.html 一.背景 分布式系统中我们会对一些数据量大的业务进行分拆,如:用户表,订 ...

  6. 分布式系统唯一ID的生成方案讨论

    在分布式系统下唯一id问题,就是id咋生成?比如分表分库,因为要是一个表分成多个表之后,每个表的id都是从1开始累加自增长,那是不对的.举个例子,一个表拆分为了2张表,每个表的id都从1开始累加,这个 ...

  7. 分布式Unique ID的生成方法

    分布式Unique ID的生成方法 分布式的Unique ID的用途如此广泛,从业务对象Id到日志的TraceId,本文总结了林林总总的各种生成算法. 1. 发号器 我接触的最早的Unique ID, ...

  8. [转帖]分布式Unique ID的生成方法一览

    分布式Unique ID的生成方法一览 http://www.importnew.com/22211.html 分布式的Unique ID的用途如此广泛,从业务对象Id到日志的TraceId,本文总结 ...

  9. java 生成20位唯一ID,生成不会重复的20位数字----https://blog.csdn.net/weixin_36751895/article/details/70331781

    java 生成20位唯一ID,生成不会重复的20位数字----https://blog.csdn.net/weixin_36751895/article/details/70331781

随机推荐

  1. mysql NULL函数 语法

    mysql NULL函数 语法 作用:如果表中的某个列是可选的,那么我们可以在不向该列添加值的情况下插入新记录或更新已有的记录.这意味着该字段将以 NULL 值保存. 说明:NULL 值的处理方式与其 ...

  2. select服务器端模型封装——回调方式快速建立服务端

    #pragma once #ifndef WINSOCK2_H #define _WINSOCK_DEPRECATED_NO_WARNINGS #include<WinSock2.h> # ...

  3. tomcat8 的优化

    1.下载tomcat8 2.配置 修改tomcat_user.xml,配置管理用户(设置角色,和用户密码) <role rolename="manager"/> < ...

  4. ElasticSearch的介绍

    1.ELK 1.1 集中式日志系统 日志,对于任何系统来说都是及其重要的组成部分.在计算机系统里面,更是如此.但是由于现在的计算机系统大多比较复杂,很多系统都不是在一个地方,甚至都是跨国界的:即使是在 ...

  5. [CSP-S模拟测试]:椎(线段树维护区间最值和单调栈)

    题目描述 虽不能至,心向往之. $Treap=Tree+Heap$ 椎$=$树$+$堆 小$\pi$学习了计算机科学中的数据结构$Treap$. 小$\pi$知道$Treap$指的是一种树. 小$\p ...

  6. [CSP-S模拟测试]:123567(莫比乌斯函数+杜教筛+数论分块)

    题目传送门(内部题92) 输入格式 一个整数$n$. 输出格式 一个答案$ans$. 样例 样例输入: 样例输出: 数据范围与提示 对于$20\%$的数据,$n\leqslant 10^6$. 对于$ ...

  7. python相关遗漏知识点补充

    python中的相关帮助命令 假设s是一个字符串, 那么dir(s)可以列出字符串对象的所有属性(方法也是函数属性),其中有下划线的部分与类重 载有关,用来表示python实现细节,没有下划线的属性是 ...

  8. javascript注

    1.浮点数: e表示法(科学计数法-10的指数次幂): let floatNum = 3.12e2; //等于312 浮点数的最高精度是17位小数. 浮点数计算精度远不如整数,0.15加0.15的和是 ...

  9. (转)js中then方法说明

    javascript中的then方法说明: then()方法是异步执行.  意思是:就是当.then()前的方法执行完后再执行then()内部的程序,这样就避免了,数据没获取到等的问题.  语法:pr ...

  10. htonl(),htons(),ntohl(),ntons()--大小端模式转换函数

    不同机器内部对变量的字节存储顺序不同,有的采用大端模式(big-endian),有的采用小端模式(little-endian). 大端模式是指高字节数据存放在低地址处,低字节数据放在高地址处. 小端模 ...