天天看点

全局唯一递增的id_全局唯一ID生成方案

原标题:全局唯一ID生成方案

在实现大型分布式程序时,通常会有全局唯一ID生成的需求,用来对每一个对象标识一个代号。另外,业务层对于全局唯一ID生成也有要求:

全局唯一性:不能出现重复的ID号。

趋势递增:在MySQL InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B-tree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能。

单调递增:保证下一个ID一定大于上一个ID,例如事务版本号、IM增量消息、排序等特殊需求。

信息安全:如果ID是连续的,恶意用户的抓取工作就非常容易做了,直接按照顺序下载指定URL即可;如果是订单号就更危险了,竞争对手可以直接知道一天的单量。所以在一些应用场景下,会需要ID无规则、不规则。

常见的全局唯一ID生成方案

UUID

UUID(Universally Unique Identifier)的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的36个字符,示例:550e8400-e29b-41d4-a716-446655440000,到目前为止业界一共有5种方式生成UUID,详情见IETF发布的UUID规范 A Universally Unique IDentifier (UUID) URN Namespace。

优点:

性能非常高:本地生成,没有网络消耗。

缺点:

不易于存储:UUID太长,16字节128位,通常以36长度的字符串表示,很多场景不适用。

信息不安全:基于MAC地址生成UUID的算法可能会造成MAC地址泄露,这个漏洞曾被用于寻找梅丽莎病毒的制作者位置。

ID作为主键时在特定的环境会存在一些问题,比如做DB主键的场景下,UUID就非常不适用:MySQL官方有明确的建议 主键要尽量越短越好,36个字符长度的UUID不符合要求。

对MySQL索引不利:如果作为数据库主键,在InnoDB引擎下,UUID的无序性可能会引起数据位置频繁变动,严重影响性能。

UUID变种

UUID变种比较流行的是基于MySQL UUID的变种:timestamp + machine number + random,具体介绍见: GUID/UUID Performance Breakthrough

优点:

开发成本较低

缺点:

基于MySQL的存储过程,性能较差,另外,随着 UUID_TO_BIN(str, swap_flag)方法的出现,以上实现方式已不太适用。

Snowflake或其变种

这种方案大致来说是一种以划分命名空间(UUID也算,由于比较常见,所以单独分析)来生成ID的一种算法,这种方案把64-bit分别划分成多段:timestamp + work number + seq number,分开来标示机器、时间等。详细内容可以查看先前的文章。

优点:

毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。

不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。

可以根据自身业务特性分配bit位,非常灵活。

缺点:

强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。

需要引入zookeeper 和独立的snowflake专用服务器

MongoDB官方文档 ObjectID可以算作是和snowflake类似方法,通过“时间+机器码+pid+inc”共12个字节,通过4+3+2+3的方式最终标识成一个24长度的十六进制字符。相比snowflake长度及可读性要差一些。

Flickr的数据库自增

Flickr的数据库自增方式,是用的一个叫做 ticketserver技术,使用纯mysql来实现的。

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

先插入一条记录,然后再用replace去获取这个id

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

SELECT LAST_INSERT_ID();

优点:

非常简单,利用现有数据库系统的功能实现,成本小,有DBA专业维护。

ID号单调自增,可以实现一些对ID有特殊要求的业务。

缺点:

强依赖DB,当DB异常时整个系统不可用,属于致命问题。配置主从复制可以尽可能的增加可用性,但是数据一致性在特殊情况下难以保证。主从切换时的不一致可能会导致重复发号。

ID发号性能瓶颈限制在单台MySQL的读写性能。

对于MySQL性能问题,可用如下方案解决:在分布式系统中我们可以多部署几台机器,每台机器设置不同的初始值,且步长和机器数相等。比如有两台机器。设置步长step为2,TicketServer1的初始值为1(1,3,5,7,9,11…)、TicketServer2的初始值为2(2,4,6,8,10…)

Instagram的存储过程

同样, Instagram的ID生成方式在前面的文章中也介绍过,简单的描述为:41b ts + 13b shard id + 10b increment seq,具体实现方式如下:

创建存储过程:

CREATE OR REPLACE FUNCTION insta5.next_id(OUT result bigint) AS $$

DECLARE

our_epoch bigint := 1314220021721;

seq_id bigint;

now_millis bigint;

shard_id int := 5;

BEGIN

SELECT nextval('insta5.table_id_seq') %% 1024 INTO seq_id;

SELECT FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000) INTO now_millis;

result := (now_millis - our_epoch) << 23;

result := result | (shard_id <<10);

result := result | (seq_id);

END;

$$ LANGUAGE PLPGSQL;

创建表:

CREATE TABLE insta5.our_table (

"id" bigint NOT NULL DEFAULT insta5.next_id(),

...rest of table schema...

)

详细介绍见: Sharding & IDs at Instagram

优点:

开发成本低

缺点:

基于postgreSQL的存储过程,通用性差

来源:https://www.biaodianfu.com

声明:文章的文图版权、观点归原作者所有,平台只提供参考;如有问题,请联系我们,我们将在第一时间处理。返回搜狐,查看更多

责任编辑: