PostgreSQL 中序列(bigserial 和手动序列)的使用与注意事项

在 PostgreSQL 中,序列(bigserial 和手动创建的序列)是生成唯一标识符(如主键)的常用工具。然而,在实际开发中,使用序列时可能会遇到一些问题,例如如何选择合适的序列方式、如何处理用户自定义 id 的情况,以及如何合理地重置序列。本文将详细探讨这些问题,并提供最佳实践建议。

1. bigserial 和手动创建序列的对比

在 PostgreSQL 中,定义主键自增的方式主要有两种:使用 bigserial 和手动创建序列后使用 DEFAULT nextval('sequence_name')。这两种方式各有优缺点,适用于不同的场景。

使用 bigserial

表结构:

sql

复制

复制代码
CREATE TABLE excel_handle_log (
    id bigserial PRIMARY KEY,
    ...
);
优点:
  1. 简洁性

    • bigserial 是 PostgreSQL 提供的便捷类型,它自动创建一个序列,并将其与列关联。使用 bigserial 时,代码更简洁。

    • 示例

      复制代码
      CREATE TABLE excel_handle_log (
          id bigserial PRIMARY KEY,
          ...
      );
  2. 自动管理

    • 使用 bigserial 时,PostgreSQL 会自动创建一个名为 <table_name>_<column_name>_seq 的序列,并将其与列关联。这意味着你不需要手动管理序列的创建和命名,减少了出错的可能性。
  3. 默认值自动设置

    • bigserial 自动为列设置默认值为 nextval('<sequence_name>'),因此在插入数据时,如果未显式指定 id 的值,系统会自动分配下一个序列值。
缺点:
  1. 灵活性较低

    • 如果需要对序列进行更复杂的自定义(例如,设置不同的起始值、增量等),使用 bigserial 可能不够灵活。

    • 例如,bigserial 默认从 1 开始,增量为 1,如果需要其他设置,需要手动调整序列。

  2. 命名限制

    • 自动创建的序列名称遵循固定的命名规则,如果需要更灵活的命名方式,bigserial 无法满足需求。

手动创建序列并使用 DEFAULT nextval('sequence_name')

表结构:
复制代码
CREATE SEQUENCE excel_handle_log_id_seq;
CREATE TABLE excel_handle_log (
    id BIGINT PRIMARY KEY DEFAULT nextval('excel_handle_log_id_seq'),
    ...
);
优点:
  1. 灵活性高

    • 手动创建序列时,可以自定义序列的起始值、增量、最小值、最大值等属性。

    • 例如,可以设置 START WITH 100 INCREMENT BY 10,以满足特定的业务需求。

  2. 命名自由

    • 可以自由命名序列,使其更符合项目规范或业务需求。

    • 例如,序列名称可以更具描述性,如 excel_handle_log_id_seq_v2,便于区分不同版本或用途的序列。

  3. 便于跨表共享序列

    • 如果多个表需要共享同一个序列,手动创建序列的方式更合适。

    • 例如,多个表可以使用同一个序列来生成唯一标识符,确保全局唯一性。

缺点:
  1. 代码冗余

    • 需要手动创建序列,并在表定义中显式指定 DEFAULT nextval('sequence_name'),代码相对冗长。

    • 示例:

      复制代码
      CREATE SEQUENCE excel_handle_log_id_seq START WITH 100 INCREMENT BY 10;
      CREATE TABLE excel_handle_log (
          id BIGINT PRIMARY KEY DEFAULT nextval('excel_handle_log_id_seq'),
          ...
      );
  2. 管理复杂

    • 手动创建序列时,需要手动管理序列的命名和关联,增加了出错的可能性。

    • 例如,如果忘记在表定义中设置默认值,可能会导致插入数据时无法自动生成 id

推荐选择

  • 如果你追求简洁性和默认的自增功能

    • 选择 bigserial。它简单易用,自动处理序列的创建和关联,适合大多数标准的自增主键场景。

    • 示例

      复制代码
      CREATE TABLE excel_handle_log (
          id bigserial PRIMARY KEY,
          ...
      );
  • 如果你需要更高的灵活性,例如自定义序列属性或跨表共享序列

    • 选择手动创建序列。这种方式虽然代码稍显冗长,但提供了更高的灵活性和控制能力。

    • 示例

      复制代码
      CREATE SEQUENCE excel_handle_log_id_seq START WITH 100 INCREMENT BY 10;
      CREATE TABLE excel_handle_log (
          id BIGINT PRIMARY KEY DEFAULT nextval('excel_handle_log_id_seq'),
          ...
      );

: PostgreSQL 官方文档 -


2. 重置序列的合理性分析

在实际开发中,使用 ALTER SEQUENCE ... RESTART WITH 来重置序列的值是一个常见的操作,但它是否合理取决于具体的业务场景和需求。

合理的场景

1. 数据清理和重新初始化

当你清理了表中的数据并希望重新开始时,重置序列是一个合理的操作。例如,你可能在开发环境中频繁地清空表并重新插入测试数据,此时重置序列可以确保主键从一个固定的值开始,便于测试和调试。

示例:

复制代码
-- 清空表
TRUNCATE TABLE excel_handle_log RESTART IDENTITY;

-- 重置序列
ALTER SEQUENCE excel_handle_log_id_seq RESTART WITH 200;
2. 业务需求变更

如果业务需求发生变化,需要从一个新的起始值开始生成主键,重置序列是必要的。例如,公司政策变更,要求新的记录从某个特定编号开始。

示例:

复制代码
-- 重置序列
ALTER SEQUENCE excel_handle_log_id_seq RESTART WITH 200;
3. 修复序列值

如果由于某些原因(如手动插入数据导致序列值混乱),需要修复序列值,重置序列是一个有效的手段。

示例:

复制代码
-- 重置序列
ALTER SEQUENCE excel_handle_log_id_seq RESTART WITH 200;

潜在风险

1. 数据一致性问题

如果表中已经存在数据,重置序列可能会导致主键冲突。例如,如果表中已经存在 id 为 200 的记录,重置序列后再次插入数据可能会导致主键冲突。

解决方案: 在重置序列之前,确保表中不存在与新起始值冲突的记录。

2. 并发问题

在高并发环境下,重置序列可能会导致并发问题。如果多个会话同时插入数据,重置序列可能会导致一些会话获取到重复的主键值。

解决方案: 在重置序列时,确保没有其他会话正在插入数据,或者在重置序列后等待所有现有会话完成操作。

3. 依赖关系

如果序列被多个表或多个字段引用,重置序列可能会影响其他依赖关系。例如,如果多个表共享同一个序列,重置序列可能会导致其他表的主键生成出现问题。

解决方案: 在重置序列之前,检查所有依赖该序列的表和字段,确保不会影响其他表的正常操作。

实际操作建议

1. 备份数据

在重置序列之前,建议备份相关数据,以防万一。

2. 检查表数据

在重置序列之前,检查表中是否存在与新起始值冲突的记录。

3. 锁定表

在重置序列时,可以锁定表以防止并发问题。

示例:

复制代码
-- 锁定表
BEGIN;
LOCK TABLE excel_handle_log IN EXCLUSIVE MODE;

-- 重置序列
ALTER SEQUENCE excel_handle_log_id_seq RESTART WITH 200;

-- 提交事务
COMMIT;
4. 监控和测试

在重置序列后,监控系统的运行情况,确保没有出现主键冲突或其他问题。

: PostgreSQL 官方文档 -


3. 插入数据时序列的行为分析

在使用 bigserial 或手动创建序列时,插入数据时的行为取决于是否显式指定了 id 的值。

使用 bigserial

表结构:
复制代码
CREATE TABLE excel_handle_log (
    id bigserial PRIMARY KEY,
    ...
);
插入数据时的行为:
  • 显式指定 id : 当你显式插入一个 id 值时,PostgreSQL 不会使用序列生成的值,而是直接使用你指定的值。此时,序列的值不会递增。

    示例

    复制代码
    INSERT INTO excel_handle_log (id, ...) VALUES (100, ...);

    在这种情况下,序列不会递增,下一个 id 值仍然是序列的下一个值。

  • 不指定 id : 当你没有显式指定 id 值时,PostgreSQL 会自动调用序列生成下一个值。

    示例:

    复制代码
    INSERT INTO excel_handle_log (...) VALUES (...);

    在这种情况下,序列会递增。

手动创建序列并使用 DEFAULT nextval('sequence_name')

表结构:
复制代码
CREATE SEQUENCE excel_handle_log_id_seq;
CREATE TABLE excel_handle_log (
    id BIGINT PRIMARY KEY DEFAULT nextval('excel_handle_log_id_seq'),
    ...
);
插入数据时的行为:
  • 显式指定 id : 当你显式插入一个 id 值时,PostgreSQL 不会使用默认值(即不会调用 nextval()),而是直接使用你指定的值。此时,序列的值不会递增。

    示例

    复制代码
    INSERT INTO excel_handle_log (id, ...) VALUES (100, ...);

    在这种情况下,序列不会递增,下一个 id 值仍然是序列的下一个值。

  • 不指定 id : 当你没有显式指定 id 值时,PostgreSQL 会使用默认值,即调用 nextval('sequence_name')

    示例

    复制代码
    INSERT INTO excel_handle_log (...) VALUES (...);

    在这种情况下,序列会递增。

注意事项

  1. 潜在风险

    • 如果用户显式插入的 id 值与序列生成的值冲突(例如,用户插入了一个已经存在的 id),会导致主键冲突错误。

    • 如果用户插入的 id 值小于序列的当前值,可能会导致后续插入操作失败(因为序列生成的值可能会与用户插入的值冲突)。

  2. 建议

    • 如果希望用户能够显式插入 id 值,建议在表设计时明确文档说明,告知用户可能的风险。

    • 如果不希望用户显式插入 id 值,可以在表上设置触发器或使用应用逻辑来阻止用户插入 id

: PostgreSQL 官方文档 -


总结

在 PostgreSQL 中,序列(bigserial 和手动序列)是生成唯一标识符的重要工具。选择合适的序列方式取决于具体的业务需求,bigserial 更适合简洁的自增主键场景,而手动创建序列则提供了更高的灵活性。在重置序列时,需要谨慎处理数据一致性和并发问题。在插入数据时,显式指定 id 值会导致序列不递增,这需要特别注意潜在的冲突风险。通过合理使用序列,可以有效提升数据库操作的效率和可靠性。

希望本文能帮助你更好地理解和使用 PostgreSQL 的序列功能。如果你有任何问题或建议,欢迎在评论区留言讨论。

复制代码
SELECT
    last_value,
    increment_by,
    last_value + increment_by AS next_value
FROM
pg_sequences
    WHERE schemaname = 'schemaname' AND sequencename = 'sequencename';
相关推荐
霑潇雨19 小时前
题解 | 分析每个商品在不同时间段的销售情况
数据库·sql·算法·笔试
季明洵19 小时前
Java中哈希
java·算法·哈希
组合缺一19 小时前
Claude Code Agent Skills vs. Solon AI Skills:从工具增强到框架规范的深度对齐
java·人工智能·python·开源·solon·skills
学海无涯书山有路19 小时前
Android ViewBinding 新手详解(Java 版)—— 结合 ViewModel+LiveData 实战
android·java·开发语言
jaysee-sjc19 小时前
【练习十】Java 面向对象实战:智能家居控制系统
java·开发语言·算法·智能家居
Watermelo61719 小时前
随机扣款实现赛博共产主义,《明日方舟:终末地》公测支付事故复盘
数据库·后端·游戏程序·技术美术·用户体验·游戏策划·游戏美术
哪里不会点哪里.19 小时前
Spring Boot 启动原理深度解析
java·spring boot·后端
零基础的修炼19 小时前
算法---常见位运算总结
java·开发语言·前端
数据知道19 小时前
PostgreSQL 实战:行级安全策略(RLS)详解
数据库·postgresql
蜂蜜黄油呀土豆19 小时前
Java虚拟机内存模型解析与内存管理问题
java·jvm·内存管理·内存泄漏·内存溢出