PostgreSQL序列用法总结

作者:技术拾光时间:2026-06-04 09:01:01浏览:605

PostgreSQL 中的序列,说白了就是一个专门用来生成唯一递增整数的数据库对象。最常见的用途就是为表字段生成自增主键。这篇文章就系统梳理一下序列的用法、核心操作函数,以及在实际使用中容易踩的坑。 序列的基本操作 1. 创建自定义序列 用 CREATE SEQUENCE 语句,就能创建一个完全在你

PostgreSQL 中的序列,说白了就是一个专门用来生成唯一递增整数的数据库对象。最常见的用途就是为表字段生成自增主键。这篇文章就系统梳理一下序列的用法、核心操作函数,以及在实际使用中容易踩的坑。

PostgreSQL序列用法总结

序列的基本操作

1. 创建自定义序列

用 CREATE SEQUENCE 语句,就能创建一个完全在你掌控之下的序列:

   -- 基本语法
   CREATE SEQUENCE 序列名
   [INCREMENT BY 步长] -- 默认为1,可设为负数实现递减
   [START WITH 起始值] -- 默认为1
   [MINVALUE 最小值] -- 默认为1(递增时)
   [MAXVALUE 最大值] -- 默认为 2^31-1(int类型)
   [CACHE 缓存数量] -- 缓存序列值以提高性能,默认1
   [CYCLE | NO CYCLE]; -- 达到最大值后是否循环,默认不循环

   -- 示例:创建从100开始,步长为2,不设置最大值的序列
   CREATE SEQUENCE test_seq
   START WITH 100
   INCREMENT BY 2
   NO MAXVALUE
   CACHE 1;

2. 将序列与表关联

无论是建表时,还是给已存在的表添加自增属性,都可以把序列绑定到字段上:

   -- 创建表时关联序列
   CREATE TABLE test_table (
           id INT PRIMARY KEY DEFAULT nextval('test_seq'),  -- 插入时自动取序列值
           content TEXT
   );

   -- 为已存在的表关联序列
   ALTER TABLE existing_table
   ALTER COLUMN id SET DEFAULT nextval('test_seq');

3. 删除序列

DROP SEQUENCE IF EXISTS test_seq; -- 如果序列被表引用,可以使用 CASCADE 强制删除并解除依赖
DROP SEQUENCE IF EXISTS test_seq CASCADE;

4. 修改序列

使用 ALTER SEQUENCE 可以灵活调整序列的各项参数,包括重置起始值、步长、最大/最小值等。

重置序列的下一个值(最常用)

RESTART WITH 可以直接改变序列下一次调用 nextval() 时返回的值。

   -- 将序列的下一个值重置为 1
   ALTER SEQUENCE 序列名 RESTART WITH 1;

修改步长、最大值、最小值等属性

可以一次性修改序列的多个属性:

   ALTER SEQUENCE 序列名
   INCREMENT BY 2 -- 修改步长为 2
   MAXVALUE 1000000 -- 修改最大值为 100万
   MINVALUE 0 -- 修改最小值为 0
   CACHE 10 -- 修改缓存数量为 10
   CYCLE; -- 开启达到最大值后循环

修改序列的归属或拥有者

可以将序列绑定到某个表的特定字段(删除该字段时序列会自动删除),或者修改序列的所有者。

   -- 将序列绑定到指定表的指定字段
   ALTER SEQUENCE 序列名 OWNED BY 表名.字段名;
   -- 解除序列与任何字段的绑定
   ALTER SEQUENCE 序列名 OWNED BY NONE;
   -- 修改序列的拥有者
   ALTER SEQUENCE 序列名 OWNER TO 新用户名;

修改序列的名称或模式

   -- 修改序列名
   ALTER SEQUENCE 序列名 RENAME TO 新序列名;
   -- 将序列移动到另一个 Schema 下
   ALTER SEQUENCE 序列名 SET SCHEMA 新Schema名;

使用 setval() 函数动态调整当前值

如果需要根据表中现有的数据来动态调整序列(比如防止主键冲突),用 setval() 函数会更方便。

设置为固定值

   -- 将序列的当前值直接设置为 1000,下一次 nextval 将返回 1001
   SELECT setval('序列名', 1000);

基于表中最大 ID 动态同步(强烈推荐)

手动插入过数据导致序列与表数据不匹配时,用这个方法可以完美解决:

   -- 将序列的当前值同步为表中的最大 ID,避免下次插入时主键冲突
   SELECT setval('序列名', (SELECT COALESCE(MAX(id), 0) FROM 表名));

需要提醒几点:执行 ALTER SEQUENCE 或 setval() 操作,必须是该序列的所有者。ALTER SEQUENCE 的大部分操作(如 RESTART)是不可回滚的;而 setval() 在事务中是可以被回滚的。ALTER SEQUENCE 在修改期间会阻塞 nextval、setval 等函数的调用,所以建议在业务低峰期执行。

序列的核心操作函数

PostgreSQL 提供了一系列函数来操作和获取序列值:

函数 作用 示例
nextval(序列名) 生成并返回下一个序列值(自动递增) SELECT nextval('test_seq');
currval(序列名) 获取当前会话中最后一次生成的序列值 SELECT currval('test_seq');
lastval() 获取当前会话中最后一次生成的任意序列值 SELECT lastval();
setval(序列名, 值) 直接设置序列的当前值 SELECT setval('test_seq', 200);
  • nextval() :最常用且最安全,即使在未提交的事务中调用也会消耗一个号(事务回滚后不退还)。
  • currval() :前提是当前会话必须先调用过 nextval(),否则会报错。常用于插入主表后,立即用该 ID 插入关联的子表。
  • lastval() :用的时候要小心。如果中间调用了其他序列,lastval() 返回的会是其他序列的值,在触发器或复杂函数中极易出错。

实战中的三种自增主键实现方式

实际开发中,有三种常见方式来实现自增主键,推荐程度依次递增:

1. 手动创建并绑定序列(最灵活)

如上文所示,手动创建序列后,在表定义中通过 DEFAULT nextval('序列名') 来使用。这种方式适合需要多个表共享同一个序列的场景。

2. 使用 SERIAL / BIGSERIAL(快捷方式)

SERIAL 并不是真实的数据类型,而是 PostgreSQL 提供的语法糖。它会自动创建一个序列,并将其绑定到字段上。

  • SERIAL 等价于 INTEGER + 自动序列
  • BIGSERIAL 等价于 BIGINT + 自动序列(推荐,防止数据量大时溢出)
   CREATE TABLE users (
       id BIGSERIAL PRIMARY KEY, -- 自动创建 users_id_seq 并关联
       username VARCHAR(50) NOT NULL
   );

3. 使用 IDENTITY 列(SQL标准,强烈推荐 )

从 PostgreSQL 10 开始,引入了符合 SQL 标准的 GENERATED AS IDENTITY。它的语义更清晰,明确表示“此列由系统生成”,且工具兼容性更好。

   CREATE TABLE products (
       id BIGINT GENERATED ALWAYS AS IDENTITY (
           START WITH 1000
           INCREMENT BY 1
           CACHE 10
       ) PRIMARY KEY,
       name TEXT NOT NULL
   );

避坑指南与性能优化

  1. ID 不连续与事务回滚

序列值一旦被 nextval() 获取,即使所在的事务回滚,这个值也不会被回收。因此,序列生成的 ID 可能会出现跳号,业务逻辑中不要强依赖 ID 的连续性。

  1. 手动插入 ID 导致的主键冲突

如果手动向表中插入了指定的 ID(例如 INSERT INTO users (id, name) VALUES (999, '张三')),序列的当前值并不会自动更新。下次自动插入时可能会因为 ID 重复而报错。

解决方法:手动同步序列值为表中的最大 ID。

   SELECT setval('users_id_seq',
               (SELECT COALESCE(MAX(id), 0) FROM users));
  1. 高并发下的性能优化(CACHE)

在并发量高的场景下,频繁获取序列值会产生争用。可以通过增大 CACHE 值来优化,序列会预分配一批值到内存中,减少磁盘 I/O。例如 CACHE 1000 适合高并发场景,但缺点是数据库异常崩溃时,内存中未使用的缓存序列号会丢失,导致 ID 出现更大的跳跃。

   -- 高并发场景推荐配置
   CREATE SEQUENCE high_perf_seq
   CACHE 1000
   NO CYCLE;

ALTER SEQUENCE 对关联表的影响

ALTER SEQUENCE 对关联表的影响,主要取决于具体修改了序列的哪个属性。可以从“生命周期绑定”和“数据生成影响”两个层面来看。

1. 修改序列的归属关系(OWNED BY)

这是 ALTER SEQUENCE 对关联表最直接的影响。通过 OWNED BY 子句,可以将序列与表的特定字段进行绑定或解绑:

  • 建立绑定:当执行 ALTER SEQUENCE 序列名 OWNED BY 表名.字段名; 后,序列就和该字段“同生共死”了。如果将来删除了这个字段或者删除了整张表,PostgreSQL 会自动将该序列一并删除。
  • 解除绑定:执行 ALTER SEQUENCE 序列名 OWNED BY NONE; 会切断这种联系,使序列变成一个独立的数据库对象,删除表时不再影响它。

2. 修改序列的生成规则(如 MAXVALUE, RESTART 等)

当修改序列的步长、最大值、起始值等属性时,不会改变表的结构,也不会修改表中已经存在的数据。它的影响主要体现在未来插入的新数据上。

对现有数据无影响:修改序列的最大值或重置序列值,绝对不会更新或删除表中已经生成的 ID。

对后续插入的影响:

  • RESTART WITH / setval() :如果将序列重置为一个较小的值(例如表中已经存在的 ID),那么下次向表中插入数据时,会因为主键重复而报错。
  • MAXVALUE:如果将最大值改得比当前序列值还小,或者设置了 NO CYCLE 且序列达到了新的上限,后续的插入操作会因为无法获取新的序列值而报错。

性能与并发影响:

  • 阻塞调用:执行 ALTER SEQUENCE 命令期间,会短暂阻塞并发的 nextval、currval 等函数调用。在高并发的业务高峰期执行可能会造成短暂的请求卡顿。
  • 清空缓存:如果修改了序列的最大值(MAXVALUE),数据库会清空该序列在所有会话中的缓存。这可能导致序列生成出现较大的跳号,并短暂影响获取序列值的性能。

3. 修改序列的其他属性(拥有者、名称等)

  • OWNER TO / RENAME TO:修改序列的拥有者或重命名序列,对关联的表没有任何逻辑上的影响。表依然可以通过内部依赖正常调用该序列。

总结一下,如果只是修改序列的生成规则,只要确保新规则不会导致主键冲突或超出范围,对关联表就是安全的。如果涉及到 OWNED BY 的绑定操作,则需要考虑到未来删除表时的连带效应。

ALTER SEQUENCE 对正在运行的事务的影响

ALTER SEQUENCE 对正在运行的事务确实有影响,但这种影响并不是一刀切的,可以分为“立即生效”和“延迟生效”两种情况。

立即生效且不可回滚(影响序列生成参数)

当修改序列的生成参数(例如 RESTART WITH、INCREMENT BY、MAXVALUE、MINVALUE、CYCLE 等)时:

  • 不可回滚:为了避免多个并发事务在获取序列值时互相阻塞,这些修改会立即生效且无法通过事务回滚撤销。
  • 阻塞并发调用:ALTER SEQUENCE 在执行期间会直接阻塞其他并发事务对 nextval、currval、lastval 和 setval 等函数的调用。如果业务正处于高并发状态,执行这些修改可能会导致短暂的请求卡顿。
  • 当前会话立即生效:执行命令的当前数据库会话会立刻受到影响,使用新的参数生成序列值。

延迟生效(受缓存 Cache 影响)

如果为序列设置了缓存(CACHE 参数大于 1),其他正在运行的事务可能会受到影响:

  • 缓存耗尽后才生效:其他事务在修改发生前可能已经预分配了一批序列值在内存中。它们会继续使用完这些缓存的值,直到缓存用尽后,才会感知并采用修改后的新序列参数。

可回滚的普通更新(影响元数据)

当修改序列的元数据属性(例如 OWNED BY、OWNER TO、RENAME TO、SET SCHEMA)时:

  • 支持事务回滚:这些操作属于普通的系统目录更新,可以被事务回滚。如果在一个事务中修改了序列的名字或归属,随后执行了 ROLLBACK,这些改动会被撤销。
  • 同样会阻塞调用:尽管支持回滚,但这些元数据修改操作在执行时,同样会阻塞并发的 nextval 等序列函数调用。

核心总结与建议

  • 对现有数据无影响:无论哪种修改,都绝对不会影响表中已经存在的数据,也不会影响序列的 currval 状态。
  • 避开业务高峰期:由于 ALTER SEQUENCE 在执行期间会阻塞并发的序列值获取操作,强烈建议在业务低峰期或维护窗口执行该命令,以避免对线上正在运行的事务造成卡顿或性能抖动。