How to implement distributed ID

What are the distributed ID schemes and their advantages and disadvantages

Distributed ID Generator Solution - CSDN

Distributed ID Solution - Toutiao

Summary of Unique ID Generation Schemes for Distributed Systems - Code

 

background

In distributed systems, it is often necessary to uniquely identify a large amount of data, messages, http requests, etc. For example, http requests between distributed systems need a unique identifier, and this unique identifier needs to be used when calling link analysis. At this time, the self-incrementing primary key of the database can no longer meet the requirements, and a system that can generate a globally unique ID is needed. This system needs to meet the following requirements:

 

Globally unique : no duplicate IDs can appear.

High availability : The ID generation system is the basic system and is called by many key systems. Once it goes down, it will have a serious impact.

 

Introduction to the classic scheme

 

1. UUID

UUID is the abbreviation of Universally Unique Identifier. It is a machine-generated identifier that is unique within a certain range (from a specific namespace to the world). UUID is a 16-byte 128-bit long number, usually in 36-byte characters. String representation, for example: 3F2504E0-4F89-11D3-9A0C-0305E82C3301.

 

UUID is generated by a certain algorithm machine. In order to ensure the uniqueness of UUID, the specification defines elements including network card MAC address, timestamp, namespace (Namespace), random or pseudo-random number, timing and other elements, as well as the algorithm for generating UUID from these elements . The complex nature of UUID means that it can only be generated by a computer while ensuring its uniqueness.

 

advantage:

The ID is generated locally, no remote call is required, the latency is low, and the performance is high.

shortcoming:

UUID is too long , 16 bytes and 128 bits, usually represented by a 36-length string, which is not applicable in many scenarios, such as using UUID as a database index field.

Without sorting, there is no guarantee that the trend is increasing .

 

 

2. Flicker scheme (self-growth mechanism)

这个方案是由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"

 

 

...

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326006337&siteId=291194637