ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

tar.gz安装OSSP uuid C库:从解压到生产落地的完整指南

tar.gz安装OSSP uuid C库:从解压到生产落地的完整指南 简介这是PostgreSQL uuid-ossp扩展1.6.2版本的源代码包面向需要在数据库层面生成全局唯一标识符的开发者、架构师及分布式系统运维人员。uuid-ossp提供的uuid_generate_v1()与uuid_generate_v4()函数可分别生成基于时间/网卡地址和纯随机的UUID避免多系统间主键冲突。压缩包共83个文件约388KB主体为C语言源码.c/.h、编译配置脚本configure、Makefile、.ac/.m4、Perl/PHP语言绑定.pm/.pl/.xs/.php以及多格式文档.pod/.txt/.readme覆盖从源码构建到二次开发的主要环节。目前已有1490人学习下载。资源不仅包含uuid_generate系列核心实现还附带UUID字符串/二进制转换、MAC地址获取、sha1/md5摘要等配套工具以及autoconf生成的跨平台编译配置与测试用例便于读者在PostgreSQL中快速部署扩展或参考其实现自行设计分布式ID生成方案。1. uuid-1.6.2.tar.gz一个 C 库压缩包凭什么值得花十分钟装它你从官网或内网源拉下uuid-1.6.2.tar.gz这个包名字看起来平平无奇甚至有点年头——它是 OSSP uuid 这个 C 语言库的 1.6.2 版本源码包。解开之后你能得到一个能生成、解析、格式化 UUID 的完整库支持 v1/v3/v4/v5 四种标准以及uuid命令行工具。对后端工程师来说分布式 uuid 做主键、日志追踪 ID、会话标识几乎是躲不开的刚需。而这个包的价值在于不依赖数据库不依赖 Python/Java运行时一个纯 C 库在任何 Linux 上都能编出来、链进去几毫秒内给你一个全球唯一的 128 位标识。它适合做基础服务、网关、数据库中间件的人也适合那些在国产系统上装软件、被 tar.gz 解压和编译流程卡住的人。本文从解压到生产落地把整条链路拆开讲。2. 从 tar.gz 到能调用的 uuid_t解压、configure、make 的全过程2.1 解压前先看清 tar.gz 里到底有什么tar.gz 是双层格式tar 负责归档和保留文件权限gzip 负责压缩体积。很多人上来就tar -xzf一把梭解出来发现文件散落一地或者缺头文件才开始后悔药。正确做法是先看包里有什么tar -tzf uuid-1.6.2.tar.gz | head -30-t是列出内容不实际解压-z表示按 gzip 解压后读取-f指定文件。head -30只看前 30 行。你会看到所有条目都以uuid-1.6.2/开头说明这个包打包时带了顶层目录——在当前位置直接解压不会污染当前目录。如果列出来没有统一前缀就得先mkdir uuid tar -xzf uuid-1.6.2.tar.gz -C uuid手动收进目录。参数说明-x是解压、-C指定目标目录。我习惯永远用-C显式指定而不是cd过去再解因为脚本里重复执行不会把文件撒到奇怪的地方。这几十秒的检查能省掉后面排查文件缺漏的大把时间。2.2 手动编译安装的三条命令与核心参数uuid-1.6.2是老 autotools 工程标准三步走./configure --prefix/opt/uuid --disable-shared --enable-static make -j4 make install--prefix/opt/uuid是核心参数。默认装到/usr/local但我强烈建议装到独立目录后续卸载就是rm -rf /opt/uuid不会污染系统库路径也不会跟发行版自带的 libuuid 抢名字。--disable-shared --enable-static是我在需要把库链进发布包时的组合——静态库不依赖目标机器上有libuuid.so减少部署环境的未知数。如果只是本机开发保留默认动态库也没问题。跑 configure 前先确认工具链gcc --version make --version缺了就先装。Debian/Ubuntu 系是apt install build-essentialRed Hat/CentOS/银河麒麟这类国产 Linux 系统是yum install gcc make。这一步看起来多余但 configure 报C compiler cannot create executables的人几乎全是编译器没装十有八九的翻车都发生在跑 configure 之前。make -j4的-j4表示四路并行编译。这个包很小加了不一定省一秒但养成习惯对以后编大项目有好处。make install默认要 root 权限写/opt/uuid所以通常加sudo。装完验证ls /opt/uuid/bin /opt/uuid/bin/uuid -v 4第二条命令会输出一个 v4 UUID格式类似e0f8f4d0-8d4f-4f43-8c0b-2f5d3e6d9c84。看到这串字符说明库、命令行工具、头文件三样都齐了。2.3 链接阶段头文件路径与库路径一个都不能少装到/opt/uuid后系统默认搜索路径里没它。写个测试程序编译时你会撞上uuid.h: No such file or directory。OSSP 自带一个辅助脚本uuid-config专门输出编译参数/opt/uuid/bin/uuid-config --cflags /opt/uuid/bin/uuid-config --libs输出分别是-I/opt/uuid/include和-L/opt/uuid/lib -luuid。编译时用命令替换拼进 gccgcc -o genuuid genuuid.c $(/opt/uuid/bin/uuid-config --cflags --libs)$(...)是 shell 命令替换把脚本输出展开成 gcc 参数。-l后跟的uuid是库名gcc 会去找libuuid.so或libuuid.a。这里已经埋了一个雷OSSP uuid 的库名和你系统里可能自带的 e2fsprogs libuuid 同名后面第 4 章专门讲怎么排。如果你不用 uuid-config也可以手写-I/opt/uuid/include -L/opt/uuid/lib -luuid完全等价用脚本的好处是以后换--prefix路径命令不用跟着改。如果你经常下载linux x64 的 java8 .tar.gz来做 JDK 环境那你对这个套路应该很熟解压、配JAVA_HOME、验证java -version本质和这里一模一样——tar.gz 从来不是某个语言专属tar -xzf解包、改 PATH、验证版本永远是同一套。C 库只是多了一步make install把头文件和库铺到指定位置。忘掉这一步的直接后果编译能过运行时报error while loading shared libraries。这时要么在/etc/ld.so.conf.d/下新建配置文件写入/opt/uuid/lib再跑ldconfig要么链接时加-Wl,-rpath,/opt/uuid/lib写死运行时搜索路径。开发机上我用 rpath部署机上用 ldconfig各得其所。3. 把 uuid.h 用起来API 关系、v1/v4 选型与格式化3.1 uuid_create、uuid_make、uuid_export 三个 API 怎么配合OSSP uuid 的 C API 风格和很多人熟悉的 libuuide2fsprogs 那套uuid_generate不一样。它用一个上下文结构uuid_t *贯穿生命周期#include uuid/uuid.h #include stdio.h #include stdlib.h #include string.h int main(void) { uuid_t *uuid; char *str NULL; if (uuid_create(uuid) ! UUID_RC_OK) { fprintf(stderr, uuid_create failed\n); return 1; } if (uuid_make(uuid, UUID_MAKE_V4) ! UUID_RC_OK) { fprintf(stderr, uuid_make failed\n); uuid_destroy(uuid); return 1; } if (uuid_export(uuid, UUID_FMT_STR, str, NULL) ! UUID_RC_OK) { fprintf(stderr, uuid_export failed\n); uuid_destroy(uuid); return 1; } printf(%s\n, str); free(str); uuid_destroy(uuid); return 0; }逻辑说明uuid_create(uuid)只是分配并初始化上下文不生成任何值uuid_make(uuid, UUID_MAKE_V4)才是真正生成——第二个参数是 UUID 版本枚举。uuid_export把内存里的 128 位数据导出成可读格式UUID_FMT_STR表示 36 字节标准字符串含连字符。参数说明str是输出参数uuid_export内部malloc一块内存并把指针写到str所以用完必须free(str)释放否则每个调用都泄漏。uuid_destroy释放uuid_create分配的上下文。最容易错的就是这两个忘 free 导致内存泄漏忘 destroy 导致句柄泄漏。长驻服务进程跑几周后 RSS 疯涨valgrind 一查一个准。我见过生产事故就出在这——一个网关程序每次请求都创建 uuid 不销毁内存翻倍直到 OOM 被 kill。3.2 v1 与 v4 的选型判断不是「随机就好」这么简单OSSP uuid 支持四个版本枚举UUID_MAKE_V1、UUID_MAKE_V3、UUID_MAKE_V4、UUID_MAKE_V5。选型逻辑值得单独说。v4 是纯随机122 位熵。碰撞概率在百万级数量下几乎不存在适合做对外暴露的随机标识、匿名 ID、令牌等安全敏感场景。v1 是时间戳加 MAC 地址时间上有序性插入数据库 B 树索引时比 v4 友好——随机值插入会让索引页频繁分裂时间有序值则基本追加写入写性能稳。但 v1 有个天生的信息泄漏问题它编码了生成时间和本机 MAC 地址别人拿到你的 uuid 可以反推网卡厂商和大致生成时间对抗场景不能直接用。v3/v5 是确定性 UUID相同输入必得相同输出。区别是 v3 用 MD5、v5 用 SHA-1。适合做幂等键——比如用「用户ID 业务类型 请求序号」拼出一个固定 uuid重复请求不会生成新值天然支持去重。我一般这样判断写密集的数据库主键用 v1或 v5 如果业务上已有唯一键对外暴露的随机标识用 v4需要多方数据对齐的关联键用 v5。另外提一句v3 因为用 MD5新项目不建议v5 是更稳妥的选择。很多人纠结「UUID 太长」「随机还是有序」本质是没想清楚使用场景——先定位场景再选版本不要一刀切。3.3 格式化与解析UUID_FMT_STR 和 UUID_FMT_BIN 的互转实际开发里经常要在字符串和二进制之间切换数据库存二进制日志打字符串API 返回字符串。OSSP uuid 的uuid_export支持多种格式核心是两个#include uuid/uuid.h #include stdio.h #include stdlib.h int main(void) { uuid_t *uuid; unsigned char *bin NULL; size_t len 0; char *str NULL; if (uuid_create(uuid) ! UUID_RC_OK) return 1; if (uuid_make(uuid, UUID_MAKE_V4) ! UUID_RC_OK) return 1; len 0; if (uuid_export(uuid, UUID_FMT_BIN, bin, len) ! UUID_RC_OK) return 1; printf(binary length: %zu bytes\n, len); if (uuid_export(uuid, UUID_FMT_STR, str, NULL) ! UUID_RC_OK) return 1; printf(string: %s\n, str); free(bin); free(str); uuid_destroy(uuid); return 0; }逻辑说明UUID_FMT_BIN导出原始 16 字节二进制len是输出参数返回实际字节数——对 BIN 格式固定是 16。UUID_FMT_STR导出 36 字节的字符串格式。第一次uuid_export传len第二次传NULL表示不需要长度。参数说明free(bin)和free(str)都不能省两种格式导出的内存都是库内部malloc的。解析时用uuid_parse把字符串还原回uuid_t和uuid_export反向对应。这里有个实用技巧如果既要二进制又要字符串不要先导出二进制再手动转十六进制而是分别调用两次uuid_export——库内部做好了格式转换你自己写 hex 转换反而容易在字节序上翻车。我见过同事手写字节转十六进制在大小端上纠结了一下午其实UUID_FMT_STR一次调用就出标准格式。4. 解压、编译、存储、用错场景五个必踩的坑与排查4.1 tar.gz 解压丢了权限位和符号链接现象tar -xzf uuid-1.6.2.tar.gz解压后编译还算正常但某些辅助脚本运行时报Permission denied或者符号链接变成了普通文本文件内容。原因普通tar -xzf解压只还原基本权限位setuid、setgid、ACL 和符号链接目标在某些精简环境下不保留。autotools 工程里的mkinstalldirs、install-sh这类脚本如果丢了执行位make install时一个Permission denied就中断排查起来很玄。解决改用tar -xpzf uuid-1.6.2.tar.gz-p参数明确要求保留权限位。解压后立刻ls -l检查脚本执行位再进下一步。这个习惯救了我好几次——不只是 uuid 这个包任何带特权位脚本的 tar.gz 都适用。4.2 系统自带 libuuid 和 OSSP uuid 同名打架现象按第 2 章流程编译通过运行时段错误或者程序输出的 uuid 格式不对更常见的是链接时直接报找不到uuid_create符号。原因很多 Linux 发行版自带 e2fsprogs 的 libuuid头文件同样叫uuid/uuid.h生成的库文件同样叫libuuid.so。gcc 默认搜索/usr/include和/usr/lib你的#include uuid/uuid.h很可能命中了系统头文件-luuid也命中了系统库。系统库提供的是uuid_generate那套 API根本没有uuid_create这个符号链接直接失败。解决先确认到底链上了谁ldd genuuid | grep uuid nm -D /usr/lib/x86_64-linux-gnu/libuuid.so | grep uuid_create如果nm没有输出说明这是 e2fsprogs 的库。解决方案是用静态库全路径直接指定绕开搜索顺序gcc -o genuuid genuuid.c -I/opt/uuid/include /opt/uuid/lib/libuuid.a逻辑说明直接给 gcc 一个.a文件路径就不用-L和-l组合了彻底避免链接到系统动态库。这是我踩过的坑——一开始以为是头文件冲突换了-I顺序还是不稳定最后发现是链接阶段的事跟头文件没关系。排查这种问题要分清楚「编译期找不到头文件」和「链接期找不到符号」是两回事。4.3 uuid 太长有没有精简方案从 CHAR(36) 到 BINARY(16)现象接口文档里 uuid 字段是9d2a1c70-8d4f-4f43-8c0b-2f5d3e6d9c84这种 36 字符字符串存 MySQL 用了CHAR(36)或VARCHAR(36)DBA 抱怨索引膨胀前端抱怨 URL 太长。原因36 个字符里包含 4 个连字符是给人看的标准格式不是存储最优格式。VARCHAR(36)在 utf8mb4 下单个值最多占 144 字节索引体积失控。解决三档精简方案按成本从低到高排列第一档去连字符存CHAR(32)。改动最小适合只换存储层不动代码的情况但 32 字符比 16 字节仍多一倍空间收益有限。第二档存BINARY(16)。应用层用uuid_export(..., UUID_FMT_BIN, ...)拿 16 字节二进制数据库字段BINARY(16)。展示时再导出成字符串或者 SQL 里用HEX()转十六进制。MySQL 8.0 有原生函数-- 写入把字符串 uuid 转成二进制 INSERT INTO t (id) VALUES (UUID_TO_BIN(9d2a1c70-8d4f-4f43-8c0b-2f5d3e6d9c84)); -- 读取把二进制转回可读字符串 SELECT BIN_TO_UUID(id) FROM t;逻辑说明UUID_TO_BIN把 36 字符的标准格式转成 16 字节二进制BIN_TO_UUID是逆操作。这两个函数在 MySQL 8.0 内置比应用层自己REPLACE去连字符再UNHEX()干净得多。第三档全链路二进制。应用层从生成到存储、到日志、到服务间传输都用 16 字节原始格式只在面向用户的最外层 API 做格式化。这是存储最省、索引最小的方案适合 RPC 内部系统。边界判断如果 uuid 只在系统内部流转直接用第三档如果会出现在 URL、邮件第二档加展示格式化最均衡第一档只是短期内不想改代码的应急。另外注意UUID 字符串的连字符位置是固定的8-4-4-4-12如果看到网上有人用去连字符后CHAR(32)就以为是最优你要知道 16 字节的二进制才是终极形态。4.4 uuid 能不能当登录 token从「能」到「别直接当」现象简单把一个 v4 uuid 当登录 token 发给前端用户表现为token 永久有效、登出后旧 token 还能用、抓包重放也能通过。原因UUID 的核心语义是「唯一标识符」不是「会话凭证」。唯一性解决的是「不重复」而凭证类数据需要过期时间、吊销状态、用户绑定、防重放。一个裸 uuid 在服务端没有任何状态记录服务端拿到 token 时既不知道它对应谁也无法判断它是否已吊销。解决常见做法是「uuid 当 token 的值而不是 token 本身」。在 Redis 里用 uuid 做 session key服务端存user_id expire_at device_info过期即失效登出时DEL删除对应 key。如果你的用户体系还没有 Redis至少要建user_id / expire_at / revoked三列手动查库校验登出时置revoked1。另一种思路是直接用 JWT把 uuid 放jtiJWT ID字段用它做防重放让 JWT 的签名和过期机制承载凭证语义。一句话uuid 可以当 token 的唯一标识部分但凭证逻辑——过期、吊销、绑定——必须另外实现。这条我深有体会早期图省事直接用 uuid 当 token上线两周就被安全评审打回连夜加 Redis session 存储教训深刻。4.5 国产 Linux 发行版上编译 tar.gz 的依赖坑现象在麒麟这类国产 Linux 系统上解压配置configure报错信息指向libtool缺失或标准头文件找不到make时 gcc 报stdio.h: No such file一类错误。原因这类系统常做最小化安装开发工具链不完整。autotools 老工程对libtool、m4、autoconf、automake有隐性依赖configure 生成Makefile的过程中一步失败后面全是连锁的。解决先补齐工具链再重跑 configureyum install -y gcc make libtool m4 autoconf automake如果仍然报错把 configure 日志存下来从第一个error:开始看./configure --prefix/opt/uuid 21 | tee config.logtee同时把输出打到屏幕和config.log。排查时搜config.log里第一个error:从第一处错误开始解决不要从底部往上看——底部的错误往往是第一处错误的连锁反应。因为 1.6.2 这个包只依赖 libc 和标准库不依赖第三方库工具链补齐后基本能编译通过问题几乎都出在工具链缺失上。5. 把 uuid 用到生产批量生成性能、二进制定长存储与内部打包5.1 批量生成 100 万条 UUID 的耗时与并发场景命令行测速最容易犯的错是把输出打到终端终端 I/O 比生成本身慢几个数量级。正确姿势time for i in $(seq 1 100000); do /opt/uuid/bin/uuid -v 4 /dev/null; done这段把每次输出丢到/dev/null测的是纯生成耗时结果通常是几秒级别。但要注意这个数字是「每次启动一个进程」的开销不是库内调用真实吞吐。想测库内性能写个 C 程序循环调用uuid_make能到每秒几十万到上百万的量级差距来自进程启动成本。实战注意多线程场景下每个线程要持有自己的uuid_t *上下文。OSSP 的上下文之间互不干扰可以并行如果多个线程共享同一个上下文调uuid_makev1 因为内部要维护时间戳连续性可能出问题。v4 没有这个顾虑随机源是线程安全的。我在多线程网关里给每个 worker 线程初始化一个上下文用完uuid_destroy没出过重复。5.2 以 16 字节二进制存 uuid配合索引的收益字符串 36 字符改用BINARY(16)后B 树一页能容纳的行数大幅提升。实测过一个十万行级的订单表varchar(36) 主键占的空间约是 binary(16) 的两三倍随机插入时的页分裂也少很多。配合 MySQL 8.0 的UUID_TO_BIN/BIN_TO_UUID已经在上文提过。一个关键参数是UUID_TO_BIN(uuid, 1)的第二个参数值为 1 时会把 uuid 内部的时间部分移到二进制开头。这样如果你用的是 v1磁盘上的 b 树顺序变成时间有序——append 友好范围查询也能走索引。如果保持默认 0无论 v1 还是 v4 都是随机序。-- 建表时主键直接 BINARY(16)写入时交换时间位 CREATE TABLE t ( id BINARY(16) PRIMARY KEY, created_at DATETIME(6) ); INSERT INTO t (id, created_at) VALUES (UUID_TO_BIN(UUID(), 1), NOW(6));说明UUID()生成 v1 还是 v4 取决于 MySQL 版本和配置MySQL 8.0 默认是 v1UUID_TO_BIN(..., 1)第二个参数 1 表示交换时间位。如果你用应用层生成 uuid不要假设它一定是 v1——v4 没有时间可交换硬用参数 1 不会报错但是也没有收益。判断标准只有一个生成时用的是UUID_MAKE_V1还是UUID_MAKE_V4。v1 才有时间序v4 谈优化索引没有物理基础。5.3 把编译产物打包推到内部仓库多台机器要用同一份编译结果不要各自解压源码编译——环境差异和工具链版本会给出「同包不同果」的黑匣子。常见做法是打包编译产物推到内部制品库cd /opt/uuid tar -czf uuid-1.6.2-linux-x64.tar.gz ./* curl -u $NEXUS_USER:$NEXUS_PASS \ --upload-file uuid-1.6.2-linux-x64.tar.gz \ http://nexus.internal/repository/raw-libs/uuid-1.6.2-linux-x64.tar.gzcurl -u是基础认证--upload-file是 PUT 上传。拉取端就是普通tar -xzf并配置PKG_CONFIG_PATHtar -xzf uuid-1.6.2-linux-x64.tar.gz -C /opt export PKG_CONFIG_PATH/opt/uuid/lib/pkgconfig:$PKG_CONFIG_PATHPKG_CONFIG_PATH让 uuid.pc 文件能被识别后续编译用pkg-config --cflags --libs uuid拿到参数。这套流程和把镜像推到私有仓库的思路完全同构一次构建、多处部署区别只在制品格式。我建议制品里只放编译产物、头文件和 pkgconfig不放源码和构建中间文件体积小、路径简单、可复现性强。最后提醒一句任何 tar.gz 制品解压后先跑一遍uuid -v 4确认链接到的是/opt/uuid/lib下的 libuuid 而不是系统的老库。查链接来源用ldd养成这个检查习惯能帮你提前发现一半的打包问题。希望帮到你。本文还有配套的精品资源点击获取
返回列表