← 返回题目列表

PostgreSQL 的 sequence、serial 和 identity 有什么区别?

高频 中等 第 4 / 31 题 更新于 2026/07/29
PostgreSQLsequenceserialidentity

简化版

sequence 是独立的序列对象,serial 是创建整数字段和默认 nextval() 的语法糖,identity 是 SQL 标准化的自增列。它们生成的值不保证事务回滚后连续,也不适合表达严格无间隙编号;需要订单号这类业务编号时,应单独设计业务号生成规则。

详细版

PostgreSQL 自增常见三种写法:

  • sequence:独立对象,可被多个表或字段使用。
  • serial:伪类型,背后创建 sequence 并设置默认值。
  • identity:较新的标准写法,和列绑定更明确。

典型误区是认为自增 ID 一定连续。实际上 nextval() 获取的序列值不会因为事务回滚而退回,数据库重启、缓存、失败插入都可能造成空洞。

主键 ID 可以有空洞,只要唯一、稳定、趋势递增即可;发票号、凭证号这类严格连续编号不能直接依赖普通 sequence。

完整版教学

一、sequence 是独立发号器

PostgreSQL 的 sequence 是数据库里的独立对象,专门负责生成递增数字。它可以通过 nextval() 获取下一个值。

create sequence order_id_seq start 1 increment 1;
select nextval('order_id_seq');

sequence 和普通表数据不同,它更像一个发号器。调用 nextval() 后值就前进,即使当前事务回滚,这个值通常也不会退回。

记忆钩子:sequence 保证“尽量递增且唯一”,不保证“无间隙连续”。

二、serial 是语法糖

serial 不是一个真正的数据类型,而是 PostgreSQL 为了方便创建自增列提供的伪类型。写 id serial 时,数据库会创建整数字段、创建 sequence,并把字段默认值设为 nextval()

create table users (
  id serial primary key,
  name text not null
);

它背后类似于:

create sequence users_id_seq;
id integer not null default nextval('users_id_seq')

所以 serial 的本质仍然是 sequence,只是帮你少写了几行。

三、identity 是更标准的新写法

identity 是 SQL 标准风格的自增列,语义上更明确地绑定到列。新项目中很多团队更推荐使用 identity。

create table users (
  id bigint generated by default as identity primary key,
  name text not null
);

generated always 更严格,默认不允许显式插入;generated by default 更灵活,允许必要时手动插入。迁移旧数据时,by default 往往更方便。

四、序列值为什么会有空洞

序列值有空洞是正常现象。事务回滚、插入失败、nextval() 被调用但未使用、sequence cache、数据库异常重启都可能造成跳号。

事务 A nextval -> 100
事务 A 插入失败并回滚
下一次 nextval -> 101
100 不会自动回收

这是为了性能和并发。如果每个序列号都要等事务提交后确认并可回收,发号器会变成高争用瓶颈。

五、主键 ID 和业务编号要分开看

主键 ID 有空洞通常没问题。它的目标是唯一标识一行、适合索引和关联,不要求连续。

但业务编号可能有额外规则:订单号要带日期、渠道;发票号可能要求连续;凭证号可能要按账期生成。这些不应该直接拿主键 ID 代替。

编号是否允许空洞设计重点
主键 ID通常允许唯一、稳定、索引友好
订单号一般允许少量空洞可读、可查、避免重复
发票号可能不允许合规、连续、审计

面试时强调“主键和业务号分离”,能避免很多误解。

六、sequence cache 会提升性能也会扩大跳号

sequence 支持 cache,数据库可以一次预取多个值,减少频繁写序列元数据的开销。代价是异常重启时未使用的缓存值可能丢失,形成更大的跳号。

create sequence order_id_seq cache 100;

这表示一次缓存 100 个值,性能更好,但重启后可能跳过一段。对主键这通常没问题,对严格连续业务号就不适合。

七、常见误区与追问

  • 误区:serial 是真正的数据类型。 它是创建 sequence 和默认值的语法糖。
  • 误区:事务回滚后 sequence 会回退。 nextval() 通常不会回滚,所以序列可能有空洞。
  • 误区:自增主键可以直接当订单号。 主键服务数据库,订单号服务业务,两者规则不同。
  • 追问:identity 比 serial 好在哪里? identity 是标准语法,和列绑定更清晰,新项目更推荐。
  • 追问:如何处理严格连续编号? 需要单独业务发号、事务控制、审计和异常补偿,不能依赖普通 sequence。
  • 追问:sequence cache 有什么影响? 提升发号性能,但异常时可能扩大跳号。

八、面试中可以这样落地

建表时可以使用 bigint generated by default as identity 作为内部主键;业务订单号另设 order_no,用专门规则生成并加唯一索引。

create table orders (
  id bigint generated by default as identity primary key,
  order_no varchar(64) not null unique,
  created_at timestamp not null
);

如果面试官追问跳号,就说明主键 ID 允许空洞;若业务要求严格连续,需要额外业务编号表、锁或单线程发号,并记录作废号段。

九、加强记忆

sequence 是发号器,serial 是语法糖,identity 是标准自增列。它们都适合作内部主键,但不保证无间隙连续。面试中把“唯一递增”和“严格连续”分开讲,再补一句“主键 ID 和业务编号分离”,答案就很稳。