绑定完请刷新页面
取消
刷新

分享好友

×
取消 复制
Greenplum主键和序列的使用
2022-06-08 14:42:42

主键和键限制

主键约束是约束和非空约束的组合。GP数据库为每个unique或primary key约束自动创建unique索引来强制执行性。因此,无需为主键列显式创建索引。

Appendonly追加表不支持主键和键约束

在追加优化表上不允许使用uniqueprimary key约束,因为由约束创建的unique索引在追加优化表上不允许 

随机分布的表不支持主键和键约束

GP表如果需要建键,表必须是哈希分布的(不是随机分布)。即随机分布不支持主键(约束+非空约束)和键,因为随机分布保证不了整体数据的性。 

有约束未指定分布键的表会将键作为分布键

如果表中含有键,但是没有指定分布键,则GP会默认将键作为表的分布键 

同一个表不支持同时存在两个约束的字段

GP数据库不支持同时存在两个的字段(复合主键除外)。 

分布键必须是主键列约束的左子集并且列的顺序是正确的

例如,如果主键是(a,b,c),该分布键只能是以下集合之一:(a),(a,b),或(a,b,c) 

不能直接修改主键类型

直接修改主键类型报错如下:ERROR: relation "XXXXX" already exists

正确的修改方式 先drop主键类型,修改类型后,再进行主键的重新创建

  1. postgres=# ALTER TABLE public.t_hash_3 drop CONSTRAINT t_hash_3_pkey;

  2. ALTER TABLE

  3. postgres=# alter table public.t_hash_3 alter column id TYPE bigint;

  4. ALTER TABLE

  5. postgres=# ALTER TABLE public.t_hash_3 ADD CONSTRAINT t_hash_3_pkey primary key(id);

  6. NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index "t_hash_3_pkey" for table "t_hash_3"

  7. ALTER TABLE

如果报约束名不存在时,可以去数据字典(pg_class和pg_constraint)中查找实际的约束名

  1. postgres=# select oid from pg_class where relname='t_hash_3';

  2. oid

  3. -------

  4. 73870

  5. (1 row)


  6. postgres=# select conname from pg_constraint where conrelid=73870;

  7. conname

  8. ---------------

  9. t_hash_3_pkey

  10. (1 row)

serial类型

如果业务需要实现自增,我们会推荐serial类型。但是某业务反馈该类型并不能达到自增的效果,因此好好研究了下该类型的用法。

GP沿用postgresql序列号(SERIAL)类型,postgresql中包括smallserial(smallint,short),serial(int)和bigserial(bigint,long long int)三种,本文测试使用的gp5.x版本只支持serial。

序列号类型其实不是真正的类型,当声明一个字段为序列号类型时其实是通过建立一个全局序列 表名_字段名_seq 实现的,INSERT时如果没有给该字段赋值会默认获取对应序列的下一个值。

添加自增字段的两种方式

通过serial自动创建序列

  1. postgres=# create table tuniq(id serial, name text);

  2. NOTICE: CREATE TABLE will create implicit sequence "tuniq_id_seq" for serial column "tuniq.id"

  3. NOTICE: Using default RANDOM distribution since no distribution was specified.

  4. HINT: Consider including the 'DISTRIBUTED BY' clause to determine the distribution of rows.

  5. CREATE TABLE

  6. postgres=# \d tuniq

  7. Table "public.tuniq"

  8. Column | Type | Modifiers

  9. --------+---------+----------------------------------------------------

  10. id | integer | not null default nextval('tuniq_id_seq'::regclass)

  11. name | text |

  12. Distributed randomly

通过sequence自定义序列

除了上述通过使用serial类型来自动创建序列,我们也可以先创建一个序列,然后将表的某字段默认值设为该序列的下一个序列值。

通过\h查看创建序列的语法

  1. postgres=# \h create sequence

  2. Command: CREATE SEQUENCE

  3. Description: define a new sequence generator

  4. Syntax:

  5. CREATE [ TEMPORARY | TEMP ] SEQUENCE name [ INCREMENT [ BY ] increment ]

  6. [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]

  7. [ START [ WITH ] start ] [ CACHE cache ] [ [ NO ] CYCLE ]

  8. [ OWNED BY { table.column | NONE } ]

含义如下

关键字含义
INCREMENT BY每次序列增加(或减少)的步长
MINVALUE序列小值,NO MINVALUE表示没有小值
MAXVALUE序列大值,NO MAXVALUE表示没有大值
START WITH以什么序列值开始
CYCLE序列是否循环使用
OWNED BY可以直接指定一个表的字段,也可以不指定
  1. postgres=# create sequence sql_tbl_serial2_a;

  2. CREATE SEQUENCE

  3. postgres=# create table tbl_serial2(a int not null default nextval('sql_tbl_serial2_a'),b varchar(2));

  4. NOTICE: Using default RANDOM distribution since no distribution was specified.

  5. HINT: Consider including the 'DISTRIBUTED BY' clause to determine the distribution of rows.

  6. CREATE TABLE

  7. postgres=# \d+ tbl_serial2

  8. Table "public.tbl_serial2"

  9. Column | Type | Modifiers | Storage | Description

  10. --------+----------------------+---------------------------------------------------------+----------+-------------

  11. a | integer | not null default nextval('sql_tbl_serial2_a'::regclass) | plain |

  12. b | character varying(2) | | extended |

  13. Has OIDs: no

  14. Distributed randomly

  15. --或者在已经好的表添加默认值约束

  16. postgres=> ALTER TABLE tbl_serial2 ALTER id set default nextval('sql_tbl_serial2_a'::regclass);

  17. ALTER TABLE

查询自增字段定义信息

tuniq_id_seq为通过serial自动创建的序列。可通过\d 序列名,可直接查看到序列的定义

  1. postgres=# \d tuniq_id_seq

  2. Sequence "public.tuniq_id_seq"

  3. Column | Type | Value

  4. ---------------+---------+---------------------

  5. sequence_name | name | tuniq_id_seq

  6. last_value | bigint | 1

  7. increment_by | bigint | 1

  8. max_value | bigint | 9223372036854775807

  9. min_value | bigint | 1

  10. cache_value | bigint | 1

  11. log_cnt | bigint |

  12. is_cycled | boolean | f

  13. is_called | boolean | f

serial范围

通过\d查看到序列的大值为9223372036854775807,然而我们测试,发现由于id字段本身是int类型,所以当超出int范围会报错

  1. postgres=# insert into tuniq values(9223372036854775807, 'zero');

  2. ERROR: integer out of range

  3. postgres=# insert into tuniq values(9223372036854775806, 'zero');

  4. ERROR: integer out of range

查找资料,验证serial类型的范围如下:

类型字节数含义范围
serial4字节自增整数1到+2147483647
bigserial8字节大范围的自增整数1到9223372036854775807

如何增大自增字段的大值

由上可知,通过serial类型,大值只能到2147483647。如果该范围已经不够用。可通过先建序列,再与声明为bigint的字段建立约束即可。如下

  1. postgres=# create sequence sql_tbl_serial2_a increment by 1 minvalue 1 maxvalue 9223372036854775807 start with 1;

  2. CREATE SEQUENCE

  3. postgres=# create table tbl_serial2(a bigint not null default nextval('sql_tbl_serial2_a'),b varchar(2));

  4. NOTICE: Using default RANDOM distribution since no distribution was specified.

  5. HINT: Consider including the 'DISTRIBUTED BY' clause to determine the distribution of rows.

  6. CREATE TABLE

  7. postgres=# \d+ tbl_serial2

  8. Table "public.tbl_serial2"

  9. Column | Type | Modifiers | Storage | Description

  10. --------+----------------------+---------------------------------------------------------+----------+-------------

  11. a | bigint | not null default nextval('sql_tbl_serial2_a'::regclass) | plain |

  12. b | character varying(2) | | extended |

  13. Has OIDs: no

  14. Distributed randomly

  15. postgres=# insert into tbl_serial2 values(9223372036854775807, 'ze');

  16. INSERT 1

自增字段往往伴随约束

如果不建立约束,会出现如下问题。

  1. postgres=> create table tuniq(id serial, name text);

  2. CREATE TABLE

  3. postgres=> insert into tuniq (name) values('zero');

  4. INSERT 1

  5. postgres=> insert into tuniq (name) values('second');

  6. INSERT 1

  7. postgres=>

  8. postgres=>

  9. postgres=> select * from tuniq;

  10. id | name

  11. ----+--------

  12. 1 | zero

  13. 2 | second

  14. (2 rows)

  15. --继续在自增字段插入一样的值,不会报错。

  16. postgres=> insert into tuniq (id, name) values(1, 'second');

  17. --出现自增字段重复,与业务预期不符

  18. postgres=> select * from tuniq;

  19. id | name

  20. ----+--------

  21. 1 | zero

  22. 2 | second

  23. 1 | second

  24. (3 rows)

该问题可通过在自增字段建立键约束解决。

自增字段为什么没有生效

如果有时候自增字段由oltp系统同步过来。有时候又需要在GP内部执行不包括自增字段的数据插入情况。

这个时候,因为sequence的当前新值尚未更新,所以可能会出现与已导入数据冲突的情况,如:

  1. postgres=> create table tuniq(id serial unique, name text);

  2. CREATE TABLE

  3. postgres=> insert into tuniq values(, 'zero');

  4. INSERT 1

  5. postgres=> insert into tuniq values(1, 'first');

  6. INSERT 1

  7. postgres=> select * from tuniq;

  8. id | name

  9. ----+-------

  10. | zero

  11. 1 | first

  12. (2 rows)

  13. postgres=> insert into tuniq (name) values('second');

  14. ERROR: duplicate key value violates unique constraint "tuniq_id_key"

  15. DETAIL: Key (id)=(1) already exists.

此时我们需要先将序列值进行更新,才可正常运行。如下:

  1. postgres=> select setval('tuniq_id_seq', max(id)) from tuniq;

  2. setval

  3. --------

  4. 1

  5. (1 row)

  6. postgres=> insert into tuniq (name) values('second');

  7. INSERT 1

  8. postgres=> select * from tuniq;

  9. id | name

  10. ----+--------

  11. | zero

  12. 1 | first

  13. 2 | second

  14. (3 rows)

序列的函数使用

函数返回类型描述
currval(regclass)bigint获取指定序列近一次使用netxval后的数值,如果没有使用nextval而直接使用currval会出错。
lastval()bigint返回近一次用 nextval 获取的任意序列的数值
nextval(regclass)bigint递增序列并返回新值
setval(regclass,bigint)bigint设置序列的当前数值
setval(regclass,bigint ,boolean)bigint设置序列的当前数值以及 is_called 标志,如果为true则立即生效,如果为false,则调用一次nextval后才会生效。

总结三点

  • serial是通过sequence设置字段的默认值

  • 可以考虑加上约束,防止主动插入该字段的值,破坏该字段值的自增序和性(如果业务关心的话)

  • 如果自增字段需要从外部同步,又需要在内部产生的场景,需要及时更新序列的新值。

参考

http://www.dbdream.com.cn/ https://gp-docs-cn.github.io/docs/refguide/sqlcommands/CREATE_TABLE.html https://yq.aliyun.com/articles/64314 https://blog.csdn.net/u012352957/article/details/84577468

分享好友

分享这个小栈给你的朋友们,一起进步吧。

Greenplum
创建时间:2022-04-08 15:36:19
Greenplum
展开
订阅须知

• 所有用户可根据关注领域订阅专区或所有专区

• 付费订阅:虚拟交易,一经交易不退款;若特殊情况,可3日内客服咨询

• 专区发布评论属默认订阅所评论专区(除付费小栈外)

技术专家

查看更多
  • itt0918
    专家
戳我,来吐槽~