在使用PostgreSQL设计表结构时,自增主键几乎是绕不开的需求。虽然SERIAL和IDENTITY列可以简化操作,但它们的底层都依赖序列(SEQUENCE)对象。理解序列的创建方式、参数含义以及重置方法,对排查主键冲突、数据迁移后的序列修复等问题非常关键。本文将从零开始介绍PostgreSQL序列的完整用法。

一、什么是序列以及如何创建
序列是PostgreSQL提供的一种数据库对象,本质上是一个特殊的单行表,专门负责产生有序的数字。它独立于普通表存在,可以被多个表共享,也可以单独调用。数据库保证每次从序列取值时都是原子操作,即使在大量并发请求下也不会产生重复值,这正是它适合做唯一标识的原因。
创建序列使用CREATE SEQUENCE语句,最简单的形式只需要一个名字:
-- 创建一个最简单的序列,默认从1开始,步长为1 CREATE SEQUENCE order_seq;
实际生产中往往会带上更多参数,下面是一个完整的例子:
CREATE SEQUENCE order_seq
START WITH 1000 -- 起始值
INCREMENT BY 1 -- 每次递增的步长
MINVALUE 1000 -- 最小值
MAXVALUE 999999999 -- 最大值
CACHE 20 -- 预分配缓存,提升并发性能
NO CYCLE; -- 到达最大值后不再循环几个参数值得展开说明。START WITH决定第一个取出的值是多少,适合从某个业务编号起步的场景。INCREMENT BY可以是负数,表示递减序列,但要注意递减序列的默认最小值和最大值方向也会反过来。CACHE选项指定数据库一次性预分配多少个值放在内存中,高并发写入时能明显减少磁盘更新次数,代价是数据库异常重启后缓存中未用完的值会被丢弃,产生空洞。CYCLE则允许序列到达边界后从头再来,做编号循环复用时有用,但用于主键时务必保持NO CYCLE,否则会撞出重复键。
序列创建好之后,取值主要靠三个函数:nextval取下一个值并推进序列,currval返回当前会话最近一次通过nextval取到的值,setval直接设置序列的当前值。需要注意currval在会话没有调用过nextval之前使用会报错,它返回的不是全局意义上的当前值。
二、序列与表字段的绑定
单独的序列用处有限,更多时候我们希望它作为表字段的默认值来实现自增。绑定方式是给字段设置DEFAULT表达式:
CREATE TABLE orders (
id bigint NOT NULL DEFAULT nextval('order_seq'),
orderno text,
PRIMARY KEY (id)
);这样插入数据时不指定id,数据库会自动从序列取值:
INSERT INTO orders (orderno) VALUES ('A001');
SELECT currval('order_seq'); -- 查看本会话刚取到的值更推荐的做法是使用OWNED BY把序列的归属绑定到具体字段,这样删除字段或表时序列会一起被删除,避免残留孤儿对象:
ALTER SEQUENCE order_seq OWNED BY orders.id;
值得一提的是,常见的SERIAL写法其实就是上面这套动作的语法糖。id serial会自动创建一个序列并绑定到id列。而从PostgreSQL 10开始,官方更推荐使用GENERATED AS IDENTITY写法,它符合SQL标准,并且能防止用户不经意间手动给自增列插入值导致的混乱。三种方式各有适用场景,老系统迁移维护时接触SERIAL和裸序列更多,新项目则建议优先考虑IDENTITY。
如果想知道某个表字段用的是哪个序列,可以通过pg_get_serial_sequence函数查询:
SELECT pg_get_serial_sequence('orders', 'id');
-- 返回结果类似 public.order_seq三、序列重置的正确姿势
序列最经典的故障场景是这样的:通过COPY或INSERT指定id的方式导入了一批数据,id最大值到了5000,但序列还停在10。此时再依赖序列自增插入,就会报duplicate key value violates unique constraint错误。解决办法就是把序列重置到正确位置,核心工具是setval函数。
setval有两种调用形式。三参数形式setval('seq_name', value, is_called)中,当is_called为true时,下一次nextval返回value加步长;为false时,下一次nextval直接返回value。两参数形式等价于三参数且is_called为true。重置时通常配合查询表中的最大id:
-- 将序列重置为表中id的最大值,下一次自增值从10001开始
SELECT setval(pg_get_serial_sequence('orders', 'id'),
(SELECT COALESCE(MAX(id), 0) FROM orders));这里用COALESCE处理空表情况很实用:表里没数据时MAX返回NULL,直接传给setval会报错,兜底为0后序列下一次从1开始,符合预期。如果想让序列从头计数,也可以用ALTER SEQUENCE ... RESTART WITH:
ALTER SEQUENCE order_seq RESTART WITH 1;
重置之外,日常运维还需要掌握查看序列状态的方法。查询pg_sequences视图可以看到每个序列的当前值、步长和缓存:
SELECT sequencename, last_value, increment_by, cache_size FROM pg_sequences WHERE schemaname = 'public';
另外有两个容易踩的坑。第一,序列不是事务性的精确恢复对象,事务回滚时序列取出的值不会退回,这是为了保证并发性能,属于正常现象,不要误以为是bug。第二,序列值只能保证不重复,不保证连续,开启CACHE或发生回滚后都会出现跳号。如果业务要求订单号严格连续,序列方案并不合适,应该考虑单独的号码表加行锁,或者用应用层发号服务。理解这些边界,才能在设计阶段就选对工具。
PostgreSQL序列SEQUENCE创建序列重置修改时间:2026-09-12 17:58:31