type
status
date
slug
summary
tags
category
icon
password
分布式id generator
- 多个服务器同时生成ID,怎么保证不重复?
- 用户在不同地区注册,ID怎么全局唯一?
总的来说有几个点:
- IDs must be unique.
- IDs are numerical values only.
- IDs fit into 64-bit.
- IDs are ordered by date.
- Ability to generate over 10,000 unique IDs per second.
有几个方案
数据库自增ID + 分段 - 简单改进
实现思路:
- 服务器A:负责生成 1, 4, 7, 10, 13...
- 服务器B:负责生成 2, 5, 8, 11, 14...
- 服务器C:负责生成 3, 6, 9, 12, 15...
问题:扩容困难,性能受数据库限制
UUID
优点:完全去中心化,性能极高
缺点:128bits太长了,最好还是64bits 随机性导致数据库索引性能差
从小到大的顺序:
bit → byte → KB → MB → GB → TB → PB
🔤 最小单位:bit(位)
bit是计算机中最小的数据单位,只能存储一个二进制数字:0或1。
想象成一个开关:
- 开(1)
- 关(0)
一个bit只能表达"是"或"否"这样的信息。
📦 基本单位:byte(字节)
1 byte = 8 bits
这是计算机处理数据的基本单位。为什么是8位?因为8位可以表示256种不同的组合(2^8),足够表示所有英文字母、数字和常用符号。
Ticket Server

pros:纯数字;单个ticket server 有一个中心的auto increment
cons:single point of failure
Snowflake
- 时间戳部分:当前时间 - 固定起始时间(如2020-01-01)
- 机器ID:每台服务器分配唯一编号(支持1024台机器)
- 序列号:同一毫秒内的计数器(支持每毫秒4096个ID)

问题:在分布式系统中,如何生成全局唯一、高性能、大致有序的ID?
解决思路:把一个64位的long型数字,按照时间+机器+序列号的方式拆分,确保每个部分的组合都是唯一的。
🔧 64位结构详解
1. 符号位(1位)
- 永远是0
- 因为我们生成的是正数ID
- 这样保证生成的ID都是正数
2. 时间戳部分(41位)
存储的是:当前时间 - 固定起始时间
为什么要减去起始时间?
- 如果直接用当前时间戳,41位装不下(2^41约等于69年)
- 减去起始时间后,可以用到2020+69=2089年
- 大大延长了算法的可用时间
时间戳的意义:
- 保证ID大致按时间递增
- 可以从ID反推生成时间
- 同一毫秒内的ID会连续
3. 机器ID部分(10位)
分成两部分:5位数据中心ID + 5位机器ID
作用:确保不同机器生成的ID不重复
实际分配方式:
4. 序列号部分(12位)
同一毫秒内的计数器
含义:
- 同一台机器在同一毫秒内最多生成4096个ID
- 单机理论QPS = 4096 * 1000 = 409万+
🔍 完整生成过程详解
让我用一个具体例子说明:
🎨 位操作详解
左移操作的作用
掩码操作
🚀 性能分析
理论性能
- 单毫秒最大ID数:4096个
- 单机理论QPS:4,096,000
- 实际性能:200-400万QPS(考虑系统开销)
性能测试代码
⚠️ 关键问题和解决方案
1. 时钟回拨问题
问题:服务器时间被调回过去,会导致生成重复ID
2. 机器ID冲突
问题:两台机器使用相同WorkerID会生成重复ID
解决方案:
3. 序列号溢出
问题:同一毫秒内ID请求超过4096个
🔄 ID解析和还原
Snowflake的一个优势是可以从ID中提取信息:
🔍 与其他方案对比
特性 | Snowflake | UUID | 数据库自增 | Redis |
性能 | 极高(400万QPS) | 极高 | 低 | 高 |
有序性 | 趋势递增 | 无序 | 严格递增 | 可控 |
长度 | 64位数字 | 128位字符 | 32-64位 | 可控 |
依赖 | 无外部依赖 | 无 | 数据库 | Redis |
扩展性 | 优秀 | 优秀 | 差 | 一般 |
💡 核心优势总结
- 高性能:纯内存操作,单机数百万QPS
- 趋势递增:大致按时间排序,对B+树索引友好
- 信息丰富:可以解析出生成时间、机器等信息
- 去中心化:不依赖外部系统,避免单点故障
- 可扩展:支持1024台机器同时运行
Snowflake已经成为分布式ID生成的标准方案,被广泛应用在各大互联网公司的核心系统中!