UUID_SHORT()的唯一性完全依赖server_id配置,其值由公式(server_id & 255) << 56 + (server_startup_time_in_seconds << 24) + incremented_variable++硬编码生成,非随机或加密保证。

UUID_SHORT() 的唯一性完全依赖 server_id 配置
它不是靠随机或加密保证唯一,而是靠公式硬编码:(server_id & 255) 。只要 <code>server_id 在整个集群中唯一且非零(1–255),时间戳不回拨,单秒内插入不超过约 1677 万条,生成值就天然不重复。
常见错误现象:SELECT @@server_id 返回 0 或重复值;建表时用 INT 存 UUID_SHORT() 导致高位截断;主从实例共用同一 server_id 却没意识到这会让 ID 碰撞。
必须做的三件事:
-
SET GLOBAL server_id = 123只是临时生效,得写进my.cnf的[mysqld]段并重启 mysqld 才持久 - 多实例部署(比如分库、容器化)时,每个 MySQL 进程的
server_id必须人工分配且互不相同 - 字段类型必须是
BIGINT UNSIGNED,否则插入直接报ERROR 1264 (22003): Out of range value
为什么它不能用于基于语句的复制(SBR)
UUID_SHORT() 在主库执行一次,返回一个数;但在从库重放同一条 INSERT 语句时,会重新调用函数——而从库有自己的 server_id 和启动时间,结果必然不同。这会导致主从记录的 id 值不一致,数据逻辑错乱。
检查方式:SELECT @@binlog_format 返回 STATEMENT 就危险;应设为 ROW 或 MIXED 并确认复制正常。
如果你正在用 MHA、Orchestrator 或自建主从,别跳过这步验证——很多线上事故就卡在这里。
UUID_SHORT() 不是“短版 UUID 字符串”,别被名字骗了
它返回的是纯数字,64 位无符号整数,不是 32 位十六进制字符串,更不是 RFC 4122 标准 UUID。你不能把它当字符串拼接、截取、或传给期待 UUID 格式的外部系统(比如 OAuth2 token、OpenAPI spec 中定义的 id: string, format: uuid)。
典型误用场景:
- 建表用
VARCHAR(36)存UUID_SHORT()结果 → 浪费空间,且查出来是数字字符串,和真正 UUID 混淆 - ORM 映射成字符串类型(如 Django 的
CharField)→ 插入时隐式转字符串,高位可能丢失(尤其 Python int 转 str 时无问题,但某些驱动会截断) - 前端 JSON 接口直接吐出这个数字 → 虽然合法,但和行业习惯的
xxxx-xxxx-xxxx格式不兼容,前端解析逻辑要额外适配
它快,但兜底仍需数据库约束
UUID_SHORT() 本身不查表、不加锁、不校验冲突,就是 CPU 算一下返回一个数。所以它快,但“是否唯一”全靠你配置对不对。一旦 server_id 冲突或时间回拨,重复就静默发生。
正确做法只有一条:字段必须定义为 PRIMARY KEY 或带 UNIQUE 约束。这样冲突时 MySQL 报 ERROR 1062 (23000): Duplicate entry,你能立刻感知并排查配置,而不是让脏数据悄悄入库。
最容易被忽略的点:很多人测试时单实例跑没问题,一上生产多节点就出问题——因为压根没在所有实例上检查 @@server_id,或者用自动化脚本部署时模板漏写了 server-id 配置项。


















