PostgreSQL ---【序列】用法详解

PostgreSQL 中的序列(Sequence)是一个独立的数据库对象,专门用于生成唯一的递增整数,最常用于为表字段生成自增主键。下面详细解析序列的用法、核心函数以及实战中的避坑指南。

🛠️ 序列的基本操作

1. 创建自定义序列

可以使用 CREATE SEQUENCE 语句来创建一个完全可控的序列:

sql 复制代码
   -- 基本语法 
   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. 将序列与表关联

在创建表时,或者为已存在的表添加自增属性时,可以将序列绑定到字段上:

sql 复制代码
    -- 创建表时关联序列 
    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. 删除序列

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

4. 修改序列

🛠️ 使用 ALTER SEQUENCE 修改序列属性

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

  • 重置序列的下一个值(最常用)
    使用 RESTART WITH 可以改变序列下一次调用 nextval() 时返回的值2。
sql 复制代码
    -- 将序列的下一个值重置为 1 
    
    ALTER SEQUENCE 序列名 RESTART WITH 1;
  • 修改步长、最大值、最小值等属性
    可以一次性修改序列的多个属性:
sql 复制代码
    ALTER SEQUENCE 序列名
    INCREMENT BY 2 -- 修改步长为 2
    MAXVALUE 1000000 -- 修改最大值为 100万 
    MINVALUE 0 -- 修改最小值为 0 
    CACHE 10 -- 修改缓存数量为 10 
    CYCLE; -- 开启达到最大值后循环
    
  • 修改序列的归属或拥有者

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

sql 复制代码
    -- 将序列绑定到指定表的指定字段 
    ALTER SEQUENCE 序列名 OWNED BY 表名.字段名; 
    -- 解除序列与任何字段的绑定 
    ALTER SEQUENCE 序列名 OWNED BY NONE; 
    -- 修改序列的拥有者 ALTER SEQUENCE 序列名 OWNER TO 新用户名;
    
  • 修改序列的名称或模式
sql 复制代码
    -- 修改序列名 
    ALTER SEQUENCE 序列名 RENAME TO 新序列名; 
    -- 将序列移动到另一个 Schema 下 
    ALTER SEQUENCE 序列名 SET SCHEMA 新Schema名;
  • 使用 setval() 函数动态调整当前值

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

设置为固定值

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

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

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

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

💡 温馨提示

  • 权限要求 :执行 ALTER SEQUENCEsetval() 操作,必须是该序列的所有者。
  • 事务特性ALTER SEQUENCE 的大部分操作(如 RESTART)是不可回滚的;而 setval() 在事务中是可以被回滚的。
  • 并发影响ALTER SEQUENCE 在修改期间会阻塞 nextvalsetval 等函数的调用,建议在业务低峰期执行。

⚙️ 序列的核心操作函数

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 + 自动序列(推荐,防止数据量大时溢出)
sql 复制代码
    CREATE TABLE users ( 
        id BIGSERIAL PRIMARY KEY, -- 自动创建 users_id_seq 并关联 
        username VARCHAR(50) NOT NULL 
    ); 

3. 使用 IDENTITY 列(SQL标准,强烈推荐 ✅) 从 PostgreSQL 10 开始,引入了符合 SQL 标准的 GENERATED AS IDENTITY。它的语义更清晰,明确表示"此列由系统生成",且工具兼容性更好

sql 复制代码
    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 的连续性。
  2. 手动插入 ID 导致的主键冲突
    如果手动向表中插入了指定的 ID(例如 INSERT INTO users (id, name) VALUES (999, '张三')),序列的当前值并不会自动更新。下次自动插入时可能会因为 ID 重复而报错。

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

sql 复制代码
    SELECT setval('users_id_seq', 
                (SELECT COALESCE(MAX(id), 0) FROM users));
  1. 高并发下的性能优化(CACHE)
    在并发量高的场景下,频繁获取序列值会产生争用(Sequence Contention)。可以通过增大 CACHE 值来优化,序列会预分配一批值到内存中,减少磁盘 I/O。例如 CACHE 1000 适合高并发场景,但缺点是数据库异常崩溃时,内存中未使用的缓存序列号会丢失,导致 ID 出现更大的跳跃。
sql 复制代码
    -- 高并发场景推荐配置 
    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 命令期间,会短暂阻塞并发的 nextvalcurrval 等函数调用。在高并发的业务高峰期执行可能会造成短暂的请求卡顿。
    • 清空缓存 :如果修改了序列的最大值(MAXVALUE),数据库会清空该序列在所有会话中的缓存(Cache)。这可能导致序列生成出现较大的跳号,并短暂影响获取序列值的性能。

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

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

总结建议

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

⚠️ ALTER SEQUENCE 影响正在运行的事务?

ALTER SEQUENCE 对正在运行的事务确实有影响,但这种影响并不是"一刀切"的,而是分为"立即生效"和"延迟生效"两种情况。具体取决于修改的是序列的哪部分属性:

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

当修改序列的生成参数(例如 RESTART WITHINCREMENT BYMAXVALUEMINVALUECYCLE 等)时:

  • 不可回滚 :为了避免多个并发事务在获取序列值时互相阻塞,这些修改会立即生效且无法通过事务回滚(ROLLBACK)撤销

  • 阻塞并发调用ALTER SEQUENCE 在执行期间会直接阻塞 其他并发事务对 nextvalcurrvallastvalsetval 等函数的调用。这意味着如果的业务正处于高并发状态,执行这些修改可能会导致短暂的请求卡顿。

  • 当前会话立即生效:执行命令的当前数据库会话(后端)会立刻受到影响,使用新的参数生成序列值。

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

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

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

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

当修改序列的元数据属性(例如 OWNED BYOWNER TORENAME TOSET SCHEMA)时:

  • 支持事务回滚 :这些操作属于普通的系统目录更新,可以被事务回滚 。如果在一个事务中修改了序列的名字或归属,随后执行了 ROLLBACK,这些改动会被撤销。

  • 同样会阻塞调用 :尽管支持回滚,但这些元数据修改操作在执行时,同样会阻塞并发的 nextval 等序列函数调用5。

💡 核心总结与建议

  • 对现有数据无影响 :无论哪种修改,都绝对不会 影响表中已经存在的数据,也不会影响序列的 currval 状态(当前会话最后一次获取的值)。
  • 避开业务高峰期 :由于 ALTER SEQUENCE 在执行期间会阻塞并发的序列值获取操作,强烈建议在业务低峰期或维护窗口执行该命令,以避免对线上正在运行的事务造成卡顿或性能抖动。
相关推荐
ITenderL20 小时前
MySQL联合索引遇到范围查询
数据库·索引
江畔柳前堤1 天前
roLabelImg 详细安装教程
开发语言·人工智能·后端·云原生
敲个大西瓜1 天前
Redis一百道核心面试题
数据库·redis·缓存
阿里云大数据AI技术1 天前
EMR Serverless Spark AI Function 的双维降本实践
人工智能·sql·spark
陈随易1 天前
moon,apt和yum之外linux系统命令安装新选择
前端·后端·程序员
码事漫谈1 天前
当单库日增 4.5TB,异构实时同步还能稳得住吗?
后端
無a伟1 天前
Redis持久化详解:RDB与AOF原理、配置、优缺点
数据库
IT_陈寒1 天前
SpringBoot自动配置的坑,这次真踩疼我了
前端·人工智能·后端
shengjk11 天前
AI Engineering 五代演进史:Prompt、RAG、Agent 到 Graph 的架构革命
后端
子兮曰1 天前
AI 浏览器 Agent 的安全困局:当自动化工具可以偷走你的登录态
前端·人工智能·后端