本文分享自华为云社区《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. Feign入门介绍

    Feign入门介绍 基本概述 除Feign之外,在Java中经常使用的HTTP客户端组件主要有3个,如下: (1)HttpURLConnection,JDK自带 (2)Apache HttpClien ...

  2. itext 生成 PDF

    itext 生成 PDF(一) 转自:https://blog.csdn.net/lcczpp/article/details/125424395   itext生成PDF excel 示例  转自: ...

  3. 网络安全-Linux常用命令

    安装上传下载的软件包 yum install lrzsz -y 下载:linux-->windows sz -y /etc/hosts 上传:windows-->linux rz -y 系 ...

  4. sql注入简单初

    import requests,sys,time from PyQt5.QtWidgets import * from PyQt5.QtGui import QIcon from threading ...

  5. 【生成对抗网络学习 其二】GAN(keras实现)代码阅读笔记

    想来想去还是记录一下吧,主要是怕以后时间长忘了 好记性不如烂笔头 代码来自eriklindernoren的开源GAN实现:https://github.com/eriklindernoren/Kera ...

  6. DataGear 自定义数据可视化看板的图表主题

    DataGear 看板的 dg-chart-theme 属性,提供了简单且强大的自定义图表主题功能. 通常,只需要设置其color.backgroundColor.actualBackgroundCo ...

  7. 【Azure Developer】Azure REST API: 如何通过 API查看 Recovery Services Vaults(恢复保管库)的备份策略信息? 如备份中是否含有虚拟机的Disk

    问题描述 如何通过 API查看 Recovery Services Vaults(恢复保管库)的备份策略信息? 如备份中是否含有虚拟机的Disk.在Azure门户中可以通过查看Backup Item查 ...

  8. jenkins 钉钉机器人插件

    官方文档: https://jenkinsci.github.io/dingtalk-plugin/guide/getting-started.html#%E6%B3%A8%E6%84%8F 注意:系 ...

  9. Shopee虾皮api接口 搜索商品、评价信息 python数据采集

    iDataRiver平台 https://www.idatariver.com/zh-cn/ 提供开箱即用的Shopee电商数据采集API,供用户按需调用. 接口使用详情请参考Shopee接口文档 接 ...

  10. 你想要一个简单的 MQ 吗?(最简单的那种)

    FolkMQ 一个简单的消息中间件(全球最简单的那种,要比谁都简单!).追世间简单为何物,可叫我生死相许! 面向简单编程 1) 启动服务 docker run -p 18602:18602 -p 86 ...