PolarDB PostgreSQL 版 Sequence 解析(一)
关于 PolarDB PostgreSQL 版
PolarDB PostgreSQL 版是一款阿里云自主研发的云原生关系型数据库产品,100% 兼容 PostgreSQL,高度兼容Oracle语法;采用基于 Shared-Storage 的存储计算分离架构,具有极致弹性、毫秒级延迟、HTAP 、Ganos全空间数据处理能力和高可靠、高可用、弹性扩展等企业级数据库特性。同时,PolarDB PostgreSQL 版具有大规模并行计算能力,可以应对 OLTP 与 OLAP 混合负载。
介绍
Sequence 作为数据库中的一个特别的表级对象,可以根据用户设定的不同属性,产生一系列有规则的整数,从而起到发号器的作用。
在使用方面,可以设置永不重复的 Sequence 用来作为一张表的主键,也可以通过不同表共享同一个 Sequence 来记录多个表的总插入行数。根据 ANSI 标准,一个 Sequence 对象在数据库要具备以下特征:
独立的数据库对象 (CREATE SEQUENCE),和表、视图同一层级
可以设置生成属性:初始值 (star value),步长 (increment),最大/小值 (max/min),循环产生 (cycle),缓存 (cache)等
Sequence 对象在当前值的基础上进行递增或者递减,当前值被初始化为初始值
在设置循环后,当前值的变化具有周期性;不设置循环下,当前值的变化具有单调性,当前值到达最值后不可再变化
为了解释上述特性,我们分别定义 、 两种序列来举例其具体的行为。
CREATE SEQUENCE a start with 5 minvalue -1 increment -2;
CREATE SEQUENCE b start with 2 minvalue 1 maxvalue 4 cycle;
两个 Sequence 对象提供的序列值,随着序列申请次数的变化,如下所示:
为了更进一步了解 PostgreSQL 中的 Sequence 对象,我们先来了解 Sequence 的用法,并从用法中透析 Sequence 背后的设计原理。
使用方法
PostgreSQL 提供了丰富的 Sequence 调用接口,以及组合使用的场景,以充分支持开发者的各种需求。
SQL 接口
PostgreSQL 对 Sequence 对象也提供了类似于 表 的访问方式,即 DQL、DML 以及 DDL。我们从下图中可一览对外提供的 SQL 接口。

分别来介绍以下这几个接口:
currval
该接口的含义为,返回 Session 上次使用的某一 Sequence 的值。
postgres=# select nextval('seq');
nextval
---------
2
(1 row)
postgres=# select currval('seq');
currval
---------
2
(1 row)
需要注意的是,使用该接口必须使用过一次 方法,否则会提示目标 Sequence 在当前 Session 未定义。
postgres=# select currval('seq');
ERROR: currval of sequence "seq" is not yet defined in this session
lastval
该接口的含义为,返回 Session 上次使用的 Sequence 的值。
postgres=# select nextval('seq');
nextval
---------
3
(1 row)
postgres=# select lastval();
lastval
---------
3
(1 row)
同样,为了知道上次用的是哪个 Sequence 对象,需要用一次 ,让 Session 以全局变量的形式记录下上次使用的 Sequence 对象。
与 两个接口仅仅只是参数不同, 需要指定是哪个访问过的 Sequence 对象,而 无法指定,只能是最近一次使用的 Sequence 对象。
nextval
该接口的含义为,取 Sequence 对象的下一个序列值。
通过使用 方法,可以让数据库基于 Sequence 对象的当前值,返回一个递增了 数量的一个序列值,并将递增后的值作为 Sequence 对象当前值。
postgres=# CREATE SEQUENCE seq start with 1 increment 2;
CREATESEQUENCE
postgres=# select nextval('seq');
nextval
---------
1
(1row)
postgres=# select nextval('seq');
nextval
---------
3
(1row)
称作 Sequence 对象的步长,Sequence 的每次以 的方式进行申请,都是以步长为单位进行申请的。同时,需要注意的是,Sequence 对象创建好以后,第一次申请获得的值,是 start value 所定义的值。对于 start value 的默认值,有以下 PostgreSQL 规则:
另外, 是一种特殊的 DML,其不受事务所保护,即:申请出的序列值不会再回滚。
postgres=# BEGIN;
BEGIN
postgres=# select nextval('seq');
nextval
---------
1
(1row)
postgres=# ROLLBACK;
ROLLBACK
postgres=# select nextval('seq');
nextval
---------
2
(1row)
PostgreSQL 为了 Sequence 对象可以获得较好的并发性能,并没有采用多版本的方式来更新 Sequence 对象,而是采用了原地修改的方式完成 Sequence 对象的更新,这种不用事务保护的方式几乎成为所有支持 Sequence 对象的 RDMS 的通用做法,这也使得 Sequence 成为一种特殊的表级对象。
setval
该接口的含义是,设置 Sequence 对象的序列值。
postgres=# select nextval('seq');
nextval
---------
4
(1 row)
postgres=# select setval('seq', 1);
setval
--------
1
(1 row)
postgres=# select nextval('seq');
nextval
---------
2
(1 row)
该方法可以将 Sequence 对象的序列值设置到给定的位置,同时可以将第一个序列值申请出来。如果不想申请出来,可以采用加入 参数的做法。
postgres=# select nextval('seq');
nextval
---------
4
(1 row)
postgres=# select setval('seq', 1, false);
setval
--------
1
(1 row)
postgres=# select nextval('seq');
nextval
---------
1
(1 row)

通过在 来设置好 Sequence 对象的值以后,同时来设置 Sequence 对象的 属性。 就可以根据 Sequence 对象的 属性来判断要返回的是否要返回设置的序列值。即:如果 为 , 接口会去设置 为 ,而不是进行 increment。
CREATE/ALTER SEQUENCE
和 用于创建/变更 Sequence 对象,其中 Sequence 属性也通过 和 接口进行设置,前面已简单介绍部分属性,下面将详细描述具体的属性。
CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IFNOTEXISTS ] name
[ AS data_type ]
[ INCREMENT [ BY ] increment ]
[ MINVALUEminvalue | NOMINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]
[ START [ WITH ] start ] [ CACHEcache ] [ [ NO ] CYCLE ]
[ OWNED BY { table_name.column_name | NONE } ]
ALTERSEQUENCE [ IFEXISTS ] name
[ AS data_type ]
[ INCREMENT [ BY ] increment ]
[ MINVALUEminvalue | NOMINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]
[ START [ WITH ] start ]
[ RESTART [ [ WITH ] restart ] ]
[ CACHEcache ] [ [ NO ] CYCLE ]
[ OWNED BY { table_name.column_name | NONE } ]
:设置 Sequence 的数据类型,只可以设置为 ,,;与此同时也限定了 和 的设置范围,默认为 类型(注意,只是限定,而不是设置,设置的范围不得超过数据类型的范围)。
:步长, 申请序列值的递增数量,默认值为 1。
/ :设置/不设置 Sequence 对象的最小值,如果不设置则是数据类型规定的范围,例如 类型,则最小值设置为 (-9223372036854775808)
/ :设置/不设置 Sequence 对象的最大值,如果不设置,则默认设置规则如上。
:Sequence 对象的初始值,必须在 和 范围之间。
:ALTER 后,可以重新设置 Sequence 对象的序列值,默认设置为 start value。
/ :设置 Sequence 对象使用的 Cache 大小, 或者不设置则默认为 1。
:设置 Sequence 对象归属于某张表的某一列,删除列后,Sequence 对象也将删除。
特殊场景下的序列回滚
下面描述了一种序列回滚的场景
CREATE SEQUENCE
postgres=# BEGIN;
BEGIN
postgres=# ALTER SEQUENCE seq maxvalue 10;
ALTERSEQUENCE
postgres=# select nextval('seq');
nextval
---------
1
(1row)
postgres=# select nextval('seq');
nextval
---------
2
(1row)
postgres=# ROLLBACK;
ROLLBACK
postgres=# select nextval('seq');
nextval
---------
1
(1row)
与之前描述的不同,此处 Sequence 对象受到了事务的保护,序列值发生了发生回滚。实际上,此处事务保护的是 (DDL),而非 (DML),因此此处发生的回滚是将 Sequence 对象回滚到 之前的状态,故发生了序列回滚现象。
DROP/TRUNCATE
,如字面意思,去除数据库中的 Sequence 对象。
,准确来讲,是通过 完成 。
postgres=# CREATE TABLE tbl_iden (i INTEGER, j int GENERATED ALWAYS AS IDENTITY);
CREATETABLE
postgres=# insert into tbl_iden values (100);
INSERT01
postgres=# insert into tbl_iden values (1000);
INSERT01
postgres=# select * from tbl_iden;
i | j
------+---
100 | 1
1000 | 2
(2rows)
postgres=# TRUNCATE TABLE tbl_iden RESTART IDENTITY;
TRUNCATETABLE
postgres=# insert into tbl_iden values (1234);
INSERT01
postgres=# select * from tbl_iden;
i | j
------+---
1234 | 1
(1row)
此处相当于在 表的时候,执行 。
Sequence 组合使用场景
SEQUENCE 除了作为一个独立的对象时候以外,还可以组合其他 PostgreSQL 其他组件进行使用,我们总结了一下几个常用的场景。

显式调用
CREATE SEQUENCE seq;
CREATE TABLE tbl (i INTEGER PRIMARY KEY);
INSERT INTO tbl (i) VALUES (nextval('seq'));
SELECT * FROM tbl ORDER BY 1 DESC;
tbl
---------
1
(1 row)
触发器调用
CREATE SEQUENCE seq;
CREATETABLE tbl (i INTEGER PRIMARY KEY, j INTEGER);
CREATEFUNCTION f()
RETURNSTRIGGERAS
$$
BEGIN
NEW.i := nextval('seq');
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';
CREATETRIGGER tg
BEFOREINSERTON tbl
FOREACHROW
EXECUTEPROCEDURE f();
INSERTINTO tbl (j) VALUES (4);
SELECT * FROM tbl;
i | j
---+---
1 | 4
(1 row)
DEFAULT 调用
显式 调用:
CREATE SEQUENCE seq;
CREATETABLE tbl(i INTEGERDEFAULTnextval('seq') PRIMARY KEY, j INTEGER);
INSERTINTO tbl (i,j) VALUES (DEFAULT,11);
INSERTINTO tbl(j) VALUES (321);
INSERTINTO tbl (i,j) VALUES (nextval('seq'),1);
SELECT * FROM tbl;
i | j
---+-----
2 | 321
1 | 11
3 | 1
(3 rows)
调用:
CREATE TABLE tbl (i SERIAL PRIMARY KEY, j INTEGER);
INSERT INTO tbl (i,j) VALUES (DEFAULT,42);
INSERT INTO tbl (j) VALUES (25);
SELECT * FROM tbl;
i | j
---+----
1 | 42
2 | 25
(2 rows)
注意, 并不是一种类型,而是 调用的另一种形式,只不过 会自动创建 约束所要使用的 Sequence。
AUTO_INC 调用
CREATE TABLE tbl (i intGENERATEDALWAYSASIDENTITY,
j INTEGER);
INSERTINTO tbl(i,j) VALUES (DEFAULT,32);
INSERTINTO tbl(j) VALUES (23);
SELECT * FROM tbl;
i | j
---+----
1 | 32
2 | 23
(2 rows)
调用对列附加了自增约束,与 约束不同,自增约束通过查找 dependency 的方式找到该列关联的 Sequence,而 调用仅仅是将默认值设置为一个 表达式。




