详解数仓对象设计中序列SEQUENCE原理与应用
本文分享自华为云社区《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原理与应用的更多相关文章
- Oracle数据库中序列(SEQUENCE)的用法详解
Oracle数据库中序列(SEQUENCE)的用法详解 在Oracle数据库中,序列的用途是生成表的主键值,可以在插入语句中引用,也可以通过查询检查当前值,或使序列增至下一个值.本文我们主要介绍了 ...
- ORACLE PL/SQL 中序列(sequence)的简易使用方法介绍
如果我是C罗 原文 ORACLE PL/SQL 中序列(sequence)的简易使用方法介绍 sequence在ORACLE中应用十分广泛,就是序列号的意思,会自动增加指定变数,如逐次增加1或者2或者 ...
- 详解数仓中sequence的应用场景及优化
摘要:本文简单介绍sequence的使用场景及如何修改sequence的cache值提高性能. 本文分享自华为云社区<GaussDB(DWS)关于sequence的那些事>,作者:Arro ...
- 一文详解数仓GaussDB(DWS) 函数出参带出方式
摘要:本文主要讲解DWS函数出参带出方式. 本文分享自华为云社区<GaussDB(DWS)功能 -- 函数出参 #[玩转PB级数仓GaussDB(DWS)]>,作者:譡里个檔 . DWS的 ...
- 序列sequence中的cache问题
Oracle中序列Sequence的创建语法如下: CREATE SEQUENCE [ schema. ] sequence [ { INCREMENT BY | START WITH } integ ...
- Oracle序列(Sequence)创建、使用、修改、删除
Oracle对象课程:序列(Sequence)创建.使用.修改.删除,序列(Sequence)是用来生成连续的整数数据的对象.序列常常用来作为主键中增长列,序列中的可以升序生成,也可以降序生成.创建序 ...
- Oracle中序列(Sequence)详解
一 序列定义 序列(SEQUENCE)是序列号生成器,可以为表中的行自动生成序列号,产生一组等间隔的数值(类型为数字).不占用磁盘空间,占用内存. 其主要用途是生成表的主键值,可以在插入语句中引用,也 ...
- python中的sequence(序列)
摘要 这篇文章主要是为了让自己记住字典不是序列,python中序列的类型 序列化的定义 有个朋友问我,什么是序列化,我瞬间懵了,然后查了一下,发现廖雪峰老师给出了一个很舒服的解释: 序列化:我们把变量 ...
- 抓到 Netty 一个隐藏很深的内存泄露 Bug | 详解 Recycler 对象池的精妙设计与实现
欢迎关注公众号:bin的技术小屋,如果大家在看文章的时候发现图片加载不了,可以到公众号查看原文 本系列Netty源码解析文章基于 4.1.56.Final版本 最近在 Review Netty 代码的 ...
- oracle数据库--序列(sequence)
一个问题: 在某张表中,存在一个id列(整数),我们希望在添加记录的时候,该列从1开始,自动的增长,怎么处理? 解决方式:oracle是利用"序列"(sequence)来完成的. ...
随机推荐
- C++ 多线程的错误和如何避免(3)
传递给 C++ 线程的构造函数的参数是通过值传递的 VS 平台:2019 问题:如何在线程中改变传递的参数值? 比如: #include <functional> #include < ...
- django学习第六天---shell指令,单表基于双下划线的模糊查询,distinct注意点,字段的choices属性,url反向解析,orm多表操作创建表
shell指令 命令 python manage.py shell 在Terminal,执行上面这个指令会进入到python解释器环境中,并且加载了我们当前django项目配置环境,所以可以在当前sh ...
- tox包
官方文档 https://tox.readthedocs.io/en/latest/example/basic.html 官方提供的一个简单的tox.ini/默认环境 [tox] envlist = ...
- KVM整理
管理命令: virsh list --all 查看所有虚拟机状态 virsh start vm1 VM1开机 virsh shutdown vm1 VM1关机 virsh destroy vm1 强制 ...
- 基于图数据库 NebulaGraph 实现的欺诈检测方案及代码示例
本文是一个基于 NebulaGraph 图算法.图数据库.机器学习.GNN 的 Fraud Detection 方法综述.在阅读本文了解欺诈检测的基本实现方法之余,也可以在我给大家准备的 Playgr ...
- Nebula Graph 的 KV 存储分离原理和性能测评
本文首发于 Nebula Graph Community 公众号 1. 概述 过去十年,图计算无论在学术界还是工业界热度持续升高.相伴而来的是,全世界的数据正以几何级数形式增长.在这种情况下,对于数据 ...
- C++ //常用算术生成算法 //#include<numeric> accumulate //fill //向容器中填充指定的元素
1 //常用算术生成算法 //#include<numeric> accumulate 2 //fill //向容器中填充指定的元素 3 #include<iostream> ...
- vue和xml复习
复习 JS知识梳理 JS定义的位置 行内js(事件名="javascript:js代码"),内部js(
- Prometheus技术分享——prometheus的函数与计算公式详解
Prometheus与zabbix相比,它的强大之处就在于可以它可以使用的很多计算公式去获取自己需要的数据.当然,这里所涉及到的计算公式,也是我们普遍认为的难点所在.比如,我们要获取CPU使用率,使用 ...
- vue 动态加载css,改变网站皮肤模式
Vue.mixin({ created () { require('view-design/dist/styles/iview.css') } }) 参考资料:https://blog.csdn.ne ...