PostgreSQL AUTO INCREMENT(自动增长)

下面是一份 PostgreSQL 自动增长(AUTO INCREMENT)完整实战手册,涵盖 序列(SEQUENCE)、SERIALIDENTITY、自定义、并发安全、性能、迁移、常见陷阱与最佳实践,适合开发、DBA、架构师使用。


一、PostgreSQL 的自动增长机制

PostgreSQL 不使用 AUTO_INCREMENT 关键字(那是 MySQL 的写法),而是使用 序列(SEQUENCE) + SERIAL / IDENTITY

方式说明
SERIAL老版本兼容(内部创建序列)
IDENTITYSQL 标准,推荐(PostgreSQL 10+)
SEQUENCE底层对象,可独立管理

二、三种实现方式对比

方式语法是否标准是否可控推荐
SERIALid SERIAL一般兼容旧项目
BIGSERIALid BIGSERIAL一般大数据量
IDENTITYid GENERATED ALWAYS AS IDENTITY强烈推荐

三、基本使用示例

1. SERIAL(传统方式)

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name TEXT
);

-- 插入时自动增长
INSERT INTO users(name) VALUES ('Alice');
-- id = 1

INSERT INTO users(name) VALUES ('Bob');
-- id = 2

内部自动创建:users_id_seq

2. IDENTITY(推荐方式)

CREATE TABLE products (
    id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    name TEXT,
    price NUMERIC
);

-- 插入
INSERT INTO products(name, price) VALUES ('iPhone', 999);
-- id = 1

两种 IDENTITY 模式

模式说明
GENERATED ALWAYS默认,禁止手动插入
GENERATED BY DEFAULT允许手动插入(若不填则自动)
-- 允许手动指定 ID
CREATE TABLE logs (
    id BIGINT GENERATED BY DEFAULT AS IDENTITY,
    message TEXT
);

INSERT INTO logs(id, message) VALUES (100, 'Custom ID');

四、序列(SEQUENCE)底层原理

-- 查看自动创建的序列
SELECT pg_get_serial_sequence('users', 'id');
-- 返回: public.users_id_seq

-- 直接操作序列
SELECT nextval('users_id_seq');  -- 获取下一个值
SELECT setval('users_id_seq', 100);  -- 设置当前值
SELECT currval('users_id_seq');  -- 获取当前值(事务内)

手动创建序列

CREATE SEQUENCE my_seq
    START WITH 1000
    INCREMENT BY 1
    MINVALUE 1
    MAXVALUE 999999
    CACHE 10;

CREATE TABLE orders (
    id INT PRIMARY KEY DEFAULT nextval('my_seq'),
    total NUMERIC
);

五、IDENTITY vs SERIAL 详细对比

特性SERIALIDENTITY
SQL 标准
可显式插入允许ALWAYS 禁止,BY DEFAULT 允许
序列名固定是(table_col_seq)是(系统命名)
可重用序列困难容易
迁移兼容性需 PostgreSQL 10+
推荐度3 stars5 stars

六、常见操作:重置、跳跃、修复

1. 重置 ID 从 1 开始

-- SERIAL 表
TRUNCATE TABLE users RESTART IDENTITY;

-- 或手动
SELECT setval(pg_get_serial_sequence('users', 'id'), 1, false);

2. 修复 ID 空洞(不推荐频繁操作)

-- 重新编号(慎用!影响外键)
WITH ranked AS (
    SELECT id, row_number() OVER (ORDER BY id) AS rn
    FROM users
)
UPDATE users u
SET id = r.rn
FROM ranked r
WHERE u.id = r.id;

3. 跳过一段 ID(预留)

SELECT setval('users_id_seq', 10000);
-- 下一个 INSERT 从 10001 开始

七、并发安全与性能

场景行为
高并发插入安全(序列是事务安全的)
事务回滚ID 不回退(序列已分配)
缓存(CACHE)提升性能,但回滚会造成空洞
-- 创建高性能序列
CREATE SEQUENCE fast_seq CACHE 100;

-- 100 个值预分配,减少锁竞争

空洞是正常现象,不要试图消除


八、与外键、复制、迁移

1. 外键引用

CREATE TABLE orders (
    id BIGSERIAL PRIMARY KEY,
    user_id INT REFERENCES users(id)
);
-- 没问题

2. 逻辑复制 / pg_dump

  • IDENTITY 列会自动处理
  • SERIAL 需注意序列权限
-- 导出时包含序列
pg_dump -Fc -f backup.dump dbname

3. MySQL 迁移到 PostgreSQL

-- MySQL
id INT AUTO_INCREMENT PRIMARY KEY

-- 转为 PostgreSQL
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY

九、查看与管理序列

-- 查看所有序列
SELECT * FROM information_schema.sequences;

-- 查看系统序列
SELECT schemaname, sequencename, last_value
FROM pg_sequences;

-- 修改序列
ALTER SEQUENCE users_id_seq
    INCREMENT BY 2
    MINVALUE 1
    MAXVALUE 1000000
    RESTART WITH 1;

十、常见错误与避坑

错误原因解决
ERROR: duplicate key value violates unique constraint手动插入冲突BY DEFAULTOVERRIDING SYSTEM VALUE
sequence is not yet defined in this sessioncurrval() 未调用 nextval()nextval()
ID 跳跃事务回滚、CACHE正常现象
迁移后 ID 从 1 开始RESTART IDENTITYTRUNCATE ... RESTART IDENTITY
GENERATED ALWAYS 插入失败显式插入省略列或用 BY DEFAULT

插入时覆盖 IDENTITY(特殊场景)

INSERT INTO products(id, name)
VALUES (999, 'Legacy Product')
OVERRIDING SYSTEM VALUE;

十一、最佳实践脚本

1. 标准建表模板(推荐)

CREATE TABLE customers (
    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    email VARCHAR(255) UNIQUE NOT NULL,
    name TEXT,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- 触发器自动更新 updated_at
CREATE OR REPLACE FUNCTION update_timestamp()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_customers_updated_at
    BEFORE UPDATE ON customers
    FOR EACH ROW
    EXECUTE FUNCTION update_timestamp();

2. 安全插入(支持覆盖)

-- 允许导入旧数据
INSERT INTO customers(id, email, name)
VALUES (1001, 'old@example.com', 'Legacy User')
OVERRIDING SYSTEM VALUE;

3. 重置测试数据

TRUNCATE TABLE customers, orders, products RESTART IDENTITY CASCADE;

十二、性能优化建议

建议说明
BIGINT 而非 INT避免溢出
序列 CACHE 20~100提升插入性能
分区表用独立序列避免热点
避免 SELECT currval()RETURNING id
-- 插入并返回 ID(推荐)
INSERT INTO users(name) VALUES ('Tom')
RETURNING id;

十三、速查表

命令用途
SERIAL自动创建序列
GENERATED ALWAYS AS IDENTITY标准,禁止手动插入
GENERATED BY DEFAULT AS IDENTITY允许手动插入
nextval('seq')获取下一个值
setval('seq', n)设置当前值
TRUNCATE ... RESTART IDENTITY重置序列
OVERRIDING SYSTEM VALUE插入时覆盖 ID
RETURNING id获取插入的 ID

十四、决策树

graph TD
    A[需要自动增长 ID?] -->|是| B{是否 PostgreSQL 10+?}
    B -->|是| C[用 GENERATED ALWAYS AS IDENTITY]
    B -->|否| D[用 SERIAL]
    C --> E{是否需要手动插入?}
    E -->|是| F[用 BY DEFAULT]
    E -->|否| G[用 ALWAYS]

十五、常见面试题

问题答案
PostgreSQL 如何实现自增?使用 SEQUENCE
SERIALIDENTITY 区别?IDENTITY 是标准,SERIAL 是扩展
事务回滚后 ID 会回退吗?不会
如何插入指定 ID?OVERRIDING SYSTEM VALUE
如何查看当前序列值?SELECT last_value FROM seq_name

十六、迁移对照表(MySQL → PostgreSQL)

MySQLPostgreSQL
AUTO_INCREMENTGENERATED ALWAYS AS IDENTITY
INT AUTO_INCREMENT PRIMARY KEYINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
BIGINT AUTO_INCREMENTBIGINT GENERATED ALWAYS AS IDENTITY
INSERT IGNOREON CONFLICT DO NOTHING

需要我提供一个完整的 “多表 + 分区 + 序列 + 迁移脚本” 的生产级模板吗?

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注