当前位置: 首页 > news >正文

每日一Go-55、分布式 ID 生成(雪花算法 / Segment / Redis / DB)

一、为什么分布式系统一定要“自己造ID”?

单机时代,利用数据库的自增`ID`AUTO_INCREMENT

但是在微服务/多实例/分库分表的情况下,会出现:

  • ID冲突

  • 数据迁移困难

  • 顺序失控

  • 跨库无法唯一定位

二、分布式ID的核心指标

一个靠谱的ID方案,至少要满足:

指标
说明
全局唯一
不能重复
高性能
AQS≥10w
有序性
越新越大
高可用
不能成为单点
易扩展
节点随时加

三、主流4种方案总览

方案
性能
有序
依赖
复杂度
典型
雪花算法
5星
4星
本地
两星
Twitter
Segment号段
4星
5星
DB
3星
美团/京东
Redis INCR
3星
5星
Redis
1星
小系统
DB自增
1星
5星
DB
1星
单体

四、方案1:DB自增

INSERT INTOtVALUES();SELECTLAST_INSERT_ID();

这是所有分布式系统大忌,不推荐。

五、方案2:Redis INCR

// Redis 命令 INCRorder:id // Go调用 id,_:=rdb.Incr(ctx,"order:id").Result()

优点:实现简单;严格递增。

缺点:Redis单点;网络开销;QPS上限。

适合低并发系统/管理后台

六、方案3:Segment号段(美团/京东订单号)

思路:

DB中维护一个号段

一次取一段(例如:1000个)

本地内存自增

表结构:

CREATETABLEid_segment(biz_tagVARCHAR(64)PRIMARYKEY,max_idBIGINT,stepINT);
typeSegmentstruct{curint64maxint64}func(s*Segment)Next()int64{ifs.cur>=s.max{s.reload()}s.cur++returns.cur}

优点:严格递增;ID短

缺点:依赖DB;实现复杂;冷启动慢。

适合订单号、流水号

七、方案4:雪花算法(Snowflake)

1. ID结构

0 | 41bit 时间戳 | 10bit 机器ID | 12bit 序列号

时间戳:毫秒

机器ID:数据中心+worker

序列号:同毫秒并发

趋势递增、完全本地生成、无依赖

2. go 代码实现

packagesnowflakeimport("errors""strconv""sync""time")// 常量定义const(workerBits=10// 工作节点位数seqBits=12// 序列号位数workerMax=-1^(-1<<workerBits)// 工作节点最大IDseqMask=-1^(-1<<seqBits)// 序列号掩码timeShift=workerBits+seqBits// 时间戳左移位数workerShift=seqBits// 工作节点左移位数defaultEpoch=int64(1672531200000)// 默认起始时间戳 (2023-01-01))// ID 自定义类型,用于区分雪花ID和普通int64typeIDint64// Snowflake 雪花算法生成器typeSnowflakestruct{mu sync.Mutex lastTimeint64workerIDint64sequenceint64epochint64}// New 创建雪花算法生成器// workerID: 工作节点ID,范围 0~1023// 返回错误如果workerID超出范围funcNew(workerIDint64)(*Snowflake,error){returnNewWithEpoch(workerID,defaultEpoch)}// NewWithEpoch 创建带自定义起始时间的雪花算法生成器// workerID: 工作节点ID,范围 0~1023// epoch: 自定义起始时间戳(毫秒)// 返回错误如果workerID超出范围funcNewWithEpoch(workerIDint64,epochint64)(*Snowflake,error){ifworkerID<0||workerID>workerMax{returnnil,errors.New("worker ID out of range [0, 1023]")}return&Snowflake{workerID:workerID,epoch:epoch,},nil}// NextID 生成下一个雪花ID// 返回ID类型的雪花ID和可能的错误func(s*Snowflake)NextID()(ID,error){s.mu.Lock()defers.mu.Unlock()now:=time.Now().UnixMilli()// 处理时间回拨ifnow<s.lastTime{return0,errors.New("time is back, ID generation failed")}ifnow==s.lastTime{// 同一毫秒内,递增序列号s.sequence=(s.sequence+1)&seqMask// 序列号耗尽,等待下一个毫秒ifs.sequence==0{// 使用短暂休眠代替自旋等待,减少CPU占用time.Sleep(time.Millisecond)now=time.Now().UnixMilli()// 处理时间回拨(再次检查)ifnow<s.lastTime{return0,errors.New("time is back, ID generation failed")}s.lastTime=now s.sequence=0}}else{// 新的毫秒,重置序列号s.lastTime=now s.sequence=0}// 生成IDid:=((now-s.epoch)<<timeShift)|(s.workerID<<workerShift)|s.sequencereturnID(id),nil}// ParseID 解析雪花ID// 返回ID的各组成部分:时间戳、工作节点ID、序列号funcParseID(id ID,epochint64)(time.Time,int64,int64){idInt:=int64(id)timestamp:=(idInt>>timeShift)+epoch workerID:=(idInt>>workerShift)&((1<<workerBits)-1)sequence:=idInt&seqMaskreturntime.UnixMilli(timestamp),workerID,sequence}// String 将ID转换为字符串func(id ID)String()string{returnstrconv.FormatInt(int64(id),10)}// Int64 将ID转换为int64func(id ID)Int64()int64{returnint64(id)}
//如何使用sf:=snowflake.New(1)id:=sf.NextID()

3. 雪花算法工程注意点:

  • 时间回拨问题

  • NTP同步导致时间倒退

    解决方案:

  • 禁止自动回拨

  • 检测回拨直接panic/等待

  • 使用逻辑时间

八、如何选?

场景
推荐
单体
DB
小系统/快速上线
Redis
订单/财务流水
Segment
微服务/高并发
雪花算法

友情链接:加班费计算器(vx小程序搜索“加班计”)

*源码地址*

私给


如果您喜欢这篇文章,请您(点赞、分享、亮爱心),万分感谢!

http://www.jsqmd.com/news/684075/

相关文章:

  • 换了Homebrew国内源还是装不上Node?可能是你的缓存和源配置在‘打架’
  • 零基础学习C语言:从入门到精通的实用指南
  • 三步解锁QQ音乐加密文件:macOS用户的音频自由指南
  • 流程平台国产替代怎么做,才更像一个技术项目?——从 BPA BPMA BPE BPI 看四层闭环
  • Spring Boot 2.x项目里,Redis突然报`event executor terminated`?别慌,可能是Lettuce连接池配置的锅
  • MATLAB深度学习工具箱:手把手教你调好convolution2dLayer的Padding和Stride,告别输出尺寸的坑
  • 线性判别分析LDA
  • Docker AI工作负载调度失效深度复盘(K8s+Docker+LLM推理协同调度白皮书)
  • 用Python的NumPy和SciPy玩转均匀分布:从骰子模拟到销售预测实战
  • 告别 Add-AppxPackage 部署失败:深入理解 Windows 应用包冲突与资源占用锁
  • STM32寄存器驱动LED流水灯:从仿真到实物的全流程实践
  • 藏在手机里的“城市”:一块电路板是如何运转的?
  • 从振动信号到股票分析:手把手教你用Python的EMD处理非平稳数据(PyEMD实战)
  • AspectJ编译期织入实战
  • YOLO自动标注工具软件
  • 2026 年绍兴养发加盟机构权威排行榜 TOP5(千唯养发居首) - 小艾信息发布
  • MLOps资源管理优化:从GPU虚拟化到智能调度
  • 消息队列消费积压到打爆磁盘:我用Consumer Lag监控+阈值告警在5分钟内止血
  • 别再死记硬背了!用PyTorch手把手带你理解ReLU和Sigmoid激活函数到底在干啥
  • 网络不稳,很多时候不在交换机:通信系统安装的结构逻辑与落地
  • PyTorch计算机视觉深度学习七日速成指南
  • 从‘Invalid HTTP status’到稳定连接:UniApp微信小程序WebSocket实战配置详解
  • Docker构建缓存失效之谜,深度解析.dockerignore误配、时间戳漂移与远程缓存断连的3大隐形杀手
  • 不止STM32F0!国产MM32L073等Cortex-M0芯片IAP中断问题通用解法
  • Reference Extractor终极指南:3分钟从Word文档恢复Zotero和Mendeley引用
  • html怎么部署到服务器_HTML文件如何上传到Nginx或Apache
  • 86253
  • C#构建低延迟AI微服务的最后机会:.NET 11推理加速黄金组合(Span<T>零拷贝+MemoryPool<T>预分配+Custom TensorKernel),仅剩217行核心代码未开源
  • JavaWeb 核心:JavaBean+JSP 动作标签 + EL 表达式全解析
  • FPGA实战:在Vivado里快速搭建一个可配置的偶数分频IP核(附源码)