在当今的云计算和微服务架构盛行的时代,分布式系统已成为软件开发的重要组成部分。随着系统规模的扩大和业务的复杂化,对数据一致性和唯一性的要求也越来越高,尤其是在全局唯一标识符(ID)的生成上。因此,分布式ID生成系统应运而生,成为保证数据唯一性和提高系统可扩展性的关键技术之一。雪花算法(Snowflake)是Twitter开源的一种算法,用于生成64位的全局唯一ID,非常适用于分布式系统中生成唯一标识符。下面我们将深入探讨雪花算法的原理、结构和实现方式。
分布式ID的需求背景
在单体应用中,生成唯一ID相对简单,如数据库的自增ID、UUID等方法可以轻易满足需求。然而,在分布式系统中,由于数据可能分布在不同的节点上,传统的ID生成方法面临着多方面的挑战:
- 高可用性:系统任何一个部分的故障都不能影响ID的生成和使用。
- 高并发:在大规模并发的场景下,ID生成系统必须能够快速响应。
- 有序性:在某些业务场景中,需要ID具有一定的可排序性,以便于后续处理。
- 低延迟:ID生成的延迟必须足够低,以满足实时性要求。
推特的雪花系统
Twitter开源的Snowflake算法是目前最流行的分布式ID生成方案之一。它通过结合时间戳、机器标识和序列号来生成64位的长整型ID,既保证了全局唯一性,又具有良好的有序性,非常适合高并发的场景。
如下展示了一个64为ID的构成:
每个部分的含义如下:
-
符号位(1位)
始终为0,预留位,将来有可能用来区分有符号数和无符号数。
-
时间戳(41位)
它是纪元或者自定义纪元开始以来的毫秒数,Snowflake使用的是2010-11-04 01:42:54 的时间戳作为默认纪元的,我们也可以自定义。即时间戳为当前时间的毫秒数 - 自定义的纪元的毫秒数。
-
数据中心id(5位)
最多可以有 2 5 2^5 25个数据中心,取值范围为 0~31。
-
机器id(5位)
最多可以有 2 5 2^5 25台机器,取值范围为 0~31。
-
序列号(12位)
对于某个服务,每一个毫秒内生成一个id,序列号就加1,这个数字在每毫秒开始时都会被重置为0。即一个毫秒内单个服务可以生成 2 12 2^{12} 212即4096个id。
我们可以根据服务的具体情况调整下id各部分的长度,比如,对于并发量低,单次生成id数量大的应用,我们可以适当减少数据中心id和机器id的位数,增加序列号位数来提高每个毫秒内id的生成数量。
Java实现雪花雪花系统
使用Java语言实现雪花算法的ID生成器,可以参考以下代码。这个实现同样遵循了雪花算法的基本结构,包括1位符号位、41位时间戳、10位机器标识(5位数据中心ID和5位工作机器ID)以及12位序列号。我们将这些位数放在了配置文件中,家人们可以根据实际情况进行调整。在这个代码中,我们提供了单id生成接口和批量id生成接口。代码如下:
- 配置信息 application.yml
server:
port: 8000
snowflake:
#数据中心id位数
datacenterBits: 5
# 机器id位数
workerBits: 5
# 序列id所占位数
sequenceBits: 12
# 数据中心id,范围0-2^5-1
datacenterId: 1
# 机器id,范围0-2^5-1
workerId: 1
# 时间戳起始点(2024-01-01 00::00:00 的毫秒数)
twepoch: 1704038400000
#单次批量生成id的最大数量 默认10万
maxBatchCount: 100000
- SnowflakeProperties
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix ="snowflake")
@Data
public class SnowflakeProperties {
//数据中心id<