分布式ID生成器的剖析与设计

本文涉及的产品
云数据库 MongoDB,通用型 2核4GB
简介: ID是身份标识,你所涉及的每类业务都会有一个,身份证, 手机号, QQ号。那么问题来了,如何设计一个算法,能快速生成ID又能有效地避免冲突。 往小了说,在存储领域每一行数据都会有一个ID,关系型数据库有 auto increment, 非关系型数据库,如mongodb有自己的objectID 算法。 对于各种ID我们可以简化为2类: 1.去中心化,统一长度,规则占坑类, mongodb属于这

ID是身份标识,你所涉及的每类业务都会有一个,身份证, 手机号, QQ号。那么问题来了,如何设计一个算法,能快速生成ID又能有效地避免冲突。
往小了说,在存储领域每一行数据都会有一个ID,关系型数据库有 auto increment, 非关系型数据库,如mongodb有自己的objectID 算法。
对于各种ID我们可以简化为2类:
1.去中心化,统一长度,规则占坑类, mongodb属于这一类, guid 属于这类类。
2.中心化,ID自增,auto increment属于这一类。

mongodb id

mongodb的ID规则

长度(byte) 含义
4 unix epoch到当前的秒数
3 机器标识
2 进程标识
3 扩展计数

机器标识可以防止不同机器出现相同ID, 进程标识则可以在同一台机器上防止出现冲突。
对于这种设计, 我想说 good! 无论是单机还是分布式都可以很有效地解决冲突的问题。
image.png

即使同一时刻有很多请求,不同机器也不会产生相同的ID,达到了去中心化的ID生成能力。

类似的设计还有twitter snowflake:

长度(bit) 含义
41 unix epoch到当前的毫秒数
10 节点ID
12 sequence

也做到了不同节点去重,不同时间有序,只是把节点ID的制定交给了使用者。

mysql auto seq
相信大部分研发同学都使用过mysql,auto increment可以有效地解决ID生成。

CREATE TABLE animals (
     id MEDIUMINT NOT NULL AUTO_INCREMENT,
     name CHAR(30) NOT NULL,
     PRIMARY KEY (id)
);

id会从1开始自增,所以单台Server的情况下是不会有冲突的可能的。
image.png

这样的设计一度非常常见于各种前端技术,用过LAMP技术栈的同学应该记得,目前仍被mysql, redis等广泛使用。

引申

第一种方案是于空间时间相关的,类似于身份证
image.png

把坑给你空好,生成机制设计好,生成ID就变成了按机制生成数字填坑, 只要时间不停摆,人口不会短时间内爆增,这个ID就很难重复。

等于把冲突保障交给了系统。

第二种方案与时空无关,只要生成器还在,就可以一直生产数据,冲突由生成器保障。

问题抛出

系统不可靠

以mongodb为例

长度(byte) 含义 依赖
4 unix epoch到当前的秒数 系统时间
3 机器标识
2 进程标识
3 扩展计数

如果因为操作不当,人为把机器的当前时间修改成过去某个时间,其新生成ID的时间部分就有可能冲突。

image.png

举例来说,某一年的公历出现了润8月(不可能出现),则8月出生的小朋友就要身份证重号了,现终于知道为什么身份证不使用农历了吧,哟呵呵。

自增的瓶颈

自增形式的生成器很可能成为瓶颈,争对单点的问题业界已经有一些方案,比如分片自增

有3个节点 p1, p2, p3, 生成的ID规则:

p1: 1,4,7,10,13,16,19,22

p2: 2,5,8,11,14,17,20,23

p3:3,6,9,12,15,18,21,24

image.png

既使节点A崩溃了,只要有一个节点还在,系统就能工作。

但是不太好扩展,如果一开始只分配了3台机器,后面系统达到瓶颈了添加机器,就比较麻烦了。

ID的意义

在分布式系统中,ID往往有很多意义,比如散列,如何精确地的把按ID的查询散列到某台机器上去,因此在一些场景下ID需要包含机器信息,第一类去中心化ID生成器很好的解决了此类问题。

另外,有时候需要从ID知道系统的数据规模,自增的ID能较好的解决此类问题。

从安全角度上看,不同场景下ID应该有不同的表述,避免被外部猜测。比如微信号在第三方服务中是不能被直接使用的,而是一个64字节不具备查询意义的open id。

能不能有一个ID机器既能满足散列,又能反映数据规模呢,我想是可以的。

本来只想分享一下现行的生成规则,作为文章的附加,我简单设计了一个有效的ID生成器。

分布式ID的设计

设计方向

快速生成,不冲突。

既使系统时间被重置,也不会冲突。

能从最新的ID拿到当前的数据规模。

支持不同场景不同表述。

结构设计

长度(byte) 含义 意义
4 unix epoch到当前的秒数 去中心化
3 机器标识
2 进程标识
3 扩展计数
8 统一分配全局ID 中心化

设计思想

设计上将中心化与去中心化结合,对外展示去中心化的ID, 对内展示全部,对统计展示中心化ID。
image.png

分配架构

image.png

全局ID统一分配器设计

作为中心化的瓶颈,必须由机器保障其功能可靠。
image.png

各个节点从全局ID分配器批量获取ID作为预分配资源,当节点剩余预留ID低于阈值时再次申请。

节点分配设计

image.png

void Pot::onRequest(long& conID, long& reqID){
    
    uint64_t  id = cache.incr();     //预留ID自增
    if(id <= 0){                     //预留ID不够
        cache.distribute([this, &conID, &reqID](uint64_t id, uint32_t err){                            //请求后端申请全局分配预留片,并设置好回调
                        this->response(conID, reqID, id);
                });                //向全局请求分本书
        return;
    }

    if(cache.shouldDistribute()){                //检查预留池是否达到申请阈值 
        cache.distribute();                        //向后端申请全局分配预留片
    }

    response(conID, reqID, id);       //返回
}

全局分配器工作原理

image.png

工作原理也是一样简单

void Distribute::onRequest(long& conID, long& reqID){
    uint64_t  minID, maxID, err;
    uint32_t flag = db.pre(minID, maxID, err);     //预分配ID片
    if(flag <= 0){                       //是否出错
        response(conID, reqID, err);    //返回
        return;
    }
 
    response(conID, reqID, minID, maxID);       //返回ID片
}

崩溃处理机制

单节点崩溃,其他节点仍能崩溃,这个比较简单。

全局分配器的崩溃处理需要在设计上保障,可以同时并存几个全局生成器,且并不缓存当前最大分配ID,该值由数据库来存储。
image.png

全局分配的请求是个低频处理器。

假设工作节点数为N, 系统每秒处理并发为Concurrent, 全局分配器的请求频次F,每次分配ID片长度为P, 全局分配器个数为M,则有下列公式:

F=Concurrent/(NPM)

假设每秒并发数为100万 (1秒产生100万个ID,1秒新增100万个用户,实际不可能),有5个工作节点,全局分配器个数为4, 每次申请ID片为10000。

F=1000,000 / (5 100004) =5.

100万每秒的分配也只有每秒几次的请求,而且P的值可以灵活修改,假设P=100,000(每片10万个ID),则每秒1个请求都没有了。

image.png

这种情况下对全局分配的安全只需要对数据主从热备就行了。

理论退化
将上面的规则重新审视一下:

字段 长度(byte) 备注
time 4 unix epoch到当前的秒数 去中心化
machine 3 机器标识
process 2 进程标识
count 3 扩展计数
center id 8 统一分配全局ID 中心化

如果只有前4个,去掉center 只退化成了mongodb的去中心化ID生成器。

如果去掉前四项,保留center id,则退化成了具备分布式功能的中心化ID生成器。

在一些简单的业务里,可以只需要在这2者中选一个就可以满足分布式业务的处理。

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。 &nbsp; 相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
目录
相关文章
|
4月前
|
缓存 算法 NoSQL
【分布式详解】一致性算法、全局唯一ID、分布式锁、分布式事务、 分布式缓存、分布式任务、分布式会话
分布式系统通过副本控制协议,使得从系统外部读取系统内部各个副本的数据在一定的约束条件下相同,称之为副本一致性(consistency)。副本一致性是针对分布式系统而言的,不是针对某一个副本而言。强一致性(strong consistency):任何时刻任何用户或节点都可以读到最近一次成功更新的副本数据。强一致性是程度最高的一致性要求,也是实践中最难以实现的一致性。单调一致性(monotonic consistency):任何时刻,任何用户一旦读到某个数据在某次更新后的值,这个用户不会再读到比这个值更旧的值。
412 0
|
1天前
|
算法 关系型数据库 MySQL
Go语言中的分布式ID生成器设计与实现
【5月更文挑战第6天】本文探讨了Go语言在分布式系统中生成全局唯一ID的策略,包括Twitter的Snowflake算法、UUID和MySQL自增ID。Snowflake算法通过时间戳、节点ID和序列号生成ID,Go实现中需处理时间回拨问题。UUID保证全局唯一,但长度较长。MySQL自增ID依赖数据库,可能造成性能瓶颈。选择策略时需考虑业务需求和并发、时间同步等挑战,以确保系统稳定可靠。
104 0
|
19天前
|
存储 SQL 算法
搞定了 6 种分布式ID,分库分表哪个适合做主键?
在《ShardingSphere5.x分库分表原理与实战》系列的第七篇文章中,作者探讨了分布式ID在分库分表中的重要性,以及如何利用`ShardingSphere-jdbc`的多种主键生成策略。文章介绍了`UUID`、`NanoID`、自定义雪花算法和`CosId`等策略的优缺点,并警告不要在SQL中手动拼接主键字段。此外,文章还展示了如何配置这些策略,并提醒读者`CosId`在5.2.0版本可能不可用。最后,文章讨论了如何自定义分布式主键生成算法,并强调选择策略时要考虑全局唯一性、性能和易用性。
|
2月前
|
缓存 算法 关系型数据库
深度思考:雪花算法snowflake分布式id生成原理详解
雪花算法snowflake是一种优秀的分布式ID生成方案,其优点突出:它能生成全局唯一且递增的ID,确保了数据的一致性和准确性;同时,该算法灵活性强,可自定义各部分bit位,满足不同业务场景的需求;此外,雪花算法生成ID的速度快,效率高,能有效应对高并发场景,是分布式系统中不可或缺的组件。
深度思考:雪花算法snowflake分布式id生成原理详解
|
2月前
|
NoSQL 算法 MongoDB
一文搞定分布式系统ID生成方案
一文搞定分布式系统ID生成方案
10 0
|
2月前
|
算法 NoSQL Java
Java实战:分布式ID生成方案
在分布式系统的设计与开发过程中,如何生成全局唯一、有序且高可用的ID是一个绕不开的核心问题。尤其是在电商、社交网络、金融交易等领域,ID不仅是业务数据的重要标识,还可能直接影响系统的稳定性和扩展性。本文将深入剖析分布式ID生成方案的设计原则、常见算法,并通过Java示例展示一种可行的实现方式。
45 2
|
2月前
|
算法 Java 数据中心
分布式ID生成系统之雪花算法详解
在当今的云计算和微服务架构盛行的时代,分布式系统已成为软件开发的重要组成部分。随着系统规模的扩大和业务的复杂化,对数据一致性和唯一性的要求也越来越高,尤其是在全局唯一标识符(ID)的生成上。因此,分布式ID生成系统应运而生,成为保证数据唯一性和提高系统可扩展性的关键技术之一。雪花算法(Snowflake)是Twitter开源的一种算法,用于生成64位的全局唯一ID,非常适用于分布式系统中生成唯一标识符。下面我们将深入探讨雪花算法的原理、结构和实现方式。
113 2
 分布式ID生成系统之雪花算法详解
|
2月前
|
NoSQL 算法 Java
【Redis】4、全局唯一 ID生成、单机(非分布式)情况下的秒杀和一人一单
【Redis】4、全局唯一 ID生成、单机(非分布式)情况下的秒杀和一人一单
65 0
|
3月前
|
存储 算法 NoSQL
全网最全的分布式ID生成方案解析
全网最全的分布式ID生成方案解析
131 0
|
4月前
|
算法 NoSQL 关系型数据库
9种 分布式ID生成方式
9种 分布式ID生成方式
427 0