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';
相关推荐
小猿姐4 小时前
实测对比:哪款开源 Kubernetes MySQL Operator 最值得用?(2026 深度评测)
数据库·mysql·云原生
一灯架构6 小时前
90%的人答错!一文带你彻底搞懂ArrayList
java·后端
倔强的石头_6 小时前
从 “存得下” 到 “算得快”:工业物联网需要新一代时序数据平台
数据库
网教盟人才服务平台7 小时前
“方班预备班盾立方人才培养计划”正式启动!
大数据·人工智能
Y4090017 小时前
【多线程】线程安全(1)
java·开发语言·jvm
TDengine (老段)7 小时前
TDengine IDMP 可视化 —— 分享
大数据·数据库·人工智能·时序数据库·tdengine·涛思数据·时序数据
布局呆星7 小时前
SpringBoot 基础入门
java·spring boot·spring
风吹迎面入袖凉8 小时前
【Redis】Redisson的可重入锁原理
java·redis
GottdesKrieges8 小时前
OceanBase数据库备份配置
数据库·oceanbase
w6100104668 小时前
cka-2026-ConfigMap
java·linux·cka·configmap