每次评审新项目的表结构,主键怎么定总要争一轮。主张UUID的说自增ID会泄露信息,主张自增的说UUID又长又乱。吵了这么多年,落到具体业务里答案其实比较清楚了,做数据库设计的新人值得把这篇看完。
自增ID的两个真实问题
一是泄露业务量。你的订单接口返回/order/1024,同行盯着这个数字,明天就能算出你一天多少单,前一个客户下个单,自己的订单号涨多少也一清二楚。二是分库分表时冲突,两台机器都从1开始发号,合到一起就撞了。这两条是硬伤,UUID正是为解决它们流行起来的。
UUID也有自己的坑
UUID是一串36个字符的无意义文本,做主键占空间、索引变大、插入时随机写导致性能下降,而且没有任何可读性和排序意义。拿它当订单号给用户看,客服报单号都能报崩溃,用户抄单号抄到怀疑人生。
实践中最稳的一套组合
内部主键用自增ID,占空间小、索引效率高,单库阶段没有任何问题。对外暴露的另编业务单号:带日期、业务类型和一段随机数的字符串,比如20261029-So-83K2F9,既看不出总量,又好念好查。将来真要做分库分表了,再引入雪花算法这类分布式ID方案替换自增,业务单号完全不受影响。把「内部技术主键」和「对外业务编号」分开,这一刀切下去,大部分争论直接消失。
顺带回答一个衍生问题:订单号里要不要带流水数字?建议带几位随机或加密段,别纯日期加序号,否则竞品观察你三天就能推算出日单量,和自增ID泄露是同一个问题。另外业务单号一旦对外,就别再改规则,打印过的面单、发给客户的邮件、财务对账单都依赖它,中途换格式是灾难,以后谁提都别轻易答应。
两条建议:新项目所有表主键统一自增ID,对外一律走业务编号,千万别把自增ID直接拼进网址;有分库分表明确预期的,一开始就用分布式ID方案,省得日后换主键伤筋动骨。比格网络做的订单类系统都是这套组合,接口文档里业务号和内部ID的界限划得清清楚楚,这是数据库设计里回报率最高的习惯之一。