顺序标识会透露分配规律
顺序标识让相邻的值很容易被猜到。看到第四千号订单,观察者就知道可以从附近的编号继续探测;持续收集编号,也可能看出分配活动的大致变化。但这不代表系统恰好有四千张订单,不代表每个编号都对应成功购买,也不代表最大编号最后提交。想得出这些结论,还需要序列本身没有记录的业务事实。 坏掉的样子 PostgreSQL 序列取值不会跟随普通事务回滚而退回。一次最终放弃的插入,也可能永久消耗一个号码。删除记录、缓存预留区间、显式修改序列以及多个并发写者,都会进一步拉开计数器与当前行数的距离。因此,用最大编号充当客户数或销售数量,即使没有攻击者参与,也会生成错误的经营报表。号码可以提供线索,却不能直接当作精确统计结果。 分配顺序同样不等于提交顺序。事务甲可能先取得较小编号,随后停在那里;事务乙取得较大编号后,反而先完成提交。如果消费者把递增主键当作完整的已提交事件流,就可能先处理较大的行并推进位置,而较小的行此时还不可见。等它后来提交,消费者已经越过了它。数字主键不能直接替代变更捕获位置,也不能证明所有业务事件的先后关系。 随机标识让逐个递增枚举变得困难,却不会自动实施访问控制。UUID 仍可能出现在日志、分享链接或其他接口返回值里,拿到它的人仍须通过对象权限检查。第四版 UUID 使用随机位;第七版在开头放置毫秒时间戳,目标之一是支持时间排序,因此不能把它的前缀说成随机位,也不能把其中的时间当作数据库提交时间。降低可猜测性与确认谁有权读取,是两项不同职责。 快速验证 准备一个可丢弃的 PostgreSQL 18 数据库,打开两个管理员 psql 会话,分别称为甲和乙。下文提取 UUID 信息的函数要求此版本。确认实验模式名和两个角色名尚未使用,初始保持自动提交,隔离级别采用通常的读已提交。先在乙中建立数据: CREATE SCHEMA article_ids; CREATE ROLE article_id_alice NOLOGIN; CREATE ROLE article_id_bob NOLOGIN; SET search_path=article_ids,public; CREATE TABLE objects ( id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, public_id uuid NOT NULL DEFAULT gen_random_uuid() UNIQUE, owner text NOT NULL, label text NOT NULL ); 在甲中取出第一个编号,但暂时不要提交: ...