标准答案

  1. sequence 是独立于业务表事务的对象。nextval 一旦取号就会推进序列,即使后续 INSERT 回滚,这个号码也不会因为回滚而自动归还。
  2. INSERT 冲突、校验失败、并发事务先取号后回滚,都会造成间隙;sequence cache 还可能在数据库异常停止时丢掉已经预分配但尚未返回的号码。
  3. 并发安全和连续编号是两种不同目标。sequence 更适合生成唯一、可排序但允许跳号的技术 ID,不应被解释成严格的创建顺序或业务流水号。
  4. 如果业务只是需要展示友好的编号,可以把技术主键和业务编号分开;业务编号使用独立序列、租户范围或年份范围,并为唯一性和状态审计设置数据库约束。
  5. 真正要求无间隙的编号必须在受控事务中串行分配,并接受锁竞争、吞吐下降和故障恢复复杂度;还要先确认“无间隙”是否是法律要求,而不是页面展示偏好。

题目解析

sequence 的职责是提供高效的并发取号,不是记录一笔业务是否最终提交。把取号动作和事务提交强行绑定,会让回滚、故障和并发都变成编号分配的等待点。

“ID 不连续”通常不是数据丢失证据。判断数据是否丢失要看业务表的约束、提交记录和对账结果;不能用 MAX(id) 与行数做简单比较。

设计编号时要先区分技术主键、对外订单号、发票号和审计流水号。它们的唯一范围、是否允许作废、是否需要连续、是否允许跨租户重复都不同,最好不要共用一个 sequence。

常见误区

  • 误区:用 MAX(id) + 1 生成下一个主键。改正:并发请求会读到同一个最大值,应该使用主键约束配合 sequence 或其他经过并发验证的号源。
  • 误区:通过回滚后手工重置 sequence 来填补间隙。改正:这可能与已提交事务或并发取号冲突,填补间隙通常不值得冒数据冲突风险。
  • 误区:把 ID 越大理解成创建时间越晚。改正:缓存、回滚、批量取号和多实例写入都会破坏这个推断,时间应由明确的 timestamptz 字段记录。

作者信息