【后端开发】(图解/真实场景)自增ID、UUID、雪花算法,业务主键到底该怎么选?

news2026/4/28 8:39:18
文章目录前言1 先说清楚主键 ID 和业务编号别混着用2 自增 ID简单好用但不要无脑用3 UUID全局唯一但不一定适合当数据库主键4 雪花算法更适合分布式业务主键但也有坑写在文后 个人主页铁皮哥欢迎关注 作者简介28届校招生后端开发/Agent 方向在学 学习内容Java、Python、计算机视觉、大语言模型、Agent开发 专栏内容从零开始的Claude Code零代码生活持续更新中✨不只背八股更想搞懂为什么这样设计前言刚开始做后端项目的时候我对数据库主键的理解很简单建表的时候加一个id类型用BIGINT再配上AUTO_INCREMENT基本就完事了。说实话在很多小项目里这样写确实没什么问题。比如一个后台管理系统、一个课程表、一个简单的用户收藏表自增 ID 简单、直观、好查甚至还挺舒服。但后来我发现主键 ID 这个东西一旦放到稍微复杂一点的业务里就没那么“无脑”了。比如订单系统里多个服务同时写入数据比如业务做大之后开始分库分表再比如新老系统迁移时需要把两边的数据合并到一起。这时候如果还直接依赖数据库自增 ID就可能遇到一些很尴尬的问题不同库里的 ID 撞了历史数据导入失败或者业务编号直接暴露给用户之后被人猜出系统的数据量。更麻烦的是这类问题平时不一定能看出来。项目刚上线时一张表几千条数据自增 ID 看起来非常稳定等到数据量上来、系统开始拆分、业务需要迁移时之前图省事留下的设计就可能变成后面补坑的成本。所以这篇文章不打算单纯罗列“自增 ID、UUID、雪花算法分别是什么”。这种内容网上已经很多了看完也不一定知道自己项目里该怎么选。我更想从真实业务场景出发聊聊几个问题数据库主键 ID 和业务编号到底是不是一回事为什么自增 ID 在单体项目里很好用但到了分布式场景就容易出问题UUID明明全局唯一为什么很多时候又不适合直接做数据库主键雪花算法看起来很完美但它又有哪些隐藏的坑最后再结合用户表、订单表、文件表、后台配置表这些常见场景给出一个更接近实际开发的选型思路。毕竟主键 ID 的选择不是为了炫技而是为了让系统在数据变多、业务变复杂之后依然能稳定地跑下去。1 先说清楚主键 ID 和业务编号别混着用在正式聊自增 ID、UUID、雪花算法之前我觉得有一个概念要先分清楚数据库主键 ID 和业务编号不是一回事。这个问题看起来很基础但在实际开发里还挺容易混着用。比如订单表里有一个id同时又有一个order_no刚开始可能会觉得这两个字段都能唯一标识一笔订单那是不是留一个就够了一般不建议这么做。**数据库主键 ID 更多是给系统内部用的。**它的核心作用是让数据库能够快速定位一行数据方便建索引、做关联、查详情。比如订单表里的主键可能长这样idBIGINTPRIMARYKEY它不一定需要有业务含义也不一定需要让用户看懂。对数据库来说只要它唯一、稳定、索引效率高就已经完成任务了。但业务编号不一样。业务编号通常是给用户、客服、运营或者外部系统看的。比如数据库主键 ID912837465123 订单业务编号202604241030001928用户查订单的时候不太可能拿着数据库主键去查客服排查问题时也更希望看到一个有规则、能定位时间和业务线的订单号。业务编号往往会带一些业务含义比如日期、渠道、类型、随机码、校验位等。如果把主键 ID 和业务编号混在一起前期确实省事但后面容易出问题。最常见的一个问题是自增 ID 直接暴露给前端会泄露业务规模。比如用户看到自己的订单地址是/order/10086 /order/10087 /order/10088那他大概能猜出系统目前有多少订单。对于一些业务来说这个信息本身就不应该暴露。还有一个更麻烦的问题是主键一旦和业务强绑定后续改造成本会变高。比如一开始用自增 ID 当订单号后来业务做大了要接入新系统、合并历史订单、或者做分库分表。这时候原来的自增 ID 可能已经出现在接口、日志、导出文件、客服后台甚至第三方系统里了。你想换一套 ID 规则就不是改一张表那么简单而是很多上下游都要跟着动。所以更稳一点的做法是主键 ID 只负责系统内部唯一标识业务编号单独设计。比如订单表可以这样设计CREATETABLEorders(idBIGINTPRIMARYKEY,order_noVARCHAR(64)NOTNULLUNIQUE,user_idBIGINTNOTNULL,amountDECIMAL(10,2)NOTNULL,created_atDATETIMENOTNULL);这里的id是数据库主键主要用于表关联和内部查询order_no是业务订单号用于对外展示、客服查询、支付对账、导入导出。这样设计的好处是两者可以各自优化。主键 ID 可以选择更适合数据库的方案比如自增 ID 或雪花 ID业务编号则可以根据业务规则生成比如按日期、渠道、随机数、校验位组合。以后即使主键生成策略调整了也不会直接影响用户看到的订单号。所以后面讨论自增 ID、UUID、雪花算法时默认讨论的是“数据库主键 ID 怎么选”。至于订单号、流水号、用户编号这类业务可见编号最好单独设计不要直接偷懒拿主键来用。2 自增 ID简单好用但不要无脑用自增 ID 应该是很多人最早接触到的主键方案。建表时写一个AUTO_INCREMENT插入数据时不用自己管 ID数据库会自动帮我们生成一个递增的数字。对于刚开始做项目的人来说这套机制非常顺手。CREATETABLEuser_info(idBIGINTPRIMARYKEYAUTO_INCREMENT,usernameVARCHAR(64)NOTNULL,created_atDATETIMENOTNULL);插入数据时也很简单INSERTINTOuser_info(username,created_at)VALUES(zhangsan,NOW());这条数据插入成功后数据库会自动生成一个新的id。在单体项目里自增 ID 确实很好用。它是递增的对数据库索引比较友好它是数字类型占用空间也不大排查问题时也直观看一眼 ID 大小大概能知道数据创建的先后顺序。所以我不太认同那种一上来就说“不要用自增 ID”的说法。自增 ID 本身没有问题问题在于很多项目一开始图方便把它用在了不该用的地方。比如后台管理系统里的菜单表、角色表、字典表、配置表用自增 ID 完全可以。这些表一般有几个特点数据量不大写入频率不高基本不会分库分表也很少会被多个系统合并。对于这类表来说设计一个复杂的分布式 ID 反而有点过度设计。真正需要注意的是那些核心业务表。比如订单表、支付流水表、用户行为表、消息表。它们的数据量会持续增长写入频率也更高而且后面很可能遇到分库分表、数据迁移、多系统合并这些问题。这个时候再用数据库自增 ID就容易留下隐患。一个比较典型的场景是数据迁移。假设老系统里有一张订单表id 1001order_no A001 id 1002order_no A002新系统里也有一张订单表id 1001order_no B001 id 1002order_no B002单独看两个系统都没问题因为它们各自的数据库里id都是唯一的。但如果某一天要把两边的数据合并到同一张表里问题就来了两个系统都有id 1001到底保留谁如果这张订单表还被其他表通过order_id关联那就更麻烦了关联关系也要跟着改。这就是自增 ID 的一个典型问题它只能保证在当前数据库实例里递增唯一不天然具备全局唯一性。分库分表时也类似。如果订单表按照用户维度拆成多个库每个库里的订单 ID 都从 1 开始递增那么不同库之间就可能出现相同的 ID。order_db_01.ordersid 9527 order_db_02.ordersid 9527如果系统内部永远都带着库表路由信息查询也许还能工作。但一旦做日志分析、数据同步、离线数仓、客服查询单独一个id 9527就不再能唯一定位一笔订单。还有一个经常被提到的问题是高并发写入。这里要稍微说清楚一点不是只要用了自增 ID高并发下一定会崩。MySQL 对自增值的生成有自己的优化普通业务量下基本不用太担心。但如果是高频插入的核心表尤其是批量写入、分布式写入、分库分表之后还想保持全局递增 ID自增 ID 就没那么合适了。因为 ID 的生成依赖数据库本身写入压力越集中数据库承担的协调成本就越高。更现实的问题是一旦 ID 生成能力绑定在单个数据库上系统扩展时就会比较别扭。业务可以拆服务流量可以加机器但 ID 还是要从数据库这里拿。短期看不明显长期看就会成为架构上的限制。所以自增 ID 的使用边界其实很清楚。如果是一张内部表数据量不大也没有跨库合并和频繁迁移的需求自增 ID 依然是一个很好的选择。比如后台配置表、权限菜单表、字典表用它简单、省事、稳定。但如果是一张核心业务表未来可能高并发写入、可能分库分表、可能做数据迁移或者这个 ID 会出现在多个系统之间那就不要轻易把数据库自增 ID 当成最终方案。自增 ID 不是不能用而是不要让它承担“全局唯一 ID”这种它本来就不擅长的职责。3 UUID全局唯一但不一定适合当数据库主键如果说自增 ID 的问题是“不够全局”那 UUID 解决的就是这个问题。UUID 生成出来通常长这样550e8400-e29b-41d4-a716-446655440000它最大的特点是不需要依赖数据库也不需要中心化的 ID 服务。只要在代码里调用对应的方法就可以生成一个几乎不会重复的字符串。比如在 Java 里生成 UUIDStringidUUID.randomUUID().toString();这种方式用起来非常方便。服务 A 可以生成服务 B 可以生成客户端甚至离线状态下也可以生成。等数据最后汇总到一起时也不太容易出现 ID 冲突。这就是 UUID 相比自增 ID 更舒服的地方。假设两个系统都要导入历史数据如果它们用的是自增 ID就很容易出现前面提到的id 1001冲突问题。但如果它们一开始用的是 UUID两个系统生成出相同 ID 的概率就非常低合并数据时会省掉很多麻烦。所以在一些跨系统、跨服务、离线生成的场景里UUID 很有优势。但问题也在这里UUID 解决的是“全局唯一”问题不代表它天然适合做所有表的数据库主键。首先UUID 太长。一个常见 UUID 字符串有 36 个字符即使用去掉横杠的形式也有 32 个字符。相比之下BIGINT只占 8 个字节。如果一张表只有几千条数据这点差异可能没什么感觉。但如果是一张订单表、消息表、日志表数据量到几千万甚至上亿主键占用空间、二级索引占用空间、缓存命中率都会受到影响。更关键的是UUID 通常是无序的。MySQL 的 InnoDB 使用 B 树组织索引。如果主键是自增 ID新数据基本会插入到索引的末尾整体比较顺滑。但如果主键是随机 UUID新数据可能插入到 B 树的任意位置就容易造成页分裂、数据页移动写入成本会变高。简单理解就是自增 ID 1 - 2 - 3 - 4 - 5 数据基本按顺序往后追加 随机 UUID a8f3... - 19bd... - ff02... - 7c91... 每次插入位置都可能不一样对于高频写入的核心业务表来说这个差异就不能忽略了。还有一点是UUID 对人不友好。排查问题时一个982f3c1e-8b3a-4c5d-91ab-2fd9e7f63a10肯定没有一个递增数字好看。日志里出现大量 UUID也会让定位问题变得更费眼睛。当然这不是说 UUID 不好。它只是更适合放在它擅长的地方。比如文件资源 ID、上传凭证、幂等 token、trace_id、外部系统对接标识这些场景往往更关注“全局唯一”和“生成方便”而不是数据库聚簇索引的写入效率。像一次请求链路里的trace_id用 UUID 就很自然trace_id 8fd2e7b4-2c91-4fd7-9a0e-8b1c7a129001它不需要参与复杂的数据库排序也不需要作为高频写入表的聚簇主键。它只要能唯一标识一次请求方便日志串联就可以了。再比如文件上传file_id 3f7e1f92a6df4a9c8f1029384756abcd这个 ID 可能会出现在对象存储、数据库记录、业务系统之间。用 UUID 可以减少不同系统之间的冲突风险。所以我对 UUID 的理解是它很适合做“全局唯一标识”但不一定适合直接做高频业务表的数据库主键。如果是一张写入量不大的表或者这个 ID 更多用于外部系统对接UUID 没问题。但如果是订单表、支付流水表、消息表这种高并发写入的核心表直接用随机 UUID 做主键就要认真考虑索引空间和写入性能的问题了。4 雪花算法更适合分布式业务主键但也有坑如果说自增 ID 太“本地”UUID 又太“随机”那雪花算法Snowflake可以理解为一个折中的方案既能做到分布式下的全局唯一又尽量保持有序。很多公司在做订单表、支付流水表、消息表时最终都会落到类似雪花算法的方案上。先简单看一下它的结构大致可以理解成这样符号位 | 时间戳 | 机器ID | 序列号生成出来是一个Long类型的数字比如1829384756102938475这个数字看起来和普通的自增 ID 很像但它其实包含了时间信息、机器信息以及同一时刻的序列号。一个直观的好处是它不依赖数据库。每个服务节点都可以自己生成 ID不需要去数据库拿号也不需要中心化服务协调。只要机器 ID 不冲突同一时间内的序列号不重复就可以保证整体唯一。这在分布式系统里非常关键。比如订单服务有多台机器同时写入数据如果用数据库自增 ID就意味着所有写请求最终要集中到数据库去分配 ID但用雪花算法每台机器可以本地生成 ID写入压力就更容易横向扩展。另外一点是它大致有序。虽然不是严格的连续递增但因为高位是时间戳整体趋势是往上走的。这一点比 UUID 要友好很多用作数据库主键时对 B 树索引的影响也更可控。所以你会看到很多实际项目里订单表主键用雪花 ID支付流水表主键用雪花 ID消息表、日志表也用雪花 ID基本都是冲着“分布式 高并发 相对有序”这几个特点来的。不过雪花算法也不是“银弹”它也有一些比较容易被忽略的坑。第一个是时钟问题。雪花算法依赖当前机器时间来生成 ID如果服务器时间发生回拨比如手动调整时间、NTP 同步异常就可能出现新生成的 ID 比之前还小极端情况下甚至会重复。这个问题在开发环境里不太容易遇到但在线上如果不处理风险是存在的。常见的做法是检测到时间回拨时暂时阻塞、或者切换备用序列策略但实现上会稍微复杂一点。第二个是机器 ID 的分配。雪花算法里通常会预留一部分位来表示“当前机器是谁”。如果多个节点拿到了同一个机器 ID就可能在同一时间生成相同的序列号最终导致 ID 冲突。在小规模部署时可以手动配置但机器数量一多就需要一套统一的分配策略比如通过配置中心、数据库、或者注册中心来管理。第三个问题是很多人一开始没注意到的ID 本身是有“信息”的。因为雪花 ID 里包含时间戳如果你直接把它当业务编号暴露给用户有经验的人是可以大致推算出订单生成时间甚至业务增长趋势的。所以在设计上一般会把雪花 ID 当作数据库主键使用而对外展示的订单号、流水号还是单独生成一套规则。实际使用时一个比较稳妥的做法是数据库主键用雪花 ID保证分布式下的唯一性和写入性能业务侧如果需要“可读”“有规则”的编号再单独设计一套生成逻辑比如拼接日期、业务类型、随机数等。这样两者各司其职后续无论是扩容、迁移还是系统拆分都更容易调整。从工程角度看雪花算法之所以被广泛使用不是因为它“最完美”而是因为它在大多数分布式业务里刚好把几个关键点平衡住了不依赖数据库、性能可控、基本有序、实现复杂度也还可以接受。但也正因为它不是完全无脑的方案像时间回拨、机器 ID 管理这些细节如果忽略了反而会在系统跑一段时间之后才暴露出来。所以用雪花 ID 的时候真正需要关注的不只是“用不用”而是“怎么用得稳”。写在文后期待您的一键三连如果有什么问题或建议欢迎在评论区交流

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2562191.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…