全局随机数
在随着数据库数据单机达到瓶颈已经无法支持的时候就会出现分库分表的场景,这个时候数据库自带的自增主键或者简单组成的随机数已经不能满足需求了,这个时候就需要使用全局唯一 ID。 一个最基本的分布式 ID 需要满足下面这些要求:
- 全局唯一:ID 的全局唯一性肯定是首先要满足的。
- 高性能:分布式 ID 的生成速度要快,对本地资源消耗要小。
- 高可用:生成分布式 ID 的服务要保证可用性无限接近于 100%。
- 方便易用:拿来即用,使用方便,快速接入。
除了这些之外,一个比较好的分布式 ID 还应保证:
- 安全:ID 中不包含敏感信息。
- 有序递增:如果要把 ID 存放在数据库的话,ID 的有序性可以提升数据库写入速度。并且很有可能会直接通过 ID 来进行排序。
- 有具体的业务含义:生成的 ID 如果能有具体的业务含义,可以让定位问题以及开发更透明化(通过 ID 就能确定是哪个业务)。
- 独立部署:也就是分布式系统单独有一个发号器服务,专门用来生成分布式 ID。这样就生成 ID 的服务可以和业务相关的服务解耦。
常见分布式 ID 设计方案
数据库号段模式
每次从数据库批量获取一部分 ID 存放在内存中,每次需要 ID 时,从内存中获取一个,当内存中的 ID 用完之后再去数据库中批量获取一批新的 ID 存入到内存中。像滴滴开源的 Tinyid 就是基于这种方式来做的。不过,TinyId 使用了双号段缓存、增加多 db 支持等方式来进一步优化。
点击跳转官网查看详细内容
缓存数据库
缓存数据库的方案,比如 Redis、MongoDB 等。一般情况下,NoSQL 方案使用 Redis 多一些。我们通过 Redis 的 incr 命令即可实现对 id 原子顺序递增。为了提高可用性和并发,我们可以使用 Redis Cluster,利用集群解决缓存重启机器或者机器故障后造成的数据丢失。
UUID
UUID 是有版本的,之于我们最常见的 UUID.randomUUID() 来说,就是版本 4 的 UUID,目前有以下五种支持方式。
- UUID 是根据时间和节点 ID(通常是 MAC 地址)生成。
- UUID 是根据标识符(通常是组或用户 ID)、时间和节点 ID 生成。
- 版本 3 和版本 5 确定性 UUID 通过散列(hashing)名字空间(namespace)标识符和名称生成。
- 使用随机性或伪随机性生成。
从上面的介绍中可以看出,UUID 可以保证唯一性,因为其生成规则包括 MAC 地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,计算机基于这些规则生成的 UUID 是肯定不会重复的。虽然,UUID 可以做到全局唯一性,但是,我们一般很少会使用它。比如使用 UUID 作为 MySQL 数据库主键的时候就非常不合适:数据库主键要尽量越短越好,而 UUID 的消耗的存储空间比较大(32 个字符串,128 位)。UUID 是无顺序的,InnoDB 引擎下,数据库主键的无序性会严重影响数据库性能。
Snowflake (雪花算法)
Snowflake 是 Twitter 开源的分布式 ID 生成算法。Snowflake 由 64 bit 的二进制数字组成,这 64bit 的二进制被分成了几部分,每一部分存储的数据都有特定的含义:
- sign(1bit):符号位(标识正负),始终为 0,代表生成的 ID 为正数。
- timestamp (41 bits):一共 41 位,用来表示时间戳,单位是毫秒,可以支撑 2 ^41 毫秒(约 69 年)。
- datacenter id + worker id (10 bits):一般来说,前 5 位表示机房 ID,后 5 位表示机器 ID(实际项目中可以根据实际情况调整)。这样就可以区分不同集群/机房的节点。
- sequence (12 bits):一共 12 位,用来表示序列号。 序列号为自增值,代表单台机器每毫秒能够产生的最大 ID 数(2^12 = 4096),也就是说单台机器每毫秒最多可以生成 4096 个 唯一 ID。
如果你想要使用 Snowflake 算法的话,一般不需要你自己再造轮子。有很多基于 Snowflake 算法的开源实现比如美团的 Leaf、百度的 UidGenerator,并且,Seata 还提出了“改良版雪花算法”,针对原版雪花算法进行了一定的优化改良,解决了时间回拨问题,大幅提高的 QPS。
宋柏成的博客