本文分享自华为云社区《GaussDB(DWS)对象设计之序列SEQUENCE原理与使用方法介绍》,作者:VV一笑。

1. 前言

  • 适用版本:8.2.1及以上版本

序列SEQUENCE用来生成唯一整数的数据库对象,本文对序列SEQUENCE的使用场景、使用方法及相关函数进行了介绍,并针对序列SEQUENCE在使用中容易遇到的问题和对应的解决方法进行了梳理总结。

2. SEQUENCE——自增整数序列

序列Sequence是用来产生唯一整数的数据库对象。序列的值是按照一定规则自增的整数。因为自增所以不重复,因此说Sequence具有唯一标识性。因此,在数据库中Sequence常常被作为主键使用。

3. 创建序列

通过序列使某字段成为唯一标识符的方法有两种:

  • 是声明字段的类型为序列整型,由数据库在后台自动创建一个对应的Sequence。
  • 使用CREATE SEQUENCE自定义一个新的Sequence,然后将nextval(‘sequence_name’)函数读取的序列值,指定为某一字段的默认值,这样该字段就可以作为唯一标识符。

方法一: 声明字段类型为序列整型来定义标识符字段。例如:

postgres=# CREATE TABLE T1
(
id serial,
name text
);

方法二: 创建序列,并通过nextval(‘sequence_name’)函数指定为某一字段的默认值。这种方式更灵活,可以为序列定义cache,一次预申请多个序列值,减少与GTM的交互次数,来提高性能。
1.创建序列

postgres=# CREATE SEQUENCE seq1 cache 100;

2.指定为某一字段的默认值,使该字段具有唯一标识属性。

postgres=# CREATE TABLE T2
(
id int not null default nextval('seq1'),
name text
);

【注意】

除了为序列指定了cache,方法二所实现的功能基本与方法一类似。但是一旦定义cache,序列将会产生空洞(序列值为不连贯的数值,如:1.4.5),并且不能保序。另外为某序列指定从属列后,该列删除,对应的sequence也会被删除。 虽然数据库并不限制序列只能为一列产生默认值,但最好不要多列共用同一个序列。
当前版本只支持在定义表的时候指定自增列,或者指定某列的默认值为nextval(‘seqname’), 不支持在已有表中增加自增列或者增加默认值为nextval(‘seqname’)的列。

3.1 CREATE SEQUENCE语句的使用方法

CREATE SEQUENCE用于向当前数据库里增加一个新的序列。序列的Owner为创建此序列的用户。

注意事项

  • Sequence是一个存放等差数列的特殊表,该表受DBMS控制。这个表没有实际意义,通常用于为行或者表生成唯一的标识符。
  • 如果给出一个模式名,则该序列就在给定的模式中创建,否则会在当前模式中创建。序列名必须和同一个模式中的其他序列、表、索引、视图或外表的名字不同。
  • 创建序列后,在表中使用序列的nextval()函数和generate_series(1,N)函数对表插入数据,请保证nextval的可调用次数大于等于N+1次,否则会因为generate_series()函数会调用N+1次而导致报错。
  • 不支持在template1数据库中创建SEQUENCE。

语法格式

CREATE SEQUENCE name [ INCREMENT [ BY ] increment ]
[ MINVALUE minvalue | NO MINVALUE | NOMINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE | NOMAXVALUE]
[ START [ WITH ] start ] [ CACHE cache ] [ [ NO ] CYCLE | NOCYCLE ]
[ OWNED BY { table_name.column_name | NONE } ];

参数说明

  • name

将要创建的序列名称。

取值范围: 仅可以使用小写字母(a~z)、 大写字母(A~Z),数字和特殊字符"#","_","$"的组合。

  • increment

指定序列的步长。一个正数将生成一个递增的序列,一个负数将生成一个递减的序列。

缺省值为1。

  • MINVALUE minvalue | NO MINVALUE| NOMINVALUE

执行序列的最小值。如果没有声明minvalue或者声明了NO MINVALUE,则递增序列的缺省值为1,递减序列的缺省值为-263-1。NOMINVALUE等价于NO MINVALUE

  • MAXVALUE maxvalue | NO MAXVALUE| NOMAXVALUE

执行序列的最大值。如果没有声明maxvalue或者声明了NO MAXVALUE,则递增序列的缺省值为263-1,递减序列的缺省值为-1。NOMAXVALUE等价于NO MAXVALUE

start

指定序列的起始值。缺省值:对于递增序列为minvalue,递减序列为maxvalue。

cache

为了快速访问,而在内存中预先存储序列号的个数。一个缓存周期内,CN不再向GTM索取序列号,而是使用本地预先申请的序列号。

缺省值为1,表示一次只能生成一个值,也就是没有缓存。

【注意】

不建议同时定义cache和maxvalue或minvalue。因为定义cache后不能保证序列的连续性,可能会产生空洞,造成序列号段浪费。

建议cache值不要设置过大,否则会出现缓存序列号时(每个cache周期的第一个nextval)耗时过长的情况;同时建议cache值小于100000000。实际使用时应根据业务设置合理的cache值,既能保证快速访问,又不会浪费序列号。

CYCLE

用于使序列达到maxvalue或者minvalue后可循环并继续下去。

如果声明了NO CYCLE,则在序列达到其最大值后任何对nextval的调用都会返回一个错误。

NOCYCLE的作用等价于NO CYCLE。

缺省值为NO CYCLE。

若定义序列为CYCLE,则不能保证序列的唯一性。

OWNED BY-

将序列和一个表的指定字段进行关联。这样,在删除那个字段或其所在表的时候会自动删除已关联的序列。关联的表和序列的所有者必须是同一个用户,并且在同一个模式中。需要注意的是,通过指定OWNED BY,仅仅是建立了表的对应列和sequence之间关联关系,并不会在插入数据时在该列上产生自增序列。

缺省值为OWNED BY NONE,表示不存在这样的关联。

【注意】

通过OWNED BY创建的Sequence不建议用于其他表,如果希望多个表共享Sequence,该Sequence不应该从属于特定表。

示例

创建一个从101开始的递增序列,名为serial:

CREATE SEQUENCE serial
START 101
CACHE 20;

从序列中选出下一个数字:

SELECT nextval('serial');
nextval
---------
101

从序列中选出下一个自增数字:

SELECT nextval('serial');
nextval
---------
102

创建与表关联的序列:

CREATE TABLE customer_address
(
ca_address_sk integer not null,
ca_address_id char(16) not null,
ca_street_number char(10) ,
ca_street_name varchar(60) ,
ca_street_type char(15) ,
ca_suite_number char(10) ,
ca_city varchar(60) ,
ca_county varchar(30) ,
ca_state char(2) ,
ca_zip char(10) ,
ca_country varchar(20) ,
ca_gmt_offset decimal(5,2) ,
ca_location_type char(20)
) ; CREATE SEQUENCE serial1
START 101
CACHE 20
OWNED BY customer_address.ca_address_sk;

使用serial创建主键自增序列表serial_table:

CREATE TABLE serial_table(a int, b serial);
INSERT INTO serial_table (a) VALUES (1),(2),(3);
SELECT * FROM serial_table ORDER BY b;
a | b
---+---
1 | 1
2 | 2
3 | 3
(3 rows)

4. 修改序列

ALTER SEQUENCE命令更改现有序列的属性,包括修改修改拥有者、归属列和最大值。

指定序列与列的归属关系:将序列和一个表的指定字段进行关联。在删除那个字段或其所在表的时候会自动删除已关联的序列。

postgres=# ALTER SEQUENCE seq1 OWNED BY T2.id;

将序列serial的最大值修改为300:

ALTER SEQUENCE seq1 MAXVALUE 300;

4.1 ALTER SEQUENCE语句的使用方法

ALTER SEQUENCE用于修改一个现有的序列的参数。

注意事项

  • 使用ALTER SEQUENCE的用户必须是该序列的所有者。
  • 当前版本仅支持修改拥有者、归属列和最大值。若要修改其他参数,可以删除重建,并用Setval函数恢复当前值。
  • ALTER SEQUENCE MAXVALUE不支持在事务、函数和存储过程中使用。
  • 修改序列的最大值后,会清空该序列在所有会话的cache。
  • ALTER SEQUENCE会阻塞nextval、setval、currval和lastval的调用。

语法格式

修改序列最大值或归属列

ALTER SEQUENCE [ IF EXISTS ] name
[ MAXVALUE maxvalue | NO MAXVALUE | NOMAXVALUE ]
[ OWNED BY { table_name.column_name | NONE } ] ;

修改序列的拥有者

ALTER SEQUENCE [ IF EXISTS ] name OWNER TO new_owner;

参数说明

  • name

将要修改的序列名称。

  • IF EXISTS

当序列不存在时使用该选项不会出现错误消息,仅有一个通知。

  • MAXVALUE maxvalue | NO MAXVALUE

序列所能达到的最大值。如果声明了NO MAXVALUE,则递增序列的缺省值为263-1,递减序列的缺省值为-1。NOMAXVALUE等价于NO MAXVALUE。

  • OWNED BY

将序列和一个表的指定字段进行关联。这样,在删除那个字段或其所在表的时候会自动删除已关联的序列。

如果序列已经和表有关联后,使用这个选项后新的关联关系会覆盖旧的关联。

关联的表和序列的所有者必须是同一个用户,并且在同一个模式中。

使用OWNED BY NONE将删除任何已经存在的关联。

new_owner

序列新所有者的用户名。用户要修改序列的所有者,必须是新角色的直接或者间接成员,并且那个角色必须有序列所在模式上的CREATE权限。

示例

将序列serial的最大值修改为200:

ALTER SEQUENCE serial MAXVALUE 200;

创建一个表,定义默认值:

CREATE TABLE T1(C1 bigint default nextval('serial'));

将序列serial的归属列变为T1.C1:

ALTER SEQUENCE serial OWNED BY T1.C1;

5. 删除序列

使用DROP SEQUENCE命令删除一个序列。 例如,将删除名为seq1的序列:

DROP SEQUENCE seq1;

5.1 DROP SEQUENCE语句的使用方法

DROP SEQUENCE用于从当前数据库里删除序列。

注意事项

只有序列的所有者或者系统管理员才能删除。

语法格式

DROP SEQUENCE [ IF EXISTS ] {[schema.]sequence_name} [ , ... ] [ CASCADE | RESTRICT ];

参数说明

  • IF EXISTS

如果指定的序列不存在,则发出一个notice而不是抛出一个错误。

  • name

序列名称。

  • CASCADE

级联删除依赖序列的对象。

  • RESTRICT

如果存在任何依赖的对象,则拒绝删除序列。此项是缺省值。

6. SEQUENCE相关函数

序列函数为用户从序列对象中获取后续的序列值提供了简单的多用户安全的方法。DWS目前支持以下SEQUENCE函数:

6.1 nextval(regclass)

nextval(regclass)用于递增序列并返回新值。
返回类型:bigint
nextval函数有两种调用方式(其中第二种调用方式兼容Oracle的语法,目前不支持Sequence命名中有特殊字符"."的情况),调用方式如下:

示例1:

postgres=# SELECT nextval('seqDemo');
nextval
---------
2
(1 row)

示例2:

postgres=# SELECT seqDemo.nextval;
nextval
---------
2
(1 row)

注意事项

为了避免从同一个序列获取值的并发事务被阻塞, nextval操作不会回滚;也就是说,一旦一个值已经被抓取, 那么就认为它已经被用过了,并且不会再被返回。 即使该操作处于事务中,当事务之后中断,或者如果调用查询结束不使用该值,也是如此。这种情况将在指定值的顺序中留下未使用的"空洞"。 因此,GaussDB(DWS)序列对象不能用于获得"无间隙"序列。

如果nextval被下推到DN上时,各个DN会自动连接GTM,请求next values值,例如(insert into t1 select xxx,t1某一列需要调用nextval函数),由于GTM上有最大连接数为8192的限制,而这类下推语句会导致消耗过多的GTM连接数,因此对于这类语句的并发数目限制为7000(其它语句需要占用部分连接)/集群DN数目。

6.2 currval(regclass)

currval(regclass)用于返回当前会话里最近一次nextval返回的指定的sequence的数值。如果当前会话还没有调用过指定的sequence的nextval,那么调用currval将会报错。需要注意的是,这个函数在默认情况下是不支持的,需要通过设置enable_beta_features为true之后,才能使用这个函数。同时在设置enable_beta_features为true之后,nextval()函数将不支持下推。
返回类型:bigint
currval函数有两种调用方式(其中第二种调用方式兼容Oracle的语法,目前不支持Sequence命名中有特殊字符"."的情况),调用方式如下:

示例1:

postgres=# SELECT currval('seq1');
currval
---------
2
(1 row)

示例2:

postgres=# SELECT seq1.currval seq1;
currval
---------
2
(1 row)

6.3 lastval()

lastval()用于返回当前会话里最近一次nextval返回的数值。这个函数等效于currval,只是它不用序列名为参数,它抓取当前会话里面最近一次nextval使用的序列。如果当前会话还没有调用过nextval,那么调用lastval将会报错。

需要注意的是,lastval()函数在默认情况下是不支持的,需要通过设置enable_beta_features或者lastval_supported为true之后,才能使用这个函数。同时这种情况下,nextval()函数将不支持下推。

返回类型:bigint

示例:

postgres=# SELECT lastval();
lastval
---------
2
(1 row)

6.4 setval(regclass, bigint)

setval(regclass, bigint)用于设置序列的当前数值。

返回类型:bigint

示例:

postgres=# SELECT setval('seqDemo',1);
setval
--------
1
(1 row)

6.5 setval(regclass, bigint, boolean)

setval(regclass, bigint, boolean)用于设置序列的当前数值以及is_called标志。

返回类型:bigint

示例:

postgres=# SELECT setval('seqDemo',1,true);
setval
--------
1
(1 row)

注意事项

Setval后当前会话及GTM上会立刻生效,但如果其他会话有缓存的序列值,只能等到缓存值用尽才能感知Setval的作用。所以为了避免序列值冲突,setval要谨慎使用。因为序列是非事务的,setval造成的改变不会由于事务的回滚而撤销。

7. 注意事项

新序列值的产生是靠GTM维护的,默认情况下,每申请一个序列值都要向GTM发送一次申请,GTM在当前值的基础上加上步长值作为产生的新值返回给调用者。GTM作为全局唯一的节点,势必成为性能的瓶颈,所以对于需要大量频繁产生序列号的操作,如使用Bulkload工具进行数据导入场景,是非常不推荐产生默认序列值的。比如,在下面所示的场景中, INSERT FROM SELECT语句的性能会非常慢。

CREATE SEQUENCE newSeq1;
CREATE TABLE newT1
(
id int not null default nextval('newSeq1'),
name text
);
INSERT INTO newT1(name) SELECT name from T1;

可以提高性能的写法是(假设T1表导入newT1表中的数据为10000行):

INSERT INTO newT1(id, name) SELECT id,name from T1;
SELECT SETVAL('newSeq1',10000);

序列操作函数nextval(),setval() 等均不支持回滚。另外setval设置的新值,会对当前会话的nextval立即生效,但对其他会话,如果定义了cache,不会立即生效,在用尽所有缓存的值后,其变动才被其他会话感知。所以为了避免产生重复值,要谨慎使用setval,设置的新值不能是已经产生的值或者在缓存中的值。

如果必须要在bulkload场景下产生默认序列值,则一定要为newSeq1定义足够大的cache,并且不要定义Maxvalue或者Minvalue。数据库会试图将nextval(‘sequence_name’)的调用下推到Data Node,以提高性能。 目前GTM对并发的连接请求是有限制的,当Data Node很多时,将产生大量并发连接, 这时一定要控制bulkload的并发数目,避免耗尽GTM的连接资源。如果目标表为复制表(DISTRIBUTE BY REPLICATION)时下推将不能进行。当数据量较大时,这对数据库将是个灾难。除了性能问题之外,空间也可能会剧烈膨胀,在导入结束后,需要用vacuum full来恢复。最好的方式还是如上建议的,不要在bulkload的场景中产生默认序列值。

另外,序列创建后,在每个节点上都维护了一张单行表,存储序列的定义及当前值,但此当前值并非GTM上的当前值,只是保存本节点与GTM交互后的状态。如果其他节点也向GTM申请了新值,或者调用了Setval修改了序列的状态,不会刷新本节点的单行表,但因每次申请序列值是向GTM申请,所以对序列正确性没有影响。

8. 使用案例

DWS如何重置自增列的开始序号?

使用函数setval(regclass, bigint)对自增列值进行重置。

示例:

将seqDemo列的开始序号重置为1:

postgres=# SELECT setval('seqDemo',1);
setval
--------
1
(1 row)

DWS如何确定sequence和哪个表有关联?

先在pg_class查找目标sequence的oid,然后在pg_depend根据oid查依赖该sequence的对象

示例:

先创建自增序列seq1和依赖seq1的表T2:

postgres=# CREATE SEQUENCE seq1 cache 100;

postgres=# CREATE TABLE T2
postgres-# (
postgres(# id int not null default nextval('seq1'),
postgres(# name text
postgres(# );
根据seq1从表pg_class、pg_depend联合查询到依赖表T2的oid:
postgres=# select * from pg_depend where objid = (select oid from pg_class where relname = 'seq1')
classid | objid | objsubid | refclassid | refobjid | refobjsubid | deptype
---------+------------+----------+------------+----------+-------------+---------
1259 | 2147485853 | 0 | 2615 | 2200 | 0 | n
(1 row)

如何查询序列的last_value?

由于SEQUENCE在自增过程中并不是严格逐个增加,因此序列号中会存在空端数据,所以last_value本身并没有实际意义,可以采用函数lastval()进行查询。

示例:

postgres=# SELECT lastval();
lastval
---------
2
(1 row)

注意事项

如果当前会话还没有调用过nextval,那么调用lastval将会报错。此外,lastval()函数在默认情况下是不支持的,需要通过设置enable_beta_features或者lastval_supported为true之后,才能使用这个函数。同时这种情况下,nextval()函数将不支持下推。

如何查询SEQUENC的当前最新值?

通过currval函数可以查询SEQUENC的当前最新值。

示例:

currval函数有两种调用方式(其中第二种调用方式兼容Oracle的语法,目前不支持Sequence命名中有特殊字符"."的情况),调用方式如下:
示例1:
postgres=# SELECT currval('seq1');
currval
---------
2
(1 row)
示例2:
postgres=# SELECT seq1.currval seq1;
currval
---------
2
(1 row)

如何解决SEQUENC取值超出范围的问题?

1.可以在创建SEQUENC时设置CYCLE字段,从而使得序列达到maxvalue或者minvalue后可循环并继续下去。但需要注意,若定义序列为CYCLE,则不能保证序列的唯一性。

2.通过调用setval(regclass, bigint)函数对序列取值进行重置。

9. 总结

本文介绍了SEQUENCE的使用场景和相关的函数的使用方法,并对使用SEQUENCE过程中遇到的常见问题及解决方法进行了汇总。

点击关注,第一时间了解华为云新鲜技术~

详解数仓对象设计中序列SEQUENCE原理与应用的更多相关文章

  1. Oracle数据库中序列(SEQUENCE)的用法详解

    Oracle数据库中序列(SEQUENCE)的用法详解   在Oracle数据库中,序列的用途是生成表的主键值,可以在插入语句中引用,也可以通过查询检查当前值,或使序列增至下一个值.本文我们主要介绍了 ...

  2. ORACLE PL/SQL 中序列(sequence)的简易使用方法介绍

    如果我是C罗 原文 ORACLE PL/SQL 中序列(sequence)的简易使用方法介绍 sequence在ORACLE中应用十分广泛,就是序列号的意思,会自动增加指定变数,如逐次增加1或者2或者 ...

  3. 详解数仓中sequence的应用场景及优化

    摘要:本文简单介绍sequence的使用场景及如何修改sequence的cache值提高性能. 本文分享自华为云社区<GaussDB(DWS)关于sequence的那些事>,作者:Arro ...

  4. 一文详解数仓GaussDB(DWS) 函数出参带出方式

    摘要:本文主要讲解DWS函数出参带出方式. 本文分享自华为云社区<GaussDB(DWS)功能 -- 函数出参 #[玩转PB级数仓GaussDB(DWS)]>,作者:譡里个檔 . DWS的 ...

  5. 序列sequence中的cache问题

    Oracle中序列Sequence的创建语法如下: CREATE SEQUENCE [ schema. ] sequence [ { INCREMENT BY | START WITH } integ ...

  6. Oracle序列(Sequence)创建、使用、修改、删除

    Oracle对象课程:序列(Sequence)创建.使用.修改.删除,序列(Sequence)是用来生成连续的整数数据的对象.序列常常用来作为主键中增长列,序列中的可以升序生成,也可以降序生成.创建序 ...

  7. Oracle中序列(Sequence)详解

    一 序列定义 序列(SEQUENCE)是序列号生成器,可以为表中的行自动生成序列号,产生一组等间隔的数值(类型为数字).不占用磁盘空间,占用内存. 其主要用途是生成表的主键值,可以在插入语句中引用,也 ...

  8. python中的sequence(序列)

    摘要 这篇文章主要是为了让自己记住字典不是序列,python中序列的类型 序列化的定义 有个朋友问我,什么是序列化,我瞬间懵了,然后查了一下,发现廖雪峰老师给出了一个很舒服的解释: 序列化:我们把变量 ...

  9. 抓到 Netty 一个隐藏很深的内存泄露 Bug | 详解 Recycler 对象池的精妙设计与实现

    欢迎关注公众号:bin的技术小屋,如果大家在看文章的时候发现图片加载不了,可以到公众号查看原文 本系列Netty源码解析文章基于 4.1.56.Final版本 最近在 Review Netty 代码的 ...

  10. oracle数据库--序列(sequence)

    一个问题: 在某张表中,存在一个id列(整数),我们希望在添加记录的时候,该列从1开始,自动的增长,怎么处理? 解决方式:oracle是利用"序列"(sequence)来完成的. ...

随机推荐

  1. mysql分组后获取每个组排序后的第一条数据(整行)

    有一个学生分数表student,数据结构是这样的 CREATE TABLE `student` ( `id` int(11) NOT NULL, `student_id` int(11) DEFAUL ...

  2. Navicat安装包和激活工具

    链接:https://pan.baidu.com/s/11YuOKdgGakt3E8CL80aA2g 提取码:h4f3

  3. Flutter学习

    常用网址 免费下载 !<AliFlutter 体系化建设和实践> Flutter 开发文档 Flutter实战 Dart 编程语言概览 pub仓库 main函数使用了(=>)符号, ...

  4. Bind DNS Server的基础配置

    1.访问https://192.168.3.254:10000 由于SSL证书是不安全的,我用的Firefox浏览器会阻止打开网页: 看到上述界面,先选择"高级", 然后再选择&q ...

  5. Html飞机大战(十): 消灭敌机

    好家伙,本篇是带着遗憾写完的. 很遗憾,我找了很久,找到了bug但并没有成功修复bug 再上一篇中我们看到 子弹射中了敌机,但是敌机并没有消失,所以这篇我们要来完善这个功能 按照惯例我们来捋一下思路: ...

  6. 【Azure 环境】 介绍两种常规的方法来监视Window系统的CPU高时的进程信息: Performance Monitor 和 Powershell Get-Counter

    问题描述 部署在Azure上的VM资源,偶尔CPU飙高,但是发现的时候已经恢复,无法判断当时High CPU原因. 在Windows系统中,有什么方式能记录CPU被进程占用情况,查找出当时是排名前列的 ...

  7. 【Azure 应用服务】Azure App Service能否使用Storage Account File Share

    问题描述 Azure App Service能否使用Storage Account File Share? 问题回答 如果部署的App Service为Linux环境,可以直接使用Mount stor ...

  8. Spring Cloud跟Dubbo区别?

    Spring Cloud是一个微服务框架,提供了微服务中很多功能组件,Dubbo一开始时RPC调用框架,核心是解决服务调用间的问题, Spring Cloud是一个大而全的框架,Dubbo更侧重于服务 ...

  9. Redis单线程为什么如此之快

    一.概述 Redis的高并发和快简单可以归结为一下几点: 1.Redis是基于内存的: 2.Redis是单线程的: 3.Redis使用多路复用技术. 4.高效的数据结构 但具体怎么做的呢,下面来详细看 ...

  10. 详解Python魔法函数,__init__,__str__,__del__

    1.简介 Python作为一门灵活而强大的编程语言,提供了许多特殊的方法,被称为魔法函数(Magic methods).这些魔法函数以双下划线开头和结尾,能够让我们自定义类的行为,使得Python更加 ...