资讯专栏INFORMATION COLUMN

探讨分布式ID生成系统

junbaor / 1649人阅读

摘要:结合对做如下调整的毫秒时间戳的数据逻辑分区以及的自增序列。为了解决这个问题,便引入了逻辑分区。参考文章批量插入返回自增的问题美团点评分布式生成系统

这里的博客版本都不会被更新维护。查看最新的版本请移步:http://neojos.com

全称Universally Unique IdentifierUUID占128bit,也就是16个英文字符的长度(16byte),需要强调的是,它的生成无需中心处理程序。

UUID被用来标识URN(Uniform Resource Names),对于Transaction ID以及其他需要唯一标志的场景都可以使用它。

UUID是空间和时间上的唯一标识,它长度固定,内部中包含时间信息。如果服务器时间存在不同步的情况,UUID可能会出现重复。

UUID构成

基本格式,由6部分组成:

time-low - time-mide - time-high-and-version - clock-seq-and-reserved & clock-seq-low - node

一个URN示例:f81d4fae-7dec-11d0-a765-00a0c91e6bf6

因为UUID占128bit,16进制数占4bit,所以转换成16进制0-f的字符串总共有32位。组成的各个部分具体由几位16进制表示,请查阅 Namespace Registration Template

因为UUID太长且无序,导致其不适合做MySQL的主键索引。而且MySQL自带的auto-increment功能,选择bigint的话也只占用64bit

All indexes other than the clustered index are known as secondary indexes. In InnoDB, each record in a secondary index contains the primary key columns for the row, as well as the columns specified for the secondary index. InnoDB uses this primary key value to search for the row in the clustered index.

If the primary key is long, the secondary indexes use more space, so it is advantageous to have a short primary key.

MongoDB"s ObjectId

ObjectId由占4-byte的时间戳、3-byte的机器标识、2-byte的进程ID以及3-byte的计数组成,总共还是占用96bit

这些ID组成包括时间、机器标识、随机数,在UUID生成时还使用到MAC地址。这些参数中时间是关键,保证集群服务器的时钟准确非常重要。

Twitter Snowflake

Twitter Snowflake生成的ID占64bit,跟bigint大小一致。由41 bit毫秒精度的时间戳、10bit的机器ID以及12 bit的序列号组成(计数每4096就重新开始一轮),剩下的1 bit奉献给未来。

作者修改了它的原始设定,将剩下的1 bit给了时间戳。使用机器MAC地址的HASH值作为当前机器的ID

服务全局保存最近一次生成ID的时间戳lastTimestamp,作为生成新ID的判断依据,避免时间回溯。详细代码请参照[1]

// Block and wait till next millisecond
private long waitNextMillis(long currentTimestamp) {
    while (currentTimestamp == lastTimestamp) {
        currentTimestamp = timestamp();
    }
    return currentTimestamp;
}

同时将sequence也声明为全局变量,每间隔4096次就重新开始计数。主要用于应对:当时间戳相同时保证生成的ID是不同的。

if (currentTimestamp == lastTimestamp) {
    sequence = (sequence + 1) & maxSequence;
    if(sequence == 0) {
        // Sequence Exhausted, wait till next millisecond.
        currentTimestamp = waitNextMillis(currentTimestamp);
    }
} else {
    // reset sequence to start with zero for the next millisecond
    sequence = 0;
}
Database Ticket Servers

该方式通过中心的DB服务来生成唯一自增ID,但DB服务的写操作会成为系统的瓶颈。如果后台是单个DB服务的话,存在单点问题。

参考Flickr的方法,后台使用两个DB来生成ID,其中auto-increment一个按照奇数步长增长,另一个按照偶数步长增长。MySQL内部使用REPLACE来实现,通过一条冲突的记录,来持续生成自增的主键ID

REPLACE makes sense only if a table has a PRIMARY KEY or UNIQUE index. Otherwise, it becomes equivalent to INSERT, because there is no index to be used to determine whether a new row duplicates another.

结合Twitter SnowflakeID做如下调整:41-bit的毫秒时间戳、13-bit的数据逻辑分区以及10-bit的自增序列。自增序列对1024取余,每个分区每毫秒内能生成1024个自增ID

Flickr中各个数据表按照不同的步长增长,当需要分表的时候就会存在巨复杂的数据迁移问题。为了解决这个问题,便引入了逻辑分区Shard ID。通过逻辑上的Shard,将数据分散在不同的数据表中。这样后续的分库分表都可以通过操作逻辑上Shard来实现,将DB从具体的实现中解脱出来。

关于获取MySQL自增ID,代码无法批量获取插入的全部自增ID列表,MySQL只会返回第一条记录的自增ID。因为自增ID是连续的,所以可以通过计算的方式来计算出ID列表。

If you insert multiple rows using a single INSERT statement, LAST_INSERT_ID() returns the value generated for the first inserted row only. The reason for this is to make it possible to reproduce easily the same INSERT statement against some other server.

关于Shard可以查看本地缓存BigCache,很有参考意义(我觉得)。

总结

文中介绍了ID的两种生成方式,核心的区别在于:整个系统的ID是否支持单调递增。Twitter Snowflake以及UUID可以保证生成的数据唯一,但多台服务器的话,无法保证生成的数据有序。而Ticket Servers通过结合MySQLauto-increment解决了这个问题。

参考文章:

Generating unique IDs in a distributed environment at high scale

A Universally Unique IDentifier (UUID) URN Namespace

Clustered and Secondary Indexes

Sharding & IDs at Instagram

Ticket Server: Distributed Unique Primary Keys on the Cheap

MySQL批量插入返回自增ID的问题

Leaf——美团点评分布式ID生成系统

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/72953.html

相关文章

  • 初探布式系统之数据拆分

    摘要:个人对分布式系统的涉及很感兴趣,但分布式系统涉及的知识非常多,刚开始学习时也是各个点分散的学习。前两天对于数据拆分这一块做了一个总结,因此记录下来。 个人对分布式系统的涉及很感兴趣,但分布式系统涉及的知识非常多,刚开始学习时也是各个点分散的学习。前两天对于数据拆分这一块做了一个总结,因此记录下来。 技术出现的原因都是为了解决问题,本文章也是按照这个思路去探讨的。 为什么需要将数据库内的...

    Martin91 评论0 收藏0
  • 人人都是 API 设计师:我对 RESTful API、GraphQL、RPC API 的思考

    摘要:通常情况下,伪都是基于第一层次与第二层次设计的。为了解决这个版本不兼容问题,在设计的一种实用的做法是使用版本号。例如,建议第三位版本号通常表示兼容升级,只有不兼容时才需要变更服务版本。 原文地址:梁桂钊的博客 博客地址:blog.720ui.com 欢迎关注公众号:「服务端思维」。一群同频者,一起成长,一起精进,打破认知的局限性。 有一段时间没怎么写文章了,今天提笔写一篇自己对 API 设...

    ormsf 评论0 收藏0
  • 人人都是 API 设计师:我对 RESTful API、GraphQL、RPC API 的思考

    摘要:通常情况下,伪都是基于第一层次与第二层次设计的。为了解决这个版本不兼容问题,在设计的一种实用的做法是使用版本号。例如,建议第三位版本号通常表示兼容升级,只有不兼容时才需要变更服务版本。 原文地址:梁桂钊的博客博客地址:http://blog.720ui.com 欢迎关注公众号:「服务端思维」。一群同频者,一起成长,一起精进,打破认知的局限性。 有一段时间没怎么写文章了,今天提笔写一篇...

    FWHeart 评论0 收藏0

发表评论

0条评论

junbaor

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<