场景应用:id全局唯一且自增,如何实现?

简介: 场景应用:id全局唯一且自增,如何实现?

场景应用:id全局唯一且自增,如何实现?


文章目录

id全局唯一且自增,如何实现?

系统唯一id是我们在设计阶段常常遇到的问题。在复杂的分布式系统中,几乎都需要对大量的数据和消息进行唯一标识。在设计初期,我们需要考虑日后数据量的级别,如果可能会对数据进行分库分表,那么就需要有一个全局唯一id来标识一条数据或记录。生成唯一id的策略有多种,但是每种策略都有它的适用场景、优点以及局限性。

需求特点

id一般是数据库的主键,数据库上会建立聚集索引,即在物理存储上以这个字段排序。这个记录标识上的查询往往又有分页或者排序的业务需求,如果再增加一个time字段以其建立普通索引则访问效率低(因为普通索引存储的是实际记录的指针)。如果ID在生成时就能基本保证时间有序,则可以省去这个字段。

故这个ID一般具有以下特点:

  • 全局唯一性:不能出现重复的ID号,既然是唯一标识,这是最基本的要求
  • 趋势递增:在MySQL的InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B-tree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能
  • 单调递增:保证下一个ID一定大于上一个ID,例如事务版本号、IM增量消息、排序等特殊需求
  • 信息安全:如果ID是连续的,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定URL即可;如果是订单号就更危险了,竞对可以直接知道我们一天的单量。所以在一些应用场景下需要ID无规则
  • 高可用性:同时除了对ID号码自身的要求,业务还对ID号生成系统的可用性要求极高,想象一下,如果ID生成系统瘫痪,这就会带来一场灾难。所以不能有单点故障
  • 分片支持:可以控制ShardingId。比如某一个用户的文章要放在同一个分片内,这样查询效率高,修改也容易
  • 长度适中

实现方案

1、数据库的 auto_increment

这个不用多说,使用数据库自带的字段自增功能

优缺点

优点

  • 非常简单。利用现有数据库系统的功能实现,成本小,代码简单,性能可以接受。ID号单调递增。可以实现一些对ID有特殊要求的业务,比如对分页或者排序结果这类需求有帮助

缺点

  • 强依赖DB。不同数据库语法和实现不同,数据库迁移的时候、多数据库版本支持的时候、或分表分库的时候需要处理,会比较麻烦。当DB异常时整个系统不可用,属于致命问题。
  • 单点故障。在单个数据库或读写分离或一主多从的情况下,只有一个主库可以生成。有单点故障的风险。
  • 数据一致性问题。配置主从复制可以尽可能的增加可用性,但是数据一致性在特殊情况下难以保证。主从切换时的不一致可能会导致重复发号。
  • 难于扩展。在性能达不到要求的情况下,比较难于扩展。ID发号性能瓶颈限制在单台MySQL的读写性能。

进行优化:分库分表

冗余主库,避免写入单点数据水平切分,保证各主库生成的ID不重复(由1个写库变成3个写库,每个写库设置不同的 auto_increment 初始值,以及相同的增长步长)。

改进后的架构保证了可用性但数据库的写压力依然很大,每次生成ID都要访问数据库,而且丧失了ID生成的“绝对递增性”:先访问DB 01生成0,3,再访问DB 02生成1,可能导致在非常短的时间内,ID生成不是绝对递增的(这个问题不大,目标是趋势递增,不是绝对递增)

2、UUID

uuid是一种常见的本地生成ID的方法,利用程序生成,减少耗时。(不管是通过数据库,还是通过服务来生成ID,业务方Application都需要进行一次远程调用,比较耗时)

UUID () 的目的,是让分布式系统中的所有元素,都能有唯一的辨识资讯,而不需要透过中央控制端来做辨识资讯的指定。如此一来,每个人都可以建立不与其它人冲突的 UUID。在这样的情况下,就不需考虑数据库建立时的名称重复问题。

生成规则

UUID的标准形式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的36个字符,示例:550e8400-e29b-41d4-a716-446655440000,到目前为止业界一共有5种方式生成UUID。

例如 Java中:

UUID uuid = UUID.randomUUID();
System.out.println(uuid);

运行结果如下:

优缺点

优点

  • 非常简单,本地生成,代码方便,API调用方便。
  • 性能高。生成的id性能非常好,没有网络消耗,基本不会有性能问题。
  • 全球唯一。在数据库迁移、系统数据合并、或者数据库变更的情况下,可以 从容应对。

缺点

  • 存储成本高:UUID太长,16字节128位,通常以36长度的字符串表示,很多场景不适用。如果是海量数据库,就需要考虑存储量的问题。
  • 信息不安全:基于MAC地址生成UUID的算法可能会造成MAC地址泄露,这个漏洞曾被用于寻找梅丽莎病毒的制作者位置。
    不适用作为主键:ID作为主键时在特定的环境会存在一些问题,比如做DB主键的场景下,UUID就非常不适用。UUID往往是使用字符串存储,查询的效率比较低。
  • UUID是无序的:不是单调递增的,而现阶段主流的数据库主键索引都是选用的B+树索引,对于无序长度过长的主键插入效率比较低。
  • 传输数据量大
  • 不可读

进行优化:

为了解决UUID不可读, 可以使用UUID to Int64的方法 。

为了解决UUID无序的问题, NHibernate在其主键生成方式中提供了Comb算法(combined guid/timestamp)。保留GUID的10个字节,用另6个字节表示GUID生成的时间(DateTime)。

3、雪花(SnowFlake)算法

雪花ID生成的是一个64位的二进制正整数,然后转换成10进制的数。64位二进制数由如下部分组成:

snowflflake id生成规则

成员部分 说明
1位标识符 始终是0,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0。
41位时间戳 41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截 )得到的值,这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的。
10位机器标识码 可以部署在1024个节点,如果机器分机房(IDC)部署,这10位可以由 5位机房ID + 5位机器ID 组成。
12位序列 毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号

优缺点

优点

  • 简单高效,生成速度快。
  • 时间戳在高位,自增序列在低位,整个ID是趋势递增的,按照时间有序递增。
  • 灵活度高,可以根据业务需求,调整bit位的划分,满足不同的需求。

缺点

  • 依赖机器的时钟,如果服务器时钟回拨,会导致重复ID生成。
  • 在分布式环境上,每个服务器的时钟不可能完全同步,有时会出现不是全局递增的情况。


相关文章
|
7月前
|
缓存 监控 网络协议
【免费开源】基于 STM32 的远程视频监控项目 —— 从无线通信选型到系统架构实战解析
本项目基于STM32实现低成本、低功耗远程视频监控,融合DCMI图像采集、JPEG硬件压缩、Wi-Fi/4G无线传输、MQTT/TCP协议及云端展示。涵盖硬件选型、通信方案对比(推荐Wi-Fi局域网/4G DTU野外)、内存优化与稳定性设计,适配农业、工业、实验室等IoT场景,提供完整开源实战方案。
【免费开源】基于 STM32 的远程视频监控项目 —— 从无线通信选型到系统架构实战解析
|
人工智能 并行计算 开发者
CUDA重大更新:原生Python可直接编写高性能GPU程序
NVIDIA在2025年GTC大会上宣布CUDA并行计算平台正式支持原生Python编程,消除了Python开发者进入GPU加速领域的技术壁垒。这一突破通过重新设计CUDA开发模型,引入CUDA Core、cuPyNumeric、NVMath Python等核心组件,实现了Python与GPU加速的深度集成。开发者可直接用Python语法进行高性能并行计算,显著降低门槛,扩展CUDA生态,推动人工智能、科学计算等领域创新。此更新标志着CUDA向更包容的语言生态系统转型,未来还将支持Rust、Julia等语言。
943 3
CUDA重大更新:原生Python可直接编写高性能GPU程序
|
缓存 关系型数据库 MySQL
解决CentOS执行yum命令出现File contains parsing errors:file:///etc/yum.repos.d/CentOS-Base.repo
解决CentOS执行yum命令出现File contains parsing errors:file:///etc/yum.repos.d/CentOS-Base.repo
解决CentOS执行yum命令出现File contains parsing errors:file:///etc/yum.repos.d/CentOS-Base.repo
|
存储 NoSQL 算法
分布式唯一 ID 的 7 种生成方案
在互联网的业务系统中,涉及到各种各样的ID,如在支付系统中就会有支付ID、退款ID等。那一般生成ID都有哪些解决方案呢?特别是在复杂的分布式系统业务场景中,我们应该采用哪种适合自己的解决方案是十分重要的。下面我们一一来列举一下,不一定全部适合,这些解决方案仅供你参考,或许对你有用。
分布式唯一 ID 的 7 种生成方案
|
运维 Kubernetes Cloud Native
Kubernetes云原生架构深度解析与实践指南####
本文深入探讨了Kubernetes作为领先的云原生应用编排平台,其设计理念、核心组件及高级特性。通过剖析Kubernetes的工作原理,结合具体案例分析,为读者呈现如何在实际项目中高效部署、管理和扩展容器化应用的策略与技巧。文章还涵盖了服务发现、负载均衡、配置管理、自动化伸缩等关键议题,旨在帮助开发者和运维人员掌握利用Kubernetes构建健壮、可伸缩的云原生生态系统的能力。 ####
简单好用的图片取色器【可取RGB数值】
这篇文章介绍了如何使用Snipaste工具进行截图和取色,包括按下快捷键F1截图、选择图片区域、移动鼠标取色以及复制颜色值的步骤,并提供了操作界面的截图。
六个好看实用的html简单登录页面源码
六个好看实用的html简单登录页面源码
2290 0
六个好看实用的html简单登录页面源码
|
前端开发
前端常用方法防抖(debounce)和节流(throttle)的示例演示及应用场景说明
前端常用方法防抖(debounce)和节流(throttle)的示例演示及应用场景说明
849 0
|
SQL NoSQL 前端开发
大厂如何解决订单幂等问题
本文探讨了分布式系统中接口幂等性的重要性和实现方法,特别是在防止重复下单的场景中。首先介绍了通过数据库事务处理创建订单时的原子性需求。接着分析了服务间调用时可能遇到的重复请求问题,提出每个请求需具备唯一标识,并记录处理状态以识别并阻止重复操作。具体实践包括生成全局唯一的订单ID,利用数据库主键唯一性约束来防止重复插入,以及使用Redis存储订单支付状态。此外,文章还讨论了解决ABA问题(即数据在两次检查之间被修改的问题)的方法,引入版本号机制来确保数据更新的原子性和一致性。这些技术方案不仅限于订单服务,也可广泛应用于需要实现幂等性的其他业务场景中。