前言

本文是 sequence 系列继三大数据库 sequence 之华山论剑 (Oracle PostgreSQL MySQL sequence 十年经验总结) 之后的第二篇,主要分享一下 PostgreSQL 中关于 sequence 的一些经验。

测试环境准备

以下测试是在 PostgreSQL 11 中进行。

通过以下 SQL 创建:

测试用户: alvin,普通用户,非 superuser

测试数据库: alvindb,owner 是 alvin

测试 schema: alvin,owner 也是 alvin

这里采用的是 user 与 schema 同名,结合默认的 search_path("$user", public),这样操作对象(table, sequence, etc.)时就不需要加 schema 前缀了。

postgres=# CREATE USER alvin WITH PASSWORD 'alvin';
CREATE ROLE
postgres=# CREATE DATABASE alvindb OWNER alvin;
CREATE DATABASE
postgres=# \c alvindb
You are now connected to database "alvindb" as user "postgres".
alvindb=# CREATE SCHEMA alvin AUTHORIZATION alvin;
CREATE SCHEMA
alvindb=# \c alvindb alvin
You are now connected to database "alvindb" as user "alvin".
alvindb=> SHOW search_path;
search_path
-----------------
"$user", public
(1 row)

创建 sequence 的两种方式

sequence 常规用途是用作主键序列的生成。下面通过通过创建 sequence 及表来讨论 sequence 创建方式。

创建 sequence 方式一 直接创建

下面是一种简单方式直接创建 sequence 及表。

alvindb=> CREATE SEQUENCE tb_test_sequence_test_id_seq;
CREATE SEQUENCE
alvindb=>
CREATE TABLE tb_test_sequence (
test_id INTEGER DEFAULT nextval('alvin.tb_test_sequence_test_id_seq') PRIMARY KEY,
create_time TIMESTAMP DEFAULT clock_timestamp()
);
CREATE TABLE

查看已创建的对象

alvindb=> \d
List of relations
Schema | Name | Type | Owner
--------+------------------------------+----------+-------
alvin | tb_test_sequence_test_id_seq | sequence | alvin
alvin | tb_test_sequence | table | alvin
(2 rows)

查看已创建对象的结构

alvindb=> \d tb_test_sequence
Table "alvin.tb_test_sequence"
Column | Type | Collation | Nullable | Default
-------------+-----------------------------+-----------+----------+---------------------------------------------------
test_id | integer | | not null | nextval('tb_test_sequence_test_id_seq'::regclass)
create_time | timestamp without time zone | | | clock_timestamp()
Indexes:
"tb_test_sequence_pkey" PRIMARY KEY, btree (test_id) alvindb=> \d tb_test_sequence_test_id_seq
Sequence "alvin.tb_test_sequence_test_id_seq"
Type | Start | Minimum | Maximum | Increment | Cycles? | Cache
--------+-------+---------+---------------------+-----------+---------+-------
bigint | 1 | 1 | 9223372036854775807 | 1 | no | 1 alvindb=>

此时,我们会注意到,问题一,列 tb_test_sequence.test_id 的类型是 integer,而创建的 sequence 默认类型是 bigint。

这样没有问题,但如果类型一致的话会更好。

接下来,我们 drop sequence 的话,会发现,由于表依赖 sequence,所以不能单独 drop sequence。

alvindb=> DROP SEQUENCE tb_test_sequence_test_id_seq;
ERROR: cannot drop sequence tb_test_sequence_test_id_seq because other objects depend on it
DETAIL: default value for column test_id of table tb_test_sequence depends on sequence tb_test_sequence_test_id_seq
HINT: Use DROP ... CASCADE to drop the dependent objects too.
alvindb=>

下面我们 drop 掉表 tb_test_sequence

alvindb=> DROP TABLE tb_test_sequence;
DROP TABLE
alvindb=> \d
List of relations
Schema | Name | Type | Owner
--------+------------------------------+----------+-------
alvin | tb_test_sequence_test_id_seq | sequence | alvin
(1 row)

可以看到,问题二,虽然表 drop 了,但 sequence 还在。

这样会有什么问题呢?

在一个大型的数据库系统中,我们可能会发现有好多孤立的 sequence,因为我们 drop 表时可能会忘记 drop 掉其对应的 sequence。

现在先手动 drop 掉 sequence。

alvindb=> DROP SEQUENCE tb_test_sequence_test_id_seq;
DROP SEQUENCE
alvindb=> \d
Did not find any relations.
alvindb=>

我们优化一下 SQL 来解决上述两个问题:

alvindb=> CREATE SEQUENCE tb_test_sequence_test_id_seq AS INTEGER;
CREATE SEQUENCE
alvindb=>
CREATE TABLE tb_test_sequence (
test_id INTEGER DEFAULT nextval('alvin.tb_test_sequence_test_id_seq') PRIMARY KEY,
create_time TIMESTAMP DEFAULT clock_timestamp()
);
CREATE TABLE
alvindb=> ALTER SEQUENCE tb_test_sequence_test_id_seq OWNED BY tb_test_sequence.test_id;
ALTER SEQUENCE

上述 SQL 的作用是:

  1. 创建 sequence 时指定类型,使列与 sequence 的类型保持一致

  2. 关联表的列与 sequence,使 drop 表或列时会自动 drop 与其关联的 sequence

查看表结构,

alvindb=> \d tb_test_sequence
Table "alvin.tb_test_sequence"
Column | Type | Collation | Nullable | Default
-------------+-----------------------------+-----------+----------+---------------------------------------------------
test_id | integer | | not null | nextval('tb_test_sequence_test_id_seq'::regclass)
create_time | timestamp without time zone | | | clock_timestamp()
Indexes:
"tb_test_sequence_pkey" PRIMARY KEY, btree (test_id) alvindb=> \d tb_test_sequence_test_id_seq
Sequence "alvin.tb_test_sequence_test_id_seq"
Type | Start | Minimum | Maximum | Increment | Cycles? | Cache
---------+-------+---------+------------+-----------+---------+-------
integer | 1 | 1 | 2147483647 | 1 | no | 1
Owned by: alvin.tb_test_sequence.test_id

可以看到,

  1. tb_test_sequence.test_id 与 sequence 的类型均为 integer
  2. sequence 下方多了 'Owned by',表示列与 sequence 已关联了。

下面 drop 表后,可以看到,sequence 也已被 drop 了。

alvindb=> DROP TABLE tb_test_sequence;
DROP TABLE
alvindb=> \d
Did not find any relations.

实际上,如果 drop 掉列 test_id,其关联的 sequence 也会被 drop

alvindb=> ALTER TABLE tb_test_sequence DROP COLUMN test_id;
ALTER TABLE
alvindb=> \d tb_test_sequence
Table "alvin.tb_test_sequence"
Column | Type | Collation | Nullable | Default
-------------+-----------------------------+-----------+----------+-------------------
create_time | timestamp without time zone | | | clock_timestamp()
alvindb=> \d
List of relations
Schema | Name | Type | Owner
--------+------------------+-------+-------
alvin | tb_test_sequence | table | alvin
(1 row)

创建 sequence 方式二 通过 serial 创建

下面通过一个 SQL 来实现与上面完全相同的效果。

alvindb=>
CREATE TABLE tb_test_sequence (
test_id SERIAL PRIMARY KEY,
create_time TIMESTAMP DEFAULT clock_timestamp()
);
CREATE TABLE

查看表结构,与方式一中完全一样。

alvindb=> \d
List of relations
Schema | Name | Type | Owner
--------+------------------------------+----------+-------
alvin | tb_test_sequence | table | alvin
alvin | tb_test_sequence_test_id_seq | sequence | alvin
(2 rows)
alvindb=> \d tb_test_sequence
Table "alvin.tb_test_sequence"
Column | Type | Collation | Nullable | Default
-------------+-----------------------------+-----------+----------+---------------------------------------------------
test_id | integer | | not null | nextval('tb_test_sequence_test_id_seq'::regclass)
create_time | timestamp without time zone | | | clock_timestamp()
Indexes:
"tb_test_sequence_pkey" PRIMARY KEY, btree (test_id) alvindb=> \d tb_test_sequence_test_id_seq
Sequence "alvin.tb_test_sequence_test_id_seq"
Type | Start | Minimum | Maximum | Increment | Cycles? | Cache
---------+-------+---------+------------+-----------+---------+-------
integer | 1 | 1 | 2147483647 | 1 | no | 1
Owned by: alvin.tb_test_sequence.test_id

这里总结一下一个单词 SERIAL 做了什么事情:

  1. 根据规则 tablename_colname_seq 创建 sequence,并设置 DEFAULT
  2. 增加 NOT NULL 约束
  3. 关联列与 sequence,使表或关联的列 drop 时,关联的 sequence 也会被 drop 掉

注:这里 SERIAL 和 PRIMARY KEY 之一都会默认增加 NOT NULL 约束

用 SERIAL 的确省了不少事,但它有什么问题吗?使用它会不会又引入了新的问题?

  1. SERIAL 对应的数据类型是 integer,作为主键的数据类型,integer 足够吗?
  2. 关联列与 sequence 后,drop 时是方便了,但同时会不会给运维带来新的问题?比如 rename 表,列或 sequence?
  3. 在复制表或迁移表时,又该对 sequence 作何操作呢?

接下来,我们从这几个问题出发进一步探讨。

serial 与 bigserial

serial 对应的是 integer,是 4 个字节,最大值是 2 147 483 647,即 21 亿左右。

作为大表主键的 sequence,21 亿真的够吗?按全球人口 70 亿算,一人一个数都不够。

为解决这个问题,可以用 bigserial,即 bigint,8 个字节,最大值是 9 223 372 036 854 775 807,即 922亿个亿左右。这对于绝大多数场景是足够了,这也是 PostgreSQL 中 sequence 的最大值。

使用 bigserial 创建表:

alvindb=>
CREATE TABLE tb_test_bigserial (
test_id BIGSERIAL PRIMARY KEY,
create_time TIMESTAMP DEFAULT clock_timestamp()
);
CREATE TABLE

查看表结构,

alvindb=> \d tb_test_bigserial
Table "alvin.tb_test_bigserial"
Column | Type | Collation | Nullable | Default
-------------+-----------------------------+-----------+----------+----------------------------------------------------
test_id | bigint | | not null | nextval('tb_test_bigserial_test_id_seq'::regclass)
create_time | timestamp without time zone | | | clock_timestamp()
Indexes:
"tb_test_bigserial_pkey" PRIMARY KEY, btree (test_id) alvindb=> \d tb_test_bigserial_test_id_seq
Sequence "alvin.tb_test_bigserial_test_id_seq"
Type | Start | Minimum | Maximum | Increment | Cycles? | Cache
--------+-------+---------+---------------------+-----------+---------+-------
bigint | 1 | 1 | 9223372036854775807 | 1 | no | 1
Owned by: alvin.tb_test_bigserial.test_id

可以看到,列 test_id 和 sequence 的 Type 都是 bigint。这样,sequence 的类型问题就解决了。

公众号

关注 DBA Daily 公众号,第一时间收到文章的更新。

通过一线 DBA 的日常工作,学习实用数据库技术干货!

公众号优质文章推荐

PostgreSQL VACUUM 之深入浅出

华山论剑之 PostgreSQL sequence

[PG Upgrade Series] Extract Epoch Trap

[PG Upgrade Series] Toast Dump Error

GitLab supports only PostgreSQL now

MySQL or PostgreSQL?

PostgreSQL hstore Insight

ReIndex 失败原因调查

PG 数据导入 Hive 乱码问题调查

PostGIS 扩展创建失败原因调查

华山论剑之 PostgreSQL sequence (一)的更多相关文章

  1. 华山论剑之 PostgreSQL sequence (二)

    rename 对 sequence 的影响 关联列与 sequence 后,即 sequence 属于该列后,drop 表或列时会自动 drop 相关 sequence. 但如果对表或列 rename ...

  2. 三大数据库 sequence 之华山论剑 (上篇)

    前言 本文将基于以下三种关系型数据库,对 sequence (序列) 展开讨论. Oracle - 应用最广泛的商用关系型数据库 PostgreSQL - 功能最强大的开源关系型数据库 MySQL - ...

  3. 三大数据库 sequence 之华山论剑 (中篇)

    sequence 用法四 AUTO INCREMENT 通过 DEFAULT 还是需要手动创建 sequence.有没有更简单的用法呢? 当然,就是通过 AUTO INCREMENT 方式,自动创建 ...

  4. 三大数据库 sequence 之华山论剑 (下篇)

    MySQL 5.7 MYISAM ENGINE 以下是 MySQL 5.7 MYISAM ENGINE 中的运行结果 mysql> CREATE TABLE tb_test5 ( -> t ...

  5. PostgreSQL VACUUM 之深入浅出 (一)

    前言 VACUUM 是 PostgreSQL MVCC (Multiversion concurrency control) 实现的核心机制之一,是 PostgreSQL 正常运行的重要保证.本文将通 ...

  6. PostgreSQL VACUUM 之深入浅出 (二)

    AUTOVACUUM AUTOVACUUM 简介 PostgreSQL 提供了 AUTOVACUUM 的机制. autovacuum 不仅会自动进行 VACUUM,也会自动进行 ANALYZE,以分析 ...

  7. PostgreSQL VACUUM 之深入浅出 (三)

    VACUUM 相关参数 对 VACUUM 有了一定的了解之后,下面系统介绍下 VACUUM 相关参数. VACUUM 相关参数主要分为三大类. 第一类 与资源相关参数 #--------------- ...

  8. PostgreSQL VACUUM 之深入浅出 (四)

    VACUUM 参数优化 上面已经介绍过了以下设置表级 AUTOVACUUM 相关参数和 autovacuum_max_workers: ALTER TABLE pgbench_accounts SET ...

  9. PostgreSQL 9.1 飞升之路

    PostgreSQL upgrade 以升级 PostgreSQL 9.1 至 PostgreSQL 11 (跨越 9.2.9.3.9.4.9.5.9.6.10 六个大版本) 为例,本文将分享一下过去 ...

随机推荐

  1. 学习Java第10天

    今天所作的工作: 1.线程 2.网络通信 明天工作安排: 做点教材后的案例,复习所学内容 总结一下,10天的时间,看完了Java从入门到精通,只能说是看完了,这里加一个表情笑哭,应该算是知道Java的 ...

  2. 从新建文件夹开始构建ShadowPlay Engine游戏引擎(6)

    本篇序言 在经历了为期很长时间的调试以及思维纠错后,我们可以开始实现我们的内存管理模块了,我在前面说过如果各位要继续跟着学习的话可能会需要一定的计算机组成原理和操作系统的知识,不过在莽代码的过程中,我 ...

  3. SQL解析器详解

    1.概述 最近,有同学留言关于SQL解析器方面的问题,今天笔者就为大家分享一下SQL解析器方便的一些内容. 2.内容 2.1 SQL解析器是什么? SQL解析与优化是属于编辑器方面的知识,与C语言这类 ...

  4. AT2645 [ARC076D] Exhausted?

    解法一 引理:令一个二分图两部分别为 \(X, Y(|X| \le |Y|)\),若其存在完美匹配当且仅当 \(\forall S \subseteq X, f(S) \ge |S|\)(其中 \(f ...

  5. TCP和UDP的区别以及应用

    TCP定义 传输控制协议 (Transmission Control Protocol).TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算 ...

  6. JAVA多线程学习七-线程池

    为什么用线程池 1.创建/销毁线程伴随着系统开销,过于频繁的创建/销毁线程,会很大程度上影响处理效率 例如: 记创建线程消耗时间T1,执行任务消耗时间T2,销毁线程消耗时间T3 如果T1+T3> ...

  7. Shell中退出状态码exit

    shell中运行的每个命令都使用退出状态码(exit status)来告诉shell它完成了处理.退出状态码是一个0~255之间的整数值,在命令结束时由命令传回shell. 1 .查看退出状态码 Li ...

  8. iOS,开发准备之申请证书 ---by吴帮雷

    一.申请真机调试证书 打开iOS Dev Center,选择Sign in,登陆(至少99美元账号),登陆选择Certificates,Identifiers & Profiles --> ...

  9. 《Effective Python》笔记——第1章 用Pythonic方式来思考

    一. 遵循PEP8风格指南. PEP8是对python代码格式而编订的风格指南.地址:https://www.python.org/dev/peps/pep-0008/ 个人觉得不一定完全按照PEP8 ...

  10. B快速导航

    GETTING STARTED If you are new to Selenium, we have a few resources that can help you get up to spee ...