SEQUENCE函数
在数据库应用开发中,唯一且递增的序号生成是一项基础而常见的需求。例如,在电商系统中为每笔订单生成唯一的订单号、在用户注册时分配自增主键ID、在日志系统中为每条记录生成唯一流水号等。这些场景都需要在多用户并发环境下安全地获取不重复的序列值。
DWS提供了SEQUENCE函数(包括nextval、currval、lastval、setval),为用户从序列对象中获取后续的序列值提供了简单的多用户安全的方法。相比应用层自行生成序号,SEQUENCE函数通过全局事务管理器(GTM)统一协调序列值分配,能够在分布式集群环境中保证序列值的唯一性和连续性,避免并发冲突。
基本概念
- regclass:DWS中的数据类型,表示系统表pg_class中某行的OID(对象标识符)。在序列函数中,regclass参数用于指定序列对象的名称或OID,系统会自动将序列名称(如'seqDemo')解析为对应的OID。
- is_called标志:序列对象的内部状态标志,控制下一次nextval的返回行为。当is_called为true时,表示当前序列值已被“调用过”,下一次nextval将返回当前值+增量;当is_called为false时,表示当前序列值还未被“调用过”,下一次nextval将返回当前值本身。
- 下推(Pushdown):DWS查询优化机制,指将SQL语句从CN下发给DN直接执行,减少CN与DN之间的数据传输。当nextval被下推到DN时,各DN会各自连接GTM请求序列值,可能消耗较多GTM连接数。
前置操作:创建序列
在使用SEQUENCE函数前,需要先创建序列对象。执行以下SQL语句创建示例序列:
1 2 3 4 | -- 创建示例序列seqDemo,起始值为1,步长为1 CREATE SEQUENCE seqDemo START 1; -- 创建示例序列seq1,起始值为1,步长为1 CREATE SEQUENCE seq1 START 1; |
CREATE SEQUENCE的完整语法和参数说明请参见CREATE SEQUENCE。创建序列后,即可使用以下SEQUENCE函数操作该序列对象。
nextval(regclass)
递增序列并返回新值。
- 为了避免从同一个序列获取值的并发事务被阻塞, nextval操作不会回滚;也就是说,一旦获取了一个值,它就会被认为已被使用,并且不会再次返回。 即使该操作处于事务中,当事务随后中断,或者调用查询最终没有使用该值,也会出现这种情况。这种情况将在指定值的顺序中留下未使用的“空洞”。 因此,DWS序列对象不能用于获取“无间隙”序列。
- 如果nextval被下推到DN上时,各个DN会自动连接GTM,请求next values值,例如(INSERT INTO t1 SELECT xxx,t1某一列需要调用nextval函数),由于GTM上有最大连接数为8192的限制,而这类下推语句会导致消耗过多的GTM连接数,因此对于这类语句的并发数目限制为7000(其他语句需要占用部分连接)/集群DN数目。
返回类型:bigint
nextval函数有两种调用方式(其中第二种调用方式兼容Oracle的语法,目前不支持Sequence命名中有特殊字符"."的情况),如下:
示例1:
1 2 3 4 5 | SELECT nextval('seqDemo'); nextval --------- 2 (1 row) |
示例2:
1 2 3 4 5 | SELECT seqDemo.nextval; nextval --------- 2 (1 row) |
currval(regclass)
返回当前会话里最近一次nextval返回的指定的sequence的数值。如果当前会话还没有调用过指定的sequence的nextval,那么调用currval将会报错。需要注意的是,这个函数在默认情况下是不支持的,需要通过设置enable_beta_features为true之后,才能使用这个函数。同时在设置enable_beta_features为true之后,nextval()函数将不支持下推。
返回类型:bigint
currval函数有两种调用方式(其中第二种调用方式兼容Oracle的语法,目前不支持Sequence命名中有特殊字符"."的情况),如下:
示例1:
1 2 3 4 5 | SELECT currval('seq1'); currval --------- 2 (1 row) |
示例2:
1 2 3 4 5 | SELECT seq1.currval seq1; currval --------- 2 (1 row) |
lastval()
描述:返回当前会话里最近一次nextval返回的数值。这个函数等效于currval,只是它不用序列名为参数,它抓取当前会话里面最近一次nextval使用的序列。如果当前会话还没有调用过nextval,那么调用lastval将会报错。
需要注意的是,这个函数在默认情况下是不支持的,需要通过设置enable_beta_features或者lastval_supported为true之后,才能使用这个函数。同时这种情况下,nextval()函数将不支持下推。
返回类型:bigint
示例:
1 2 3 4 5 | SELECT lastval(); lastval --------- 2 (1 row) |
setval(regclass, bigint)
描述:设置序列的当前数值。
返回类型:bigint
示例:
1 2 3 4 5 | SELECT setval('seqDemo',1); setval -------- 1 (1 row) |
setval(regclass, bigint, boolean)
描述:设置序列的当前数值以及is_called标志。
返回类型:bigint
示例:
1 2 3 4 5 | SELECT setval('seqDemo',1,true); setval -------- 1 (1 row) |
Setval后当前会话及GTM上会立刻生效,但如果其他会话有缓存的序列值,只能等到缓存值用尽才能感知Setval的作用。所以为了避免序列值冲突,setval要谨慎使用。
因为序列是非事务的,setval造成的改变不会由于事务的回滚而撤销。