PostgreSQL中的序列
一、什么是 PostgreSQL 序列(SEQUENCE)
核心定义
PostgreSQL 的
SEQUENCE 是 独立的、持久化的数据库对象,本质是一个自增 / 自减的数值生成器,专门用来生成连续的、唯一的整数序列值。关键特性
- 序列是独立于数据表存在的对象,一个序列可以被多张表的多个字段共用;
- 序列存储的是下一个要生成的数值,生成规则可自定义(起始值、步长、最大值、最小值等);
- 序列的数值类型默认是
bigint(8 字节,范围极大),足够满足绝大多数业务的自增需求; - 序列是并发安全的,多个会话同时调用序列,不会生成重复值,也不会出现并发冲突;
- 核心用途:实现表的主键自增、生成业务唯一编号(订单号、流水号)、批量生成连续编号等。
二、创建序列(CREATE SEQUENCE)
语法格式(完整)
CREATE [TEMP | TEMPORARY] SEQUENCE [IF NOT EXISTS] 序列名称
[ INCREMENT [ BY ] 步长 ] -- 步长,正数自增,负数自减,默认 1
[ MINVALUE 最小值 | NO MINVALUE ] -- 默认 NO MINVALUE(无最小值)
[ MAXVALUE 最大值 | NO MAXVALUE ] -- 默认 NO MAXVALUE(无最大值)
[ START [ WITH ] 起始值 ] -- 序列的起始值,默认 1
[ CACHE 缓存数量 ] -- 缓存序列值提升性能,默认 1(不缓存)
[ [ NO ] CYCLE ] -- 是否循环生成,默认 NO CYCLE(不循环)
[ OWNED BY 表名.字段名 | OWNED BY NONE ]; -- 绑定表字段,默认 NONE
核心参数说明
步长:正数 = 自增,负数 = 自减,比如INCREMENT BY 2就是每次生成的数值 + 2;CYCLE:当序列值达到最大值(自增)/ 最小值(自减)时,是否循环从头开始,业务主键严禁设置 CYCLE,会生成重复值;CACHE:缓存 N 个序列值到内存,提升高并发下的取值性能,默认 1(不缓存);OWNED BY:将序列和指定表的指定字段绑定,删除该字段 / 表时,序列会被自动级联删除,推荐设置!
实战创建示例(常用 2 种)
示例 1:创建默认规则的序列(最常用)
默认规则:起始 1、步长 1、无最值、不循环、不绑定表
-- 创建一个名为 seq_user_id 的序列
CREATE SEQUENCE seq_user_id;
示例 2:创建自定义规则的序列
-- 创建一个订单编号序列:起始10000、步长1、最大值99999999、不循环、绑定表字段
CREATE SEQUENCE IF NOT EXISTS seq_order_no
INCREMENT BY 1
MINVALUE 10000
MAXVALUE 99999999
START WITH 10000
NO CYCLE
OWNED BY t_order.order_id;
三、序列的核心操作函数(重中之重,必背)
序列创建后,不会自动生效,必须通过专用函数调用取值,这是操作序列的核心,这 3 个函数是高频必用,一定要记住!
1. nextval('序列名') 【最常用】
✅ 功能:获取序列的下一个值,调用一次,序列的数值就永久自增一次(核心特性);
✅ 特性:原子性、并发安全,即使多个客户端同时调用,也绝对不会重复;
✅ 注意:调用后数值会「消耗」,不会回滚!比如插入数据失败,这次获取的序列值也会被跳过,不会重复使用。
2. currval('序列名')
✅ 功能:获取当前会话中,最近一次调用 nextval () 得到的序列值;
✅ 注意事项:
- 必须在当前会话中先调用过 nextval (),才能调用 currval (),否则会报错;
- 只返回「当前会话」的取值,其他会话调用的序列值,不会影响当前会话的 currval 结果。
3. setval('序列名', 数值, [是否立即生效])
✅ 功能:手动修改序列的当前值,重置序列的计数起点;
✅ 语法两种写法:
setval('seq_user_id', 100);:重置序列下一次取值为 101;setval('seq_user_id', 100, true);:和上面等价,第三个参数默认true;setval('seq_user_id', 100, false);:重置序列下一次取值为 100;
四、序列的函数调用实战(独立调用 + 结合表使用)
✔️ 场景 1:直接调用序列函数(测试 / 生成独立编号)
-- 1. 获取序列下一个值(调用一次,值+1)
SELECT nextval('seq_user_id'); -- 第一次调用:1,第二次:2,第三次:3...
-- 2. 获取当前会话的序列值(必须先调用nextval)
SELECT currval('seq_user_id'); -- 比如返回:3
-- 3. 重置序列,让下一次取值从 100 开始
SELECT setval('seq_user_id', 99);
SELECT nextval('seq_user_id'); -- 返回:100
-- 4. 重置序列,让下一次取值就是 100
SELECT setval('seq_user_id', 100, false);
SELECT nextval('seq_user_id'); -- 返回:100
✔️ 场景 2:序列结合表使用(核心场景:主键自增)
这是序列的最核心使用场景:给表的主键字段,通过序列实现「插入数据时自动生成自增 ID」
步骤 1:创建表
CREATE TABLE t_user(
id BIGINT PRIMARY KEY, -- 主键,用序列赋值
name VARCHAR(50) NOT NULL,
create_time TIMESTAMP DEFAULT NOW()
);
步骤 2:插入数据时,通过 nextval () 获取自增 ID
-- 插入数据,主键id的值从序列seq_user_id中获取
INSERT INTO t_user(id, name) VALUES (nextval('seq_user_id'), '张三');
INSERT INTO t_user(id, name) VALUES (nextval('seq_user_id'), '李四');
INSERT INTO t_user(id, name) VALUES (nextval('seq_user_id'), '王五');
步骤 3:查询结果,主键 ID 自动自增
SELECT * FROM t_user;
查询结果:
| id | name | create_time |
|---|---|---|
| 1 | 张三 | 2026-01-16 10:00:00 |
| 2 | 李四 | 2026-01-16 10:00:01 |
| 3 | 王五 | 2026-01-16 10:00:02 |
五、序列与表字段「绑定默认值」(终极简化,开发首选)
上面的插入语句需要手动写
nextval('序列名'),不够简洁。我们可以给表的主键字段设置默认值,值就是序列的 nextval 函数,这样插入数据时,无需指定主键字段,数据库会自动填充自增 ID!✔️ 实现方式(两种)
方式 1:创建表时,直接给字段设置默认值
CREATE TABLE t_user(
id BIGINT PRIMARY KEY DEFAULT nextval('seq_user_id'),
name VARCHAR(50) NOT NULL,
create_time TIMESTAMP DEFAULT NOW()
);
方式 2:给已存在的表字段,新增默认值
ALTER TABLE t_user
ALTER COLUMN id SET DEFAULT nextval('seq_user_id');
✔️ 简化后的插入语法(推荐)
-- 无需指定id字段,数据库自动从序列取值填充
INSERT INTO t_user(name) VALUES ('赵六');
INSERT INTO t_user(name) VALUES ('钱七');
查询结果,id 依然自动自增:4、5 ✔️
六、PostgreSQL 自增主键「简化方案」:SERIAL / BIGSERIAL 伪类型
核心痛点解决
手动创建序列 + 绑定默认值,虽然灵活,但步骤有点多。PostgreSQL 为了简化「主键自增」这个高频需求,提供了 SERIAL 系列伪类型,本质是:
SERIAL = 自动创建序列 + 字段设置默认值 (nextval) + 序列绑定表字段
一句话总结:用
SERIAL 可以一行代码实现主键自增,底层自动帮你完成所有序列相关操作,开发中 99% 的自增主键场景,都用这个方案!✔️ 三种 SERIAL 伪类型(区别仅在于数值类型)
SERIAL→ 对应integer类型(4 字节,范围:1 ~ 2147483647)BIGSERIAL→ 对应bigint类型(8 字节,范围:1 ~ 9223372036854775807),推荐优先使用,数值范围足够大,不会溢出SMALLSERIAL→ 对应smallint类型(2 字节,范围极小,几乎不用)
✔️ 实战示例(一行实现自增主键,超级简洁)
-- 创建表,id字段用BIGSERIAL,自动实现自增主键
CREATE TABLE t_user(
id BIGSERIAL PRIMARY KEY, -- 核心:BIGSERIAL 伪类型
name VARCHAR(50) NOT NULL,
create_time TIMESTAMP DEFAULT NOW()
);
-- 插入数据,无需指定id字段
INSERT INTO t_user(name) VALUES ('测试1'),('测试2'),('测试3');
-- 查询结果,id自动从1开始自增
SELECT * FROM t_user;
七、序列的常用管理操作(ALTER / DROP / 查询)
✔️ 1. 修改序列(ALTER SEQUENCE):修改序列规则
比如修改步长、起始值、最大值、是否循环等,语法和创建时一致:
-- 修改序列seq_user_id的步长为2,最大值为1000000
ALTER SEQUENCE seq_user_id
INCREMENT BY 2
MAXVALUE 1000000;
-- 重置序列的起始值,让下一次取值为1
ALTER SEQUENCE seq_user_id RESTART WITH 1;
✔️ 2. 删除序列(DROP SEQUENCE)
-- 删除单个序列
DROP SEQUENCE IF EXISTS seq_user_id;
-- 删除多个序列
DROP SEQUENCE IF EXISTS seq_user_id, seq_order_no;
-- 级联删除(如果序列绑定了表字段,必须加CASCADE)
DROP SEQUENCE IF EXISTS seq_user_id CASCADE;
✔️ 3. 查询序列信息(常用 2 种方式)
方式 1:psql 命令行快速查看(推荐)
\d 序列名; -- 比如 \d seq_user_id
方式 2:查询系统表,查看所有序列及详情
-- 查询当前数据库所有的序列
SELECT relname AS 序列名称 FROM pg_class WHERE relkind = 'S';
-- 查询序列的详细信息(当前值、步长、起始值等)
SELECT * FROM pg_sequence WHERE seqrelid = (SELECT oid FROM pg_class WHERE relname='seq_user_id');
八、序列的重要注意事项(避坑必看,高频考点)
⚠️ 注意 1:序列的值「不会回滚」(核心特性)
这是 PG 序列最容易踩坑的点,也是面试高频问题:
当你调用nextval()获取序列值后,无论后续的 SQL 执行成功还是失败(比如 INSERT 回滚、事务失败),这个序列值都会被永久消耗,不会被复用。
SELECT nextval('seq_user_id'); -- 返回 4
BEGIN;
INSERT INTO t_user(name) VALUES ('测试'); -- id=4
ROLLBACK; -- 事务回滚,数据未插入
SELECT nextval('seq_user_id'); -- 下一次取值是 5,不是4!
✅ 结论:序列的数值是单调递增的,但不一定连续,这是正常现象,不影响业务主键的唯一性,无需处理。
⚠️ 注意 2:序列是独立对象,可被多表共用
一个序列可以被多张表的多个字段调用,比如:
INSERT INTO t_user(id) VALUES (nextval('seq_common'));
INSERT INTO t_order(id) VALUES (nextval('seq_common'));
两张表的 id 会共用同一个序列的数值,保证全局唯一,适合生成「全局业务编号」。
⚠️ 注意 3:SERIAL 是「伪类型」,不是真正的数据类型
SERIAL 只是语法糖,底层还是序列 + integer/bigint,所以在 Java 等编程语言中,对应的实体类字段类型还是
Integer / Long。⚠️ 注意 4:主键自增严禁设置 CYCLE
如果给序列设置了
CYCLE,当数值达到最大值后会循环从头开始,会生成重复的主键值,导致主键冲突,业务中绝对禁止!
浙公网安备 33010602011771号