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

从零起步学习Redis || 第五章:利用Redis构造分布式全局唯一ID

前言:

最近国庆假期,有点放松了,好久没写博客,今天学习了利用Redis构造分布式全局唯一ID,正好整理一下所学内容。

问题分析:

在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识。数据日渐增长,对数据分库分表后需要有一个唯一ID来标识一条数据或消息,数据库的自增ID显然不能满足需求;特别一点的如订单、骑手、优惠券也都需要有唯一ID做标识。此时一个能够生成全局唯一ID的系统是非常必要的。概括下来,那业务系统对ID号的要求有哪些呢?

  1. 全局唯一性: 生成的ID必须在整个系统范围内绝对唯一,不允许出现任何重复。
  2. 写入性能优化(趋势递增): 为了在如MySQL InnoDB(使用B-tree索引)等数据库中-tree索引)等数据库中实现高效的写入性能,ID应具备整体趋势递增的特性(新ID通常比旧ID大),避免插入时索引结构的频繁分裂调整。
  3. 严格顺序性(单调递增): 在需要严格时间或逻辑顺序的场景(如事务版本号、增量消息、排序等),ID必须具备单调递增的特性,确保新生成的ID一定大于之前生成的所有ID。
  4. 信息安全(非连续/无规律): 为了防止恶意用户通过连续ID推测业务量、爬取数据或获取敏感信息(如订单量),ID应设计成非连续、无明显规律、不可预测的形式。

上述123对应三类不同的场景,3和4需求还是互斥的,无法使用同一个方案满足。

方案一:UUID生成

UUID(Universally Unique Identifier)的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的36个字符,示例:550e8400-e29b-41d4-a716-446655440000

优点:

  • 性能非常高:本地生成,没有网络消耗。

缺点:

  • 不易于存储:UUID太长,16字节128位,通常以36长度的字符串表示,很多场景不适用。
  • 信息不安全:基于MAC地址生成UUID的算法可能会造成MAC地址泄露

  • ID作为主键时在特定的环境会存在一些问题,比如做DB主键的场景下,UUID就非常不适用(Mysql中的主键id要求尽量要短)

方案二:利用Redis构造全局唯一ID

由图可看得,我们设计的ID一共分为三部分:

1.符号位:永远为0,标识我们的ID是一个整数。

2.时间戳:从自定义时间开始,按秒计算。那么32位我们大约可以使用60多年。

3.序列号:同一时间内下单进行自增。在时间戳相等的形况下的区分不同的订单。

    public long nextId(String keyPrefix) {//1.生成时间戳LocalDateTime now = LocalDateTime.now();long nowhSecond = now.toEpochSecond(ZoneOffset.UTC);long timestamp = nowhSecond - BEGIN_TIMESTAMP;//2.生成序列号//1.获取日期,精确到天String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));Long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);return (timestamp << COUNT_BITS) | count;}

最后的return为了提高效率,使用了位运算。

优点:

高位为时间,低位为序列,保证整体在递增

以服务的形式生成id,减小对数据库的压力

缺点:

如果机器的时间回拨,可能会导致id出现重复

解决方案:

通过Redis保存最后一次生成ID的逻辑时间戳(而非依赖物理时间),确保逻辑时间只增不减。即使物理时间回拨,逻辑时间仍延续上次的值,从而保证ID的唯一性和有序性。

1. 优化ID结构(必选)

为解决分布式环境下的唯一性(不同机器可能生成相同时间戳+序列号),需在ID中增加机器ID(或数据中心ID)。推荐结构(64位Long类型,兼容Java的long):

符号位(1位) + 时间戳(41位) + 机器ID(10位) + 序列号(12位)

  • 符号位:固定为0(保证ID为正数);
  • 时间戳:相对于某个epoch(如2020-01-01)的毫秒数,可使用约69年;
  • 机器ID:每个实例唯一(通过配置文件/环境变量/ZooKeeper获取);
  • 序列号:每毫秒内的自增值(0-4095,支持每秒409.6万次ID生成)。

2. 关键组件:Redis存储逻辑时间

用Redis的String类型存储两个关键值:

  • last_timestamp:最后一次生成ID的逻辑时间戳(毫秒);
  • sequence:当前毫秒内的序列号(0-4095)。

通过Lua脚本保证这两个值的原子更新(避免并发 race condition)。

3. 具体实现步骤(Java+Redis)
(1)Lua脚本:原子生成逻辑时间戳和序列号:

Lua脚本是Redis原子操作的核心,用于处理时间回拨、序列号溢出等场景。

(2)Java客户端实现

通过Redisson或Jedis调用Lua脚本,处理返回结果,构造最终ID。

  • 逻辑时间戳:Redis中保存的last_timestamp逻辑时间,仅增不减。即使物理时间回拨,逻辑时间仍延续上次的值,保证ID的有序性。
  • 时间回拨处理
    • 回拨未超过阈值(如1秒):使用逻辑时间继续生成ID,序列号递增。若序列号溢出,则递增逻辑时间(如last_timestamp+1),重置序列号。
    • 回拨超过阈值:拒绝生成ID,抛出异常(需报警通知管理员处理,如系统时间被恶意修改)。

由于该项方案技术实现较为复杂,我这里没有实现

方案三:Leaf-segment方案

批量获取ID进行处理。在上文我们简单的对数据库进行优化的时候,优化问题基本都来源于高并发下数据库高频的读写操作。而LEAF数据库方案也是针对这个方面进行优化的

我们可以把图中的leaf简单的理解为是一个生成全局唯一ID的服务。那么整个LEAF数据库的思想就是:leaf服务提前就拿好一批号端,例如从0-1000。那么我在生成唯一ID的时候,压力就从数据库转到了Leaf这个服务里面。

优点:

将生成id的功能作为服务,方便后续扩展和维护

容灾性强,即使数据库挂掉,服务中因为保存了一部分号段,短时间内仍可进行服务

满足递增要求

缺点:

在号段用完后,突然出现大量请求,服务器压力增大

解决方案:双buffer优化

Leaf——美团点评分布式ID生成系统 - 美团技术团队

我们并不会等到号段全部用完之后再去请求新的号段。美团给出的技术方案是当号段消费到某个点时就异步的把下一个号段加载到内存中。而不需要等到号段用尽的时候才去更新号段。

一开始先用A号段,等 A号段消耗10%的时候,就向数据库请求新号段。之后当前号段消耗完之后就可以进行快速的切换。如此循环往复。

总结:

三个方案各有千秋,大家可以根据结合自身项目的情况选择合适的方案为项目赋能

http://www.dtcms.com/a/434429.html

相关文章:

  • C++基础语法核心技术详解
  • 临沂网站建设报价手机百度免费下载
  • 打工人日报#20251002
  • GMSL Layout Guide及其解读
  • ThinkPad X1 Carbon Gen13,X1 2in1 Gen10(21NX,21NY,21Q0,21Q1)原厂Win11Home系统
  • 设置VLC播放器的皮肤样式
  • 外贸网站搭建网站优化过度被k
  • 【工业实战】从架构到优化:企业级RAG客服对话系统的构建之道
  • CMake 入门实战手册:从理解原理开始,打造高效 C/C++ 开发流程
  • MySQL 5.7 主主复制 + Keepalived 高可用配置实例
  • 2014 年真题配套词汇单词笔记(考研真相)
  • 构建AI智能体:五十一、深思熟虑智能体:从BDI架构到认知推理的完整流程体系
  • 自由学习记录(104)
  • 【开题答辩全过程】以 ssm蛋糕销售网站的设计与实现为例,包含答辩的问题和答案
  • Photoshop - Photoshop 工具从工具栏消失
  • 专题网站建设策划dw一个完整网页的代码
  • 刷赞网站推广免费链接网站后台怎么添加栏目
  • LLM 笔记 —— 01 大型语言模型修炼史(Self-supervised Learning、Supervised Learning、RLHF)
  • 框架系统在自然语言处理深度语义分析中的作用、挑战与未来展望
  • LLM 笔记 —— 03 大语言模型安全性评定
  • d-分离:图模型中的条件独立性判定准则
  • 【自然语言处理】文本规范化知识点梳理与习题总结
  • 上海商城网站建设公司算命手机网站开发
  • 重塑Excel的智慧边界:ExcelAgentTemplate架构深度解析与LLM集成最佳实践
  • QoS之拥塞避免配置方法
  • vscode搭建C/C++配置开发环境
  • 在鸿蒙NEXT中发起HTTP网络请求:从入门到精通
  • 做网站商家网站公告栏代码
  • 做企业网站联系群晖网站建设
  • Java坐标转换的多元实现路径:在线调用、百度与高德地图API集成及纯Java代码实现——纯Java代码实现与数学模型深度剖析