这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

序列

序列可用于将列的默认值设置为连续整数值。序列保证了唯一性,并避免了约束强制执行问题和开销。序列对于主键列特别有用。

虽然序列对象值保证唯一,但不能保证它们连续,因此您可能会将返回的值解释为缺失。例如,两个节点可以用不同的速率递增序列。具有较大处理负载的节点将递增序列,但在具有较小处理负载的节点上递增的值不连续。

Vertica 支持以下序列类型:

  • 命名序列是以升序或降序顺序生成唯一编号的数据库对象。命名序列是通过 CREATE SEQUENCE 语句独立定义的,并且独立于引用它们的表进行管理。一个表可以将一个或多个列的默认值设置为命名序列。

  • AUTO_INCREMENT/IDENTITY 列序列:列约束 AUTO_INCREMENTIDENTITY 是同义词,用于指定在添加新行时递增或递减列的值。此序列类型与表相关,不会独立保留。一个表只能包含一个 AUTO_INCREMENTIDENTITY 列。

1 - 比较序列类型

下表列出了两种序列类型之间的差异:

2 - 命名序列

命名序列是由 CREATE SEQUENCE 定义的序列。虽然可以将表列的值设置为命名序列,但与 AUTO_INCREMENT 和 IDENTITY 序列不同,命名序列独立于表而存在。

当应用程序要求表或表达式中使用唯一标识符时,最常使用命名序列。在命名序列返回一个值后,它永远不会在同一会话中再次返回相同的值。

2.1 - 创建和使用命名序列

使用 CREATE SEQUENCE 创建命名序列。该语句只需提供序列名称即可;所有其他参数均为可选参数。要创建序列,用户必须对包含该序列的架构具有 CREATE 权限。

以下示例将创建一个起始值为 100 的升序命名序列 my_seq

=> CREATE SEQUENCE my_seq START 100;
CREATE SEQUENCE

递增和递减序列

在创建命名序列对象时,还可以通过设置其 INCREMENT 参数指定其递增量或递减量值。如果省略此参数(如上一示例中所示),则默认值设置为 1。

可以通过对序列调用函数 NEXTVAL 来递增或递减序列(直接在序列本身上递增或递减,或通过向引用该序列的表中添加新行来间接地递增或递减)。对新序列第一次调用时,NEXTVAL 将该序列初始化为其起始值。Vertica 还会为序列创建缓存。随后对序列调用 NEXTVAL 会递增其值。

下面对 NEXTVAL 的调用会将新 my_seq 序列初始化为 100:

=> SELECT NEXTVAL('my_seq');
 nextval
---------
     100
(1 row)

获取序列的当前值

可以通过对序列调用 CURRVAL 来获取该序列的当前值。例如:

=> SELECT CURRVAL('my_seq');
 CURRVAL
---------
     100
(1 row)

在表中引用序列

表可以将任何列的默认值设置为命名序列。表创建者必须具有以下权限:SELECT(对于序列)、USAGE(对于其架构)。

在以下示例中,列 id 从命名序列 my_seq 获取其默认值:

=> CREATE TABLE customer(id INTEGER DEFAULT my_seq.NEXTVAL,
  lname VARCHAR(25),
  fname VARCHAR(25),
  membership_card INTEGER
);

对于插入到表 customer 中的每一行,该序列调用 NEXTVAL 函数来设置 id 列的值。例如:

=> INSERT INTO customer VALUES (default, 'Carr', 'Mary', 87432);
=> INSERT INTO customer VALUES (default, 'Diem', 'Nga', 87433);
=> COMMIT;

对于每一行,插入操作都会对序列 my_seq 调用 NEXTVAL,这会将该序列递增至 101 和 102,并将 id 列设置为这些值:

=> SELECT * FROM customer;
 id  | lname | fname | membership_card
-----+-------+-------+-----------------
 101 | Carr  | Mary  |           87432
 102 | Diem  | Nga   |           87433
(1 row)

2.2 - 分发命名序列

当创建命名序列时,其 CACHE 参数决定了每个节点在会话期间所保留的序列值的数量。默认缓存值为 250K,因此每个节点在每个会话期间为每个序列保留 250,000 个值。此默认缓存大小为大规模插入或复制操作提供了一种有效的方式。

如果将序列缓存设置为较低的数字,则节点可能会更频繁地请求一组新的缓存值。提供新缓存时,Vertica 必须锁定编录。在 Vertica 释放锁之前,其他数据库活动(例如表插入)会被阻止,这将对整体性能产生不利影响。

当一个新会话启动时,节点缓存最初为空。默认情况下,启动程序节点会为群集中的所有节点请求和保留缓存。可以通过将配置参数 ClusterSequenceCacheMode 设置为 0 来更改此默认值,以便每个节点都请求其自己的缓存。

有关 Vertica 如何请求缓存并在群集中的所有节点之间分发缓存的信息,请参阅序列缓存

分布式会话的影响

Vertica 在所有节点之间分发会话。群集节点第一次对序列调用 NEXTVAL 函数以递增(或递减)其值时,该节点会请求其自己的序列值缓存。然后,该节点会为当前会话维护该缓存。当其他节点调用 NEXTVAL 时,它们也会创建和维护其自己的序列值缓存。

在会话期间,节点会以不同的频率独立调用 NEXTVAL。每个节点都使用其自己的缓存来填充序列。所有序列值都保证唯一,但是可能与另一个节点上执行的 NEXTVAL 语句出现乱序。因此,序列值通常不连续。

在所有情况下,每行仅递增序列一次。因此,如果多个列引用了同一序列,则 NEXTVAL 会将该行中的所有列设置为相同的值。这适用于联接表的行。

计算命名序列

Vertica 按如下方式计算序列的当前值:

  • 在每个语句结束时,会话中使用的所有序列的状态都会返回到启动程序节点。

  • 启动程序节点计算每个序列在所有节点上的所有状态下的最大 CURRVAL

  • 此最大值将用作后续语句中的 CURRVAL,直到调用另一个 NEXTVAL。

丢失序列值

在下列情况下,缓存中的序列值可能会丢失:

  • 如果在调用 NEXTVAL 后某条语句失败了(因此耗用了缓存中的一个序列值),则值就会丢失。

  • 如果连接断开了(如删除了会话),则缓存中尚未通过 NEXTVAL 返回的所有剩余值都会丢失。

  • 一个或多个节点尚未用完其当前缓存分配时,启动程序节点便为每个节点分发新的缓存块。有关此场景的信息,请参阅序列缓存

可以使用 ALTER SEQUENCE...RESTART 恢复丢失的序列值,这会在下一个会话中将序列重置为指定的值。

2.3 - 更改序列

ALTER SEQUENCE 可以通过两种方式更改命名序列

  • 重置控制序列行为的参数,例如其起始值或最小值和最大值的范围。这些更改仅在您启动新数据库会话时生效。

  • 重置序列名称、架构或所有权。这些更改会立即生效。

更改序列行为

ALTER SEQUENCE 可以通过以下参数更改一个或多个序列属性:

这些更改仅在您启动新数据库会话时生效。例如,如果创建一个从 10 开始并按 1(默认值)递增的命名序列 my_sequence,则序列每次调用 NEXTVAL 都会按 1 递增其值:

=> CREATE SEQUENCE my_sequence START 10;
=> SELECT NEXTVAL('my_sequence');
 nextval
---------
      10
(1 row)
=> SELECT NEXTVAL('my_sequence');
 nextval
---------
      11
(1 row)

以下 ALTER SEQUENCE 语句指定重新启动序列时从 50 开始:

=>ALTER SEQUENCE my_sequence RESTART WITH 50;

但是,此更改在当前会话中不起作用。对 NEXTVAL 的下一次调用会将序列递增至 12:

=> SELECT NEXTVAL('my_sequence');
 NEXTVAL
---------
      12
(1 row)

只有在启动新的数据库会话后,该序列才会在 50 处重新启动:

=> \q
$ vsql
Welcome to vsql, the Vertica Analytic Database interactive terminal.

=> SELECT NEXTVAL('my_sequence');
 NEXTVAL
---------
      50
(1 row)

更改序列名称、架构和所有权

可以使用 ALTER SEQUENCE 对命名序列进行以下更改:

  • 重命名它。

  • 将其移动到另一个架构。

  • 重新分配所有权。

这些更改中的每个更改都需要使用单独的 ALTER SEQUENCE 语句。这些更改会立即生效。

例如,以下语句将序列从 my_seq 重命名为 serial

=> ALTER SEQUENCE s1.my_seq RENAME TO s1.serial;

以下语句将序列 s1.serial 移动到架构 s2 中:

=> ALTER SEQUENCE s1.my_seq SET SCHEMA TO s2;

以下语句将 s2.serial 的所有权重新分配给另一个用户:

=> ALTER SEQUENCE s2.serial OWNER TO bertie;

2.4 - 删除序列

使用 DROP SEQUENCE 移除命名序列。例如:

=> DROP SEQUENCE my_sequence;

如果满足以下条件之一,则不能删除序列:

  • 其他对象依赖该序列。 DROP SEQUENCE 不支持级联操作。

  • 列的 DEFAULT 表达式引用该序列。在删除该序列之前,必须移除对它的所有列引用。

3 - AUTO_INCREMENT 和 IDENTITY 序列

AUTO_INCREMENT 和 IDENTITY 约束是将列与序列相关联的同义词。当添加新行时,此序列会自动递增列值。

在表中定义 AUTO_INCREMENT 或 IDENTITY 列,如下所示:

CREATE TABLE table-name...
  (column-name
    {AUTO_INCREMENT | IDENTITY}
      ( [ cache-size | start, increment [, cache-size ] ] )

AUTO_INCREMENT/IDENTITY 序列由定义它们的表拥有,并且不存在于该表之外。与命名序列不同,您不能使用 ALTER SEQUENCE 管理 AUTO_INCREMENT/IDENTITY 序列。例如,您不能独立于其表更改 AUTO_INCREMENT/IDENTITY 序列的架构。如果将表移动到另一个架构,序列会自动随之移动。

可以通过调用 LAST_INSERT_ID 函数获取为 AUTO_INCREMENT/IDENTITY 序列生成的最后一个值。

参数

限制

以下限制适用于 AUTO_INCREMENT/IDENTITY 列:

  • 一个表只能包含一个 AUTO_INCREMENT/IDENTITY 列。

  • 即使未提交尝试向表中插入值的事务,AUTO_INCREMENT/IDENTITY 值也绝对不会回退。

  • 不能更改 AUTO_INCREMENT/IDENTITY 列的值。

具有 AUTO_INCREMENT/IDENTITY 列的表还可以包含一个或多个设置为命名序列的列。

示例

以下示例显示了如何使用 IDENTITY 列约束创建一个包含 ID 列的表。ID 列的初始值为 1。每次插入一行时,该列都将以 1 为增量递增。

  1. 创建名为 Premium_Customer 的表:

    => CREATE TABLE Premium_Customer(
         ID IDENTITY(1,1),
         lname VARCHAR(25),
         fname VARCHAR(25),
         store_membership_card INTEGER
    );
    => INSERT INTO Premium_Customer (lname, fname, store_membership_card )
         VALUES ('Gupta', 'Saleem', 475987);
    

    IDENTITY 列的种子值为 1,它指定加载到表中的第一行的值,增量为 1,它指定添加到上一行的 IDENTITY 值的值。

  2. 确认添加的行并查看 ID 值:

    => SELECT * FROM Premium_Customer;
     ID | lname | fname  | store_membership_card
    ----+-------+--------+-----------------------
      1 | Gupta | Saleem |                475987
    (1 row)
    
  3. 增加一个行:

    => INSERT INTO Premium_Customer (lname, fname, store_membership_card)
       VALUES ('Lee', 'Chen', 598742);
    
  4. 调用 Vertica 函数 LAST_INSERT_ID。该函数将返回值 2,因为您之前插入了一个新客户 (Chen Lee),并且每次插入一行时,此值都会递增:

    => SELECT LAST_INSERT_ID();
     last_insert_id
    ----------------
                   2
    (1 row)
    
  5. 查看 Premium_Customer 表中的所有 ID 值:

    => SELECT * FROM Premium_Customer;
     ID | lname | fname  | store_membership_card
    ----+-------+--------+-----------------------
      1 | Gupta | Saleem |                475987
      2 | Lee   | Chen   |                598742
    (2 rows)
    

接下来的三个示例说明使用 IDENTITY 实参的三种有效方法。这些示例对 AUTO_INCREMENT 实参也有效。

第一个示例使用缓存 100,以及起始值 (1) 和增量值 (1) 的默认值:

=> CREATE TABLE t1(x IDENTITY(100), y INT);

第二个示例将起始值和增量值指定为 1,并将默认缓存值指定为 250,000:

=> CREATE TABLE t2(y IDENTITY(1,1), x INT);

第三个示例将起始值和增量值指定为 1,并将缓存值指定为 100:

=> CREATE TABLE t3(z IDENTITY(1,1,100), zx INT);

4 - 序列缓存

缓存对于所有序列类型都是类似的:命名序列、标识序列和自动递增序列。为了在群集中的节点之间为给定序列分配缓存,Vertica 使用以下过程。

  1. 默认情况下,当会话开始时,群集启动程序节点会为其自身和群集中的其他节点请求缓存。

  2. 启动程序节点会在分发执行计划时将缓存一并分发给其他节点。

  3. 由于启动程序节点会为所有节点请求缓存,因此只有启动程序会在请求缓存时锁定全局编录。

这种方法最适合处理大型 INSERT-SELECT 和 COPY 操作。下图显示了启动程序如何在三节点群集中为命名序列请求和分发缓存,其中将该序列的缓存设置为 250 K:

各节点在不同的时间用尽缓存。当执行相同的查询时,节点会根据需要单独请求其他缓存。

对于同一个会话中的新查询,如果启动程序使用其所有缓存执行上一个查询执行,则该启动程序可能具有空缓存。在这种情况下,启动程序会为所有节点请求缓存。

可以通过将配置参数 ClusterSequenceCacheMode 设置为 0(禁用)来更改节点获得序列缓存的方式。将此参数设置为 0 时,群集中的所有节点都会请求各自的缓存和编录锁。但是,一开始执行大型 INSERT-SELECT 和 COPY 操作时,如果所有节点的缓存为空,每个节点会同时请求缓存。这些多个请求会导致全局编录上同时出现多个锁定,从而对性能产生不利影响。因此,ClusterSequenceCacheMode 应保持设置为其默认值 1(启用)。

以下示例比较了 ClusterSequenceCacheMode 的不同设置如何影响 Vertica 管理序列缓存的方式。该示例假定一个三节点群集,每个节点 250 K 缓存(默认值),序列 ID 值递增量为 1。