背景
在分布式系统中,经常需要对大量的数据、消息、http请求等进行唯一标识,例如:在分布式系统之间http请求需要唯一标识,调用链路分析的时候需要使用这个唯一标识。这个时候数据库自增主键已经不能满足需求,需要一个能够生成全局唯一ID的系统,这个系统需要满足以下需求:
全局唯一:不能出现重复ID。
高可用:ID生成系统是基础系统,被许多关键系统调用,一旦宕机,会造成严重影响。
经典方案介绍
1. UUID
UUID是Universally Unique Identifier的缩写,它是在一定的范围内(从特定的名字空间到全球)唯一的机器生成的标识符,UUID是16字节128位长的数字,通常以36字节的字符串表示,比如:3F2504E0-4F89-11D3-9A0C-0305E82C3301。
UUID经由一定的算法机器生成,为了保证UUID的唯一性,规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成UUID的算法。UUID的复杂特性在保证了其唯一性的同时,意味着只能由计算机生成。
优点:
本地生成ID,不需要进行远程调用,时延低,性能高。
缺点:
UUID过长,16字节128位,通常以36长度的字符串表示,很多场景不适用,比如用UUID做数据库索引字段。
没有排序,无法保证趋势递增。
2. Flicker方案(自增长机制)
这个方案是由Flickr团队提出,设计单独的库表,单独提供产生全局ID的服务,主要思路采用了MySQL自增长ID的机制(auto_increment + replace into)
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;
#每次业务使用下列SQL读写MySQL得到ID号
REPLACE INTO Tickets64 (stub) VALUES ('a'); SELECT LAST_INSERT_ID();
replace into 跟 insert 功能类似,不同点在于:replace into 首先尝试插入数据到表中,如果发现表中已经有此行数据(根据主键或者唯-索引判断)则先删除此行数据,然后插入新的数据, 否则直接插入新数据。
为了避免单点故障,最少需要两个数据库实例,通过区分auto_increment的起始值和步长来生成奇偶数的ID。
Server1:
auto-increment-increment = 2
auto-increment-offset = 1
Server2:
auto-increment-increment = 2
auto-increment-offset = 2
优点:
充分借助数据库的自增ID机制,可靠性高,生成有序的ID。
缺点:
ID生成性能依赖单台数据库读写性能。
依赖数据库,当数据库异常时整个系统不可用。
对于依赖MySql性能问题,可用如下方案解决:
在分布式环境中我们可以部署多台,每台设置不同的初始值,并且步长为机器台数,比如部署N台,每台的初始值就为0,1,2,3…N-1,步长为N。
以上方案虽然解决了性能问题,但是也存在很大的局限性:
系统扩容困难:系统定义好步长之后,增加机器之后调整步长困难。
数据库压力大:每次获取一个ID都必须读写一次数据库。
3. 阿里-TDDL序列生成方式(取出一定数量放内存中)
TDDL是阿里的分库分表中间件,它里面包含了全局数据库ID的生成方式,主要思路:
使用数据库同步ID信息。
每次批量取一定数量的可用ID在内存中,使用完后,再请求数据库重新获取下一批可用ID,每次获取的可用ID数量由步长控制,实际业务中可根据使用速度进行配置。
每个业务可以给自己的序列起个唯一的名字,隔离各个业务系统的ID。
数据表设计:
seqName varchar(100) 序列名称,主键 cur_value bigint(20) 当前值 step int 步长,根据实际情况设置
优点:
- 相比flicker方案,大大降低数据库写压力,数据库不再是性能瓶颈。
- 相比flicker方案,生成ID性能大幅度提高,因为获取一个可用号段后在内存中直接分配,相对于每次读取数据库性能提高了几个量级。
- 不同业务不同的ID需求可以用seqName字段区分,每个seqName的ID获取相互隔离,互不影响。
缺点:
强依赖数据库,当数据库异常时整个系统不可用。
4. twitter-snowflake方案
snowflake是twitter开源的分布式ID生成系统。 Twitter每秒有数十万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统中不同机器产生的id必须不同。
这种方案生成一个64bit的数字,64bit被划分成多个段,分别表示时间戳、机器编码、序号。
ID为64bit 的long 数字,由三部分组成:
41位的时间序列(精确到毫秒,41位的长度可以使用69年)。
10位的机器标识(10位的长度最多支持部署1024个节点)。
12位的计数顺序号(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)。
优点:
时间戳在高位,自增序列在低位,整个ID是趋势递增的,按照时间有序。
性能高,每秒可生成几百万ID。
可以根据自身业务需求灵活调整bit位划分,满足不同需求。
缺点:
强依赖时钟,如果主机时间回拨,则会造成重复ID,会产生
ID虽然有序,但是不连续
在单机上是递增的,但是由于涉及到分布式环境,每台机器上的时钟不可能完全同步,有时候会出现不是全局递增的情况。
5、Redis生成ID
当使用数据库来生成ID性能不够要求的时候,我们可以尝试使用Redis来生成ID。这主要依赖于Redis是单线程的,所以也可以用生成全局唯一的ID。可以用Redis的原子操作 INCR和INCRBY来实现。
可以使用Redis集群来获取更高的吞吐量。假如一个集群中有5台Redis。可以初始化每台Redis的值分别是1,2,3,4,5,然后步长都是5。各个Redis生成的ID为:
A:1,6,11,16,21
B:2,7,12,17,22
C:3,8,13,18,23
D:4,9,14,19,24
E:5,10,15,20,25
这个,随便负载到哪个机确定好,未来很难做修改。但是3-5台服务器基本能够满足器上,都可以获得不同的ID。但是步长和初始值一定需要事先需要了。使用Redis集群也可以方式单点故障的问题。
另外,比较适合使用Redis来生成每天从0开始的流水号。比如订单号=日期+当日自增长号。可以每天在Redis中生成一个Key,使用INCR进行累加。
优点:
1)不依赖于数据库,灵活方便,且性能优于数据库。
2)数字ID天然排序,对分页或者需要排序的结果很有帮助。
缺点:
1)如果系统中没有Redis,还需要引入新的组件,增加系统复杂度。
2)需要编码和配置的工作量比较大。
Redis Incr 命令将 key 中储存的数字值增一。
如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
实例
redis> SET page_view 20 OK redis> INCR page_view (integer) 21 redis> GET page_view # 数字值在 Redis 中以字符串的形式保存 "21"
Redis Incrby 命令将 key 中储存的数字加上指定的增量值。
如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCRBY 命令。
如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
实例
# key 存在且是数字值 redis> SET rank 50 OK redis> INCRBY rank 20 (integer) 70 redis> GET rank "70"
...
相关推荐
项目中的代码截取自mybatis-plus-3.5项目源码中分布式ID的实现,主要是内容是Mybatis-plus项目中IdentifierGenerator接口的两个实现类DefaultIdentifierGenerator和ImadcnIdentifierGenerator的源码,还有IdWorker工具...
为了更清晰地展示分布式ID生成的具体实现方式,以下列举了几种典型的方法。 ##### 4.1 数据库自增ID 利用数据库提供的自增功能是一种简单有效的解决方案。例如,MySQL提供了`AUTO_INCREMENT`属性,可以自动为表中...
Redis分布式锁实现Redisson 15问 Redis分布式锁是指在分布式系统中,多个服务实例之间对同一个资源加锁的机制,以保证数据的一致性和安全性。Redisson是一个基于Redis的分布式锁实现,它提供了一个高效、可靠的加锁...
分布式ID是现代大规模分布式系统中不可或缺的一个组成部分,它主要用于为分布式环境中的每个实体生成全局唯一且具有特定属性的标识符。以下将详细讲解分布式ID的相关知识点: 1. **UUID(Universally Unique ...
标题 "基于ZooKeeper的分布式Session实现" 涉及的是在分布式系统中如何利用Apache ZooKeeper来管理和共享Session信息。ZooKeeper是一款开源的分布式协调服务,它为分布式应用程序提供了一个简单一致的接口,用于处理...
分布式ID生成策略是现代互联网应用中的重要组成部分,尤其是在大数据时代,每个数据实体通常都需要一个唯一标识符(ID)来区分其身份。Snowflake算法是由Twitter开源的一种高效且可扩展的分布式ID生成方案,广泛应用...
4. **分布式Redis生成器**:利用Redis的原子操作,如INCR命令,可以实现分布式ID生成。每个节点向Redis请求下一个ID,保证了全局唯一性。这种方式需要额外的Redis服务,但避免了数据库的依赖。 5. **分布式ID服务**...
总结来说,Java中获取分布式唯一ID可以通过实现雪花ID算法,例如`SnowflakeIdWorker`,它能提供时间序列的64位整数ID。同时,可以使用`UUID`生成字符串唯一ID,但不适用于分布式环境。而`IdUtils`中的方法可能是对...
Vesta,uidgennator等分布式id生成方案 UidGenerator是Java实现的, 基于Snowflake算法的唯一ID生成器。UidGenerator以组件形式工作在应用项目中, 支持自定义workerId位数和初始化策略, 从而适用于docker等虚拟化环境...
本篇文章将详细探讨如何使用Go和MySQL来实现一个分布式ID生成服务。 ### 1. 分布式ID的重要性 在大规模分布式系统中,为了保证数据的一致性和避免冲突,每个新生成的ID必须是全局唯一的。传统的递增ID在多节点环境...
分布式锁通常有三种实现方式:基于关系数据库的乐观锁、基于Redis的分布式锁以及基于ZooKeeper的分布式锁。 #### 1. 关系数据库乐观锁 基于关系数据库实现分布式锁主要利用数据库本身的事务机制和锁定机制。在高...
### 分布式ID生成方法详解 #### 一、需求背景及重要性 在现代软件开发过程中,无论是消息系统、订单管理、论坛应用等场景,都离不开一个关键元素——记录标识,比如`message-id`、`order-id`或`tiezi-id`等。这些...
分布式ID的定义,可什么时候需要它呢?有人会回答分布式系统需要,可真的需要吗?并不一定,不是所有分布式系统都需要,回想以前单体架构时代,ID通常是作为数据的唯一标识,比如用户会有用户ID、订单会有订单ID……...
Java Redis分布式锁的正确实现方式详解 Java Redis分布式锁是指使用Redis实现的分布式锁机制,旨在解决分布式系统中的并发问题。分布式锁有三种实现方式:数据库乐观锁、基于Redis的分布式锁和基于ZooKeeper的...
分布式ID雪花算法的实现
常见的分布式锁实现包括基于Zookeeper、Redis或数据库的分布式锁,它们通常采用乐观锁或悲观锁策略,确保在多个节点间安全地进行资源访问。 分布式事务则涉及多个节点之间的数据一致性保证。本地事务在单一资源管理...
总结来说,结合Go、gRPC和Protobuf,我们可以构建一个可靠的、可扩展的分布式ID生成服务。这种方法保证了ID的全局唯一性和连续递增,同时利用了gRPC的高性能和跨平台特性,以及Protobuf的高效序列化能力。
分布式Session的实现是现代Web应用中一个至关重要的技术,它涉及到多服务器间的用户会话共享,以确保用户在切换服务器时仍然能保持登录状态和其他个性化设置。本文将深入探讨这个主题,结合给定的标签“源码”和...
雪花算法是Twitter开源的一种分布式ID生成算法,它能够生成全局唯一、递增且无碰撞的64位整数ID。这个算法在分布式系统中非常适用,因为传统的序列号生成方式在分布式环境中往往难以解决冲突问题。下面我们将详细...
在众多的分布式ID生成方法中,基于数据库自增ID的方式是一种常见的解决方案。具体来说,就是在数据库中创建一张表,用来存储当前的最大ID值,每当需要生成新的ID时,就在该表中插入一条新记录,从而获得一个新的ID值...