
如果你在一线写过接口、搬过数据库、或者折腾过Linux服务器UUID这个名字你大概率不陌生。它是一个长度为36个字符的字符串看起来像是一堆乱码但几乎所有涉及身份标识的系统里都有它的身影。UUID通用唯一识别码的全称是Universally Unique Identifier目的就是在分布式环境下生成全局唯一的标识不需要中心节点分配也不需要重复校验。这一篇我从原理讲到实际使用结合Excel批量生成、Linux下查看U盘UUID、Windows 11下获取主板UUID这些高频场景把实用细节一次性说透。不管你是后端开发、运维还是经常跟硬件、脚本打交道的办公族这篇文章都适合你。我会把版本选择、存储格式、常见坑点都掰开揉碎地讲保证你看完能直接用上。1. UUID到底是个什么东西1.1 为什么是一串36个字符的乱码UUID的标准格式是8-4-4-4-12总共32个十六进制字符加上4个连字符代表128位数据。比如这个例子550e8400-e29b-41d4-a716-446655440000这128位里不是随便填的。根据不同的生成版本某些位会固定代表版本号和变体号剩下的位才填充时间戳、随机数或者 MAC 地址。格式里的连字符其实只是为了人类阅读方便机器存储时完全可以去掉。拿版本4来拆解一下这是目前最常用的一种第1段8个字符来自随机数第2段4个字符来自随机数第3段开头第一个字符固定为4代表版本号第4段开头两个字符的取值在 80、90、A0、B0 之间代表变体最后一段12个字符来自随机数也就是说一个版本4的UUID大约有122位随机信息。这意味着什么如果你每纳秒生成十亿个UUID持续一百亿年重复的概率也才到 50% 左右。这个数字基本可以让你忘掉“冲突”这两个字日常业务根本不用额外做唯一性校验。想查看某个UUID的版本可以看第3段的第一位数字。如果是4就是随机生成如果是1就是基于时间戳和MAC地址如果是7则是新版的时间有序UUID。这个细节在排查问题时很管用先学会看版本号后面很多判断就快了。1.2 四个常用版本怎么挑UUID一共定义了5个版本实际用得最多的是1、3、4、5这四种。每个版本的生成逻辑和适用场景差别很大选错了虽然也能跑但在隐私、性能和可排查性上会埋坑。版本1用当前时间戳加上节点的MAC地址生成。优点是生成速度极快而且同一台机器上按时间大致有序。缺点是MAC地址是硬件指纹泄露出去别人能追踪到你的机器。在一个不信任的网络环境里暴露版本1的UUID等于把你的网卡地址告诉了全世界。另外时间戳回拨会导致重复这个在分布式环境下尤其要小心。版本3和版本5都是基于命名空间的哈希方案区别在于哈希算法前者用MD5后者用SHA-1。给相同的输入比如同一域名、同一用户ID永远得到同一个UUID。这种特性适合需要确定性映射的场景比如把URL、订单号映射成固定ID不需要存储映射关系随时都能算出来。版本4是纯随机生成也是我日常用得最多的一个。没有隐私问题不需要协调时间戳生成逻辑简单各语言标准库直接调用。缺点是完全无序如果当数据库主键用会引发索引页频繁分裂后面我单独展开讲。版本7是2021年新提出的草案把时间戳和高低位随机数结合既保证了全局唯一又让生成的ID在时间上有序。很多新项目已经开始转向版本7因为它兼顾了版本1的有序性和版本4的匿名性。如果你的技术栈允许新项目建议优先用版本7。2. 分布式环境下的UUID为什么它成了标配2.1 自增ID的痛UUID怎么解在单体应用时代自增ID是最省事的方案。数据库帮你维护一个计数器每次INSERT就自动加一查询走索引也快日志里看数字也直观。但一旦拆成多库多表自增ID的问题就来了。最常见的是统一发号服务比如Redis的INCR命令或者单独部署一套ID生成器。这套方案的问题是引入了额外的网络依赖每次生成ID都要走一次网络请求延迟高不说发号服务一旦挂了整个写入链路就断了。更麻烦的是ID总量的预估一套发号器能撑多少并发、能发多少年都得提前算清楚。UUID天然规避了这些问题。它不需要询问任何中心节点每个服务实例在本地就能独立生成靠概率保证全局唯一。生成开销只有内存里的一段计算性能极高也没有网络故障点。在一个微服务架构里每个服务自己管自己的ID彼此之间不需要协调系统整体的可用性反而提升了。我印象很深的一个项目早期用自增ID做主键后来数据量上来做了分库分表原本简单的ID一下子变得复杂起来。既要保证全局唯一又要保证不冲突最后改造成基于时间戳加机器号加序号的复合ID相当于自己实现了一套简化版雪花算法。如果一开始就用UUID这套改造成本完全可以省掉。2.2 UUID和雪花ID怎么选在分布式ID这个赛道上除了UUID还有一票基于雪花算法的方案。雪花ID是Twitter开源的思路64位整数前41位时间戳、中间10位机器标识、后12位序列号。它比UUID短得多只有19位数字存储空间小而且是趋势递增的对数据库索引非常友好。那是不是说雪花ID一定比UUID好不是的看场景。雪花ID依赖机器ID的配置和时钟同步。机器ID分配错了或者服务器时钟发生回拨就会出现ID重复。时钟回拨在虚拟机迁移、NTP校时不当的时候很容易触发处理起来还挺麻烦。UUID版本4没有这些外部依赖对运行环境的要求几乎为零。数据量级上雪花ID适合单表数据量极大、对写入性能极其敏感的场景比如订单表、流水表。UUID版本4虽然也够快但完全无序导致索引碎片化写入吞吐在高并发下会明显下降。 我的建议是如果项目还在早期团队规模不大优先选UUID版本4或者版本7省心如果数据量已经到了亿级别或者你明确知道要深度依赖数据库索引顺序扫描那再考虑雪花ID方案同时要做好时钟回拨的预案。分段批量插入的场景还要额外注意一点如果一次插入几万条数据UUID的随机性反而成了优势因为它能均匀分散到各个分片不会像自增ID那样集中在某个分片的热点区间导致单一分片压力过大。这也是为什么很多分库分表方案最终选了UUID或类似的无序ID而不只是看索引性能。3. 三个最常用的UUID实操场景3.1 在Excel里批量写UUID很多做运营、做测试的朋友会遇到一个需求在Excel里生成一批UUID用来做数据导入、批量造数或者接口联调。不要为了这个需求专门去装编程环境Excel自带的函数就能搞定。最简单的方式是用RANDARRAY配合TEXT格式化。但Excel没有内置现成的UUID函数老老实实用公式组合也能做。下面这个方法是网上用的较多的经典写法基于RAND()的随机数生成LOWER(CONCATENATE( DEC2HEX(RANDBETWEEN(0, 2147483647), 8), -, DEC2HEX(RANDBETWEEN(0, 2147483647), 4), -, 4, DEC2HEX(RANDBETWEEN(0, 4095), 3), -, DEC2HEX(RANDBETWEEN(32768, 65535), 4), -, DEC2HEX(RANDBETWEEN(0, 2147483647), 8), DEC2HEX(RANDBETWEEN(0, 2147483647), 4) ))注意几个细节。第三段我们手动加了一个4就是为了把版本号固定成4。第四段用RANDBETWEEN(32768, 65535)转成十六进制后第一位数字必然落在8到F之间这正好符合UUID变体规范。虽然Excel生成的随机数本质上不是密码学安全的但对于测试造数完全够用。如果你用的是Microsoft 365版本的Excel可以用更简洁的动态数组公式一次拉出一整列LET( rnd, RANDARRAY(100, 8), LOWER( DEC2HEX(INT(rnd*2^32), 8) - DEC2HEX(INT(rnd*2^16), 4) - 4 DEC2HEX(INT(rnd*2^12), 3) - DEC2HEX(INT(rnd*2^16), 4) - DEC2HEX(INT(rnd*2^32), 8) DEC2HEX(INT(rnd*2^16), 4) ) )这个公式会直接生成100行UUID。要改数量就调整RANDARRAY第一个参数。生成之后建议复制、粘贴为值不然每次Excel刷新公式都会重新生成一批拿去导入数据时前后不一致很容易出错。再提供一个更省力的办法如果你电脑上装有Python直接用一行代码搞定结果更规范import uuid with open(uuid_list.txt, w) as f: for _ in range(100): f.write(str(uuid.uuid4()) \n)Excel方案适合临时用用批量且频繁生成还是建议脚本方案可控性更强。3.2 Linux下查看U盘UUIDLinux里UUID用得最频繁的场景之一是磁盘管理。插入一个U盘系统并不会永远给它分配同一个设备名/dev/sdb可能插拔顺序一变就成了/dev/sdc。但UUID是文件系统在格式化时写入的只要不重新格式化就不会变。这就是为什么/etc/fstab里挂载磁盘一律推荐用UUID而不是设备名。查看磁盘和分区的UUID最直接的是blkidsudo blkid输出长这样/dev/sda1: LABELBOOT UUID5D6E-3A2F TYPEvfat /dev/sda2: UUID8a6e3f4c-1d52-4b7e-9f0d-6c3a2b5e8f01 TYPEext4 /dev/sdb1: LABELUSB32G UUIDA1B2-C3D4 TYPEvfat看TYPEvfat的那一行通常就是U盘。想只看某个具体的设备把设备名加上sudo blkid /dev/sdb1不习惯用blkid的话可以用lsblk输出更贴近人类阅读习惯lsblk -f它会以树状结构展示设备、文件系统类型、UUID和挂载点看着更直观。还有一个土办法是查看目录下的软链接ls -l /dev/disk/by-uuid/这个目录里每个文件名就是UUID指向实际的设备节点。因为名字本身就是UUID配合lsblk一起用很容易辨认哪个UUID对应哪块盘。如果发现U盘的UUID是A1B2-C3D4这种短格式而不是标准的36位格式先别慌。FAT32和exFAT文件系统的UUID本身就是8位短格式这是正常的。ext4、XFS这类Linux原生文件系统才会显示完整的UUID。想改U盘的UUID也不是不行比如你想给某块U盘设一个固定的辨识符可以用tune2fs改ext系列文件系统的UUIDsudo tune2fs /dev/sdb1 -U 550e8400-e29b-41d4-a716-446655440000FAT系列的U盘没有原生命令直接改一般需要借助第三方工具普通使用场景也不建议折腾这个。记住一条原则UUID是在格式化时写进文件系统元数据里的想要修改就做好数据备份别再指望像改文件名一样随手改。3.3 Windows 11下获取主板UUIDWindows 11用户有时需要查主板UUID多见于软件授权绑定、资产盘点、技术支持远程排查这类场景。这里说的UUID不是某一个磁盘分区的UUID而是计算机系统层面的唯一标识一般叫“系统UUID”或者“主板UUID”BIOS在出厂时写入SMBIOS信息里同一块主板不会变。最省事的方法是用wmic。虽然在较新的Windows 11版本中wmic已被弃用但在当前多数正式版里仍然可以用wmic csproduct get uuid输出会直接显示一行UUID。如果这行命令提示wmic不是内部或外部命令说明你的系统版本已经把旧组件移除了换用 PowerShell 的Get-CimInstanceGet-CimInstance Win32_ComputerSystemProduct | Select-Object -ExpandProperty UUID两种方式拿到的都是同一个值。这个UUID同时可以在BIOS/UEFI界面里查到开机进主板设置找到 “System Information” 或 “Product Information” 菜单一般会有一项叫System UUID和系统里查到的完全一致。这里有个容易混淆的点要提醒一下。上面这个命令拿到的UUID是主板级别的和磁盘的卷UUID、Windows安装时生成的机器GUID不是一回事。如果你在设备管理器里看到的“设备实例ID”那又是另一个维度的标识别混在一起。曾经有朋友用Get-CimInstance Win32_ComputerSystemProduct查到的UUID是满屏的FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF直接吓到了。这种情况一般出现在组装机上主板制造商没有在SMBIOS里正确写入UUID或者用的兼容板把这一段留空了。系统检测不到真实值的时候会返回全F的占位值这不是电脑坏了也不影响正常使用。正规品牌机和笔记本基本不会出现这种情况如果你正好遇到说明你这块主板的信息完整度堪忧后续做硬件资产入库时可能要额外注意。4. 实践中最容易踩的坑4.1 存进数据库前后的格式问题UUID存储的坑我见过太多次了。最常见的一种是把UUID直接存成字符串然后用VARCHAR(36)。这样做直观查询结果直接能看但浪费空间而且字符串比较比数值比较慢很多。另一个常见问题是连字符处理。很多系统在存储时会把连字符去掉只存32位的十六进制字符串。前端展示时需要手动拼回标准格式后端读取时也要注意格式转换。如果改造到一半有的服务返回带连字符的有的服务返回不带连字符的联调时就会莫名奇妙地匹配不上排查半天才发现是格式不统一。MySQL里有个更高效的方案是用BINARY(16)存UUID的原始二进制数据。写入时把字符串转成二进制读取时再转回字符串。这样不仅省了一半以上的存储空间而且因为BINARY(16)的排序规则配合版本7的时间有序UUID索引效率能逼近自增ID。给个Java里的转换示例public static UUID fromBytes(byte[] bytes) { ByteBuffer bb ByteBuffer.wrap(bytes); return new UUID(bb.getLong(), bb.getLong()); } public static byte[] toBytes(UUID uuid) { ByteBuffer bb ByteBuffer.allocate(16); bb.putLong(uuid.getMostSignificantBits()); bb.putLong(uuid.getLeastSignificantBits()); return bb.array(); }如果你用PostgreSQL就更方便了它有原生的uuid类型存储和查询都由数据库内部优化不需要自己搞二进制转换直接字符串往里塞就行。项目选型时如果预见到会用UUID作为主键优先考虑PostgreSQL或带原生UUID类型的数据库能省下不少类型转换的隐形成本。还有一点容易被忽略大写的UUID在某些系统里能正常排序在小写环境下又表现不同。业务系统如果涉及跨平台传输UUID建议统一规范为小写因为RDBMS里的字符串排序默认区分大小写规则不一致的话索引命中率会受影响。4.2 不适合直接当主键的两种情形UUID不是万能的有两种情况我坚决不建议直接用UUID做主键。第一种是单库单表的数据量极大且写入极其频繁的场景。排在末尾的随机UUID会让B树索引疯狂分裂本来每次插入都顺序追加现在每次插入都可能落在页面的任意位置触发页分裂的概率大幅上升。同一张表写了几百万行数据之后你会发现查询延迟开始波动写入吞吐上不去。这就是网上经常说的“UUID主键性能杀手”的由来。应对办法是用版本7代替版本4。版本7的时间戳前缀保证了同一时刻生成的UUID在时间上有序减少了索引页分裂同时又保留了一定的随机性避免批量插入时集中在同一个区间。MySQL和PostgreSQL的社区都有实现版本7的开源插件或函数使用成本不高收效却明显。第二种是UUID作为外键被其他表大量引用的场景。外键字段本身要建索引UUID长度是64位整数的四倍索引体积膨胀严重。一个订单主表几百GB关联表的外键索引比主表还大是常见的事。因此遇到核心链路的高频联表查询我更倾向于用一个自增或雪花ID作为代理主键UUID作为业务标识单独存在单独字段两个职责分离互不干扰。在设计主键时先问自己一个问题这个ID是只用来在系统内部关联数据还是也需要暴露到外部接口、作为业务单据号流传如果只用于内部关联代理主键更合适如果要暴露到外部那UUID作为业务标识的优势就体现出来了既能防枚举又无中心生成依赖。4.3 网上那些UUID工具可靠吗这个问题被问过很多次。打开网页搜“UUID生成器”能搜出一大堆在线工具一键生成、批量生成、带不带连字符都能选。临时用一次图个方便没问题但如果你的系统把在线工具生成的UUID当核心数据标识就得想想随机数质量的问题了。浏览器里的JavaScriptMath.random()生成的是伪随机数可预测性比较强。在线工具如果基于这个API生成的UUID在理论上是可以被推测的。对于安全要求高的场景比如令牌、会话ID、防重放凭证坚决不能用这种UUID。安全场景下要使用密码学安全的随机数生成器。在Node.js里用crypto.randomUUID()在Python里用uuid.uuid4()CPython的uuid4使用的是系统级随机源在浏览器里可以用crypto.randomUUID()这些都是基于操作系统提供的安全随机源质量有保障。再补一条自查指南如果某个在线工具声称能生成大量不可重复的UUID而你又不清楚它的随机源那把它用于内部测试数据可以不建议直接进生产库。生产环境的UUID生成代码应该跟随应用代码一起走版本控制而不是依赖外部网页。5. 一些零散但很实用的小技巧UUID转换时有一个容易忽视的边界问题。标准字符串表示法是36个字符但很多系统在保存时为了省空间会去掉连字符变成32位字符串。如果外部接口送来一个32位字符串后端程序解析时就得自行补出连字符。Java的UUID.fromString()能接受32位无连字符的格式也会自动补充解析但Python的uuid.UUID()同样支持两种格式。各语言的兼容性不一致联调前最好先确认对方给的是完整格式还是压缩格式。在使用UUID做文件名或者对象存储的Key时建议保留连字符或者统一转换成小写便于日志排查时直接读。曾经有人把UUID去掉连字符再取前8位做云存储Key的一部分结果日志里看到一堆无规律乱码没法跟业务对接。不如直接用完整UUID加一层前缀区分业务域比如order/2025/06/550e8400-e29b-41d4-a716-446655440000.json既有可读性又保留唯一性。Excel生成UUID后有一个常见坑粘贴到数据库导入工具时Excel可能会把长字符串自动截断或者把某些字段识别成科学计数法。解决办法是在Excel里先把目标列格式设为“文本”再粘贴生成好的UUID。如果是导入CSV文件建议用文本编辑器确认原始内容别直接看Excel渲染后的效果。再聊聊跨系统传输时的大小写问题。标准UUID是十六进制字符A到F的大小写不影响唯一性但会影响查询。两个系统一个存大写一个存小写数据库按字符串比较时就会认为是不同的值。在团队内部约定统一为小写是最省心的做法。PostgreSQL的gen_random_uuid()和MySQL 8的UUID()返回值都是小写直接用就好不要额外做大小写转换。还有一点值得分享的是日志打印的格式问题。分布式系统排查链路时如果把UUID放在日志里建议统一用trace_id550e8400-e29b-41d4-a716-446655440000这种键值形式打印方便日志平台自动提取。有的日志平台对带连字符的UUID解析有额外优化格式统一后在排查问题时可以少走很多弯路。千万别在日志里打一个32位无连字符的版本然后在检索时又用完整36位去搜那基本搜不到。我个人在实际操作中的体会是UUID这类基础工具越早统一规范越好。到底用哪个版本、存什么格式、是否保留连字符、日志里怎么打印这些都是很小的决定但等系统大了再改成本会高得离谱。先花半小时定好规范后面几年省下来的排查时间都值回票价。