PostgreSQL中的序列

一、什么是 PostgreSQL 序列(SEQUENCE)

核心定义

PostgreSQL 的 SEQUENCE 是 独立的、持久化的数据库对象,本质是一个自增 / 自减的数值生成器,专门用来生成连续的、唯一的整数序列值。

关键特性

  1. 序列是独立于数据表存在的对象,一个序列可以被多张表的多个字段共用;
  2. 序列存储的是下一个要生成的数值,生成规则可自定义(起始值、步长、最大值、最小值等);
  3. 序列的数值类型默认是 bigint(8 字节,范围极大),足够满足绝大多数业务的自增需求;
  4. 序列是并发安全的,多个会话同时调用序列,不会生成重复值,也不会出现并发冲突;
  5. 核心用途:实现表的主键自增、生成业务唯一编号(订单号、流水号)、批量生成连续编号等。
 

 

二、创建序列(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;
查询结果:
 
idnamecreate_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 伪类型(区别仅在于数值类型)

  1. SERIAL → 对应 integer 类型(4 字节,范围:1 ~ 2147483647
  2. BIGSERIAL → 对应 bigint 类型(8 字节,范围:1 ~ 9223372036854775807),推荐优先使用,数值范围足够大,不会溢出
  3. 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,当数值达到最大值后会循环从头开始,会生成重复的主键值,导致主键冲突,业务中绝对禁止!

posted on 2026-01-16 09:07  数据与人文  阅读(2)  评论(0)    收藏  举报