暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PolarDB PostgreSQL 版 Sequence 解析(一)

PolarDB 2025-04-08
180

PolarDB PostgreSQL 版 Sequence 解析(一)

关于 PolarDB PostgreSQL 版

PolarDB PostgreSQL 版是一款阿里云自主研发的云原生关系型数据库产品,100% 兼容 PostgreSQL,高度兼容Oracle语法;采用基于 Shared-Storage 的存储计算分离架构,具有极致弹性、毫秒级延迟、HTAP 、Ganos全空间数据处理能力和高可靠、高可用、弹性扩展等企业级数据库特性。同时,PolarDB PostgreSQL 版具有大规模并行计算能力,可以应对 OLTP 与 OLAP 混合负载。

介绍

Sequence 作为数据库中的一个特别的表级对象,可以根据用户设定的不同属性,产生一系列有规则的整数,从而起到发号器的作用。

在使用方面,可以设置永不重复的 Sequence 用来作为一张表的主键,也可以通过不同表共享同一个 Sequence 来记录多个表的总插入行数。根据 ANSI 标准,一个 Sequence 对象在数据库要具备以下特征:

  1. 独立的数据库对象 (CREATE SEQUENCE),和表、视图同一层级

  2. 可以设置生成属性:初始值 (star value),步长 (increment),最大/小值 (max/min),循环产生 (cycle),缓存 (cache)等

  3. Sequence 对象在当前值的基础上进行递增或者递减,当前值被初始化为初始值

  4. 在设置循环后,当前值的变化具有周期性;不设置循环下,当前值的变化具有单调性,当前值到达最值后不可再变化

为了解释上述特性,我们分别定义 、 两种序列来举例其具体的行为。

CREATE SEQUENCE a start with 5 minvalue -1 increment -2;
CREATE SEQUENCE b start with 2 minvalue 1 maxvalue 4 cycle;

两个 Sequence 对象提供的序列值,随着序列申请次数的变化,如下所示:

PostgreSQL
Oracle
SQLSERVER
MySQL
MariaDB
DB2
Sybase
Hive
支持
支持
支持
仅支持自增字段
支持
支持
仅支持自增字段
不支持

为了更进一步了解 PostgreSQL 中的 Sequence 对象,我们先来了解 Sequence 的用法,并从用法中透析 Sequence 背后的设计原理。

使用方法

PostgreSQL 提供了丰富的 Sequence 调用接口,以及组合使用的场景,以充分支持开发者的各种需求。

SQL 接口

PostgreSQL 对 Sequence 对象也提供了类似于  的访问方式,即 DQL、DML 以及 DDL。我们从下图中可一览对外提供的 SQL 接口。

image

分别来介绍以下这几个接口:

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)

image

通过在  来设置好 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 其他组件进行使用,我们总结了一下几个常用的场景。

image

显式调用

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,而  调用仅仅是将默认值设置为一个  表达式。


文章转载自PolarDB,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论