这是一个架构设计中绕不开的问题。先给你结论再详细分析。 一句话结论场景推荐核心理由单库单表、用户量 500万数据库自增ID简单、有序、性能足够分库分表、微服务、分布式系统分布式ID全局唯一、不依赖数据库、可扩展需要暴露用户ID给前端分布式ID非自增防止信息泄露和爬虫用户量不确定可能爆发增长分布式ID预留扩展能力一、数据库自增ID数据库自增ID是最传统的方式靠数据库的AUTO_INCREMENT或SEQUENCE生成。✅ 优势优势说明简单无需额外组件数据库原生支持有序递增对BTree索引友好插入性能高占用空间小BIGINT类型8字节INT类型4字节便于调试能直接从ID看出记录创建顺序❌ 劣势劣势说明影响程度主从延迟问题插入后获取ID需要再次查询或依赖Last_Insert_ID中迁移困难分库分表时ID会冲突需重写高暴露业务信息用户A的ID100用户B的ID101能推断出注册顺序中性能瓶颈高并发下数据库锁竞争高仅限于单库跨库无法保证唯一高适用场景小型项目用户量在百万级以下无分库分表计划用户ID不对外暴露二、分布式ID主流方案对比方案原理长度性能有序优点缺点Snowflake (雪花算法)时间戳 机器ID 序列号64bit (19位十进制)10w QPS趋势递增不依赖外部服务性能极高依赖机器时钟Leaf (美团)Snowflake变体 / 号段模式64bit5w QPS趋势递增支持号段缓存DB容灾需额外部署Redis自增INCR命令自定义5w QPS递增简单有序依赖Redis持久化风险UUID随机生成128bit (36位字符串)极高无序全局唯一无需中心化占用空间大索引性能差数据库号段批量获取ID区间64bit1w QPS递增简单可靠需DB支持有单点TinyID (滴滴)号段模式64bit10w QPS递增支持多业务HTTP接入需额外部署三、深度分析3.1 Snowflake 雪花算法 — 最推荐雪花算法是分布式ID生成的事实标准由Twitter开源64bit长整型Go中用uint64表示。结构图0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 1bit(未用) 41bit时间戳(毫秒) 10bit机器ID 12bit序列号Go语言实现package id import ( errors sync time ) type Snowflake struct { mu sync.Mutex startTime int64 // 起始时间戳 (毫秒) machineID int64 // 机器ID (0-1023) sequence int64 // 序列号 (0-4095) lastStamp int64 // 上次生成ID的时间戳 } // 配置参数 (可调整) const ( machineBits 10 // 机器ID位数 sequenceBits 12 // 序列号位数 machineMax -1 ^ (-1 machineBits) // 最大机器ID: 1023 sequenceMax -1 ^ (-1 sequenceBits) // 最大序列号: 4095 timeShift machineBits sequenceBits // 22 machineShift sequenceBits // 12 ) func NewSnowflake(machineID int64) (*Snowflake, error) { if machineID 0 || machineID machineMax { return nil, errors.New(machineID out of range) } return Snowflake{ startTime: 1704067200000, // 2024-01-01 00:00:00 (可自定义) machineID: machineID, }, nil } func (s *Snowflake) NextID() (int64, error) { s.mu.Lock() defer s.mu.Unlock() now : time.Now().UnixMilli() if now s.lastStamp { return 0, errors.New(clock moved backwards) } if now s.lastStamp { s.sequence (s.sequence 1) sequenceMax if s.sequence 0 { // 当前毫秒序列号用完等待下一毫秒 for now s.lastStamp { now time.Now().UnixMilli() } } } else { s.sequence 0 } s.lastStamp now // 组装ID id : ((now - s.startTime) timeShift) | (s.machineID machineShift) | s.sequence return id, nil }使用示例// 初始化 (每个服务实例一个唯一machineID如用IP后10位或Pod Name) worker, _ : NewSnowflake(1) // 生成ID userID, _ : worker.NextID() // 输出如: 1234567890123456789为什么推荐雪花算法性能极高单机每秒可生成几十万ID无网络开销趋势递增对MySQL BTree索引友好64bit长整型占用空间小适合做数据库主键无需外部依赖不依赖Redis、DB等中间件Go语言天然支持int64类型刚好够用3.2 如果必须用UUID的情况以下场景才适合用UUID分布式文件系统如对象存储的Key日志追踪IDTraceID临时会话ID数据库非聚簇索引字段⚠️ 警告绝对不要用UUID作为MySQL的聚簇索引主键随机插入会导致页分裂和碎片化3.3 数据库自增ID的妥协方案如果坚持用自增ID但担心暴露信息可采用双ID策略数据库主键用自增ID内部使用对外暴露用HashID加密或Snowflake生成的公开ID// 对外暴露的ID使用HashID (保持短小) import github.com/speps/go-hashids func EncodeID(id int64) string { hd : hashids.NewData() hd.Salt your-salt-key hd.MinLength 8 h, _ : hashids.NewWithData(hd) result, _ : h.EncodeInt64([]int64{id}) return result }四、决策流程图开始 │ ▼ ┌─────────────────┐ │ 是否分库分表或 │ │ 微服务架构 │ └─────────────────┘ │ │ 是 否 │ │ ▼ ▼ ┌─────────────┐ ┌─────────────┐ │ 使用分布式ID │ │ 用户量会超 │ │ (Snowflake) │ │ 500万吗 │ └─────────────┘ └─────────────┘ │ │ 是 否 │ │ ▼ ▼ ┌─────────────┐ ┌─────────────┐ │ 分布式ID │ │ 数据库自增ID │ │ (预留扩展) │ │ 或Snowflake │ └─────────────┘ └─────────────┘五、最终推荐项目阶段推荐方案理由创业初期 10万用户数据库自增ID简单够用成长期10万-100万数据库自增ID HashID加密保护商业隐私中大规模百万-千万Snowflake雪花算法性能优越易扩展大型分布式分库分表Leaf / 分布式雪花方案支持多机房容灾经验之谈如果拿不准直接上Snowflake。它比自增ID多不了几行代码但省去了将来迁移的千倍痛苦。微服务架构中从一开始就使用分布式ID是最稳妥的选择。
分布式ID vs 数据库自增ID:如何选择?
这是一个架构设计中绕不开的问题。先给你结论再详细分析。 一句话结论场景推荐核心理由单库单表、用户量 500万数据库自增ID简单、有序、性能足够分库分表、微服务、分布式系统分布式ID全局唯一、不依赖数据库、可扩展需要暴露用户ID给前端分布式ID非自增防止信息泄露和爬虫用户量不确定可能爆发增长分布式ID预留扩展能力一、数据库自增ID数据库自增ID是最传统的方式靠数据库的AUTO_INCREMENT或SEQUENCE生成。✅ 优势优势说明简单无需额外组件数据库原生支持有序递增对BTree索引友好插入性能高占用空间小BIGINT类型8字节INT类型4字节便于调试能直接从ID看出记录创建顺序❌ 劣势劣势说明影响程度主从延迟问题插入后获取ID需要再次查询或依赖Last_Insert_ID中迁移困难分库分表时ID会冲突需重写高暴露业务信息用户A的ID100用户B的ID101能推断出注册顺序中性能瓶颈高并发下数据库锁竞争高仅限于单库跨库无法保证唯一高适用场景小型项目用户量在百万级以下无分库分表计划用户ID不对外暴露二、分布式ID主流方案对比方案原理长度性能有序优点缺点Snowflake (雪花算法)时间戳 机器ID 序列号64bit (19位十进制)10w QPS趋势递增不依赖外部服务性能极高依赖机器时钟Leaf (美团)Snowflake变体 / 号段模式64bit5w QPS趋势递增支持号段缓存DB容灾需额外部署Redis自增INCR命令自定义5w QPS递增简单有序依赖Redis持久化风险UUID随机生成128bit (36位字符串)极高无序全局唯一无需中心化占用空间大索引性能差数据库号段批量获取ID区间64bit1w QPS递增简单可靠需DB支持有单点TinyID (滴滴)号段模式64bit10w QPS递增支持多业务HTTP接入需额外部署三、深度分析3.1 Snowflake 雪花算法 — 最推荐雪花算法是分布式ID生成的事实标准由Twitter开源64bit长整型Go中用uint64表示。结构图0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 1bit(未用) 41bit时间戳(毫秒) 10bit机器ID 12bit序列号Go语言实现package id import ( errors sync time ) type Snowflake struct { mu sync.Mutex startTime int64 // 起始时间戳 (毫秒) machineID int64 // 机器ID (0-1023) sequence int64 // 序列号 (0-4095) lastStamp int64 // 上次生成ID的时间戳 } // 配置参数 (可调整) const ( machineBits 10 // 机器ID位数 sequenceBits 12 // 序列号位数 machineMax -1 ^ (-1 machineBits) // 最大机器ID: 1023 sequenceMax -1 ^ (-1 sequenceBits) // 最大序列号: 4095 timeShift machineBits sequenceBits // 22 machineShift sequenceBits // 12 ) func NewSnowflake(machineID int64) (*Snowflake, error) { if machineID 0 || machineID machineMax { return nil, errors.New(machineID out of range) } return Snowflake{ startTime: 1704067200000, // 2024-01-01 00:00:00 (可自定义) machineID: machineID, }, nil } func (s *Snowflake) NextID() (int64, error) { s.mu.Lock() defer s.mu.Unlock() now : time.Now().UnixMilli() if now s.lastStamp { return 0, errors.New(clock moved backwards) } if now s.lastStamp { s.sequence (s.sequence 1) sequenceMax if s.sequence 0 { // 当前毫秒序列号用完等待下一毫秒 for now s.lastStamp { now time.Now().UnixMilli() } } } else { s.sequence 0 } s.lastStamp now // 组装ID id : ((now - s.startTime) timeShift) | (s.machineID machineShift) | s.sequence return id, nil }使用示例// 初始化 (每个服务实例一个唯一machineID如用IP后10位或Pod Name) worker, _ : NewSnowflake(1) // 生成ID userID, _ : worker.NextID() // 输出如: 1234567890123456789为什么推荐雪花算法性能极高单机每秒可生成几十万ID无网络开销趋势递增对MySQL BTree索引友好64bit长整型占用空间小适合做数据库主键无需外部依赖不依赖Redis、DB等中间件Go语言天然支持int64类型刚好够用3.2 如果必须用UUID的情况以下场景才适合用UUID分布式文件系统如对象存储的Key日志追踪IDTraceID临时会话ID数据库非聚簇索引字段⚠️ 警告绝对不要用UUID作为MySQL的聚簇索引主键随机插入会导致页分裂和碎片化3.3 数据库自增ID的妥协方案如果坚持用自增ID但担心暴露信息可采用双ID策略数据库主键用自增ID内部使用对外暴露用HashID加密或Snowflake生成的公开ID// 对外暴露的ID使用HashID (保持短小) import github.com/speps/go-hashids func EncodeID(id int64) string { hd : hashids.NewData() hd.Salt your-salt-key hd.MinLength 8 h, _ : hashids.NewWithData(hd) result, _ : h.EncodeInt64([]int64{id}) return result }四、决策流程图开始 │ ▼ ┌─────────────────┐ │ 是否分库分表或 │ │ 微服务架构 │ └─────────────────┘ │ │ 是 否 │ │ ▼ ▼ ┌─────────────┐ ┌─────────────┐ │ 使用分布式ID │ │ 用户量会超 │ │ (Snowflake) │ │ 500万吗 │ └─────────────┘ └─────────────┘ │ │ 是 否 │ │ ▼ ▼ ┌─────────────┐ ┌─────────────┐ │ 分布式ID │ │ 数据库自增ID │ │ (预留扩展) │ │ 或Snowflake │ └─────────────┘ └─────────────┘五、最终推荐项目阶段推荐方案理由创业初期 10万用户数据库自增ID简单够用成长期10万-100万数据库自增ID HashID加密保护商业隐私中大规模百万-千万Snowflake雪花算法性能优越易扩展大型分布式分库分表Leaf / 分布式雪花方案支持多机房容灾经验之谈如果拿不准直接上Snowflake。它比自增ID多不了几行代码但省去了将来迁移的千倍痛苦。微服务架构中从一开始就使用分布式ID是最稳妥的选择。