由于序列也是类似整型类型,我们先看一下PG中的整型数值类型的有效范围:

smallint - int2 - A 2-byte integer, -32768 to 32767
integer - int4 -A 4-byte integer, -2147483648 to 2147483647
bigint - int8 -An 8-byte integer, -9223372036854775808 to +9223372036854775807

PG中有序列类型,使用该类型,会自动创建一个序列,序列类型如下:

smallserial  2 bytes small autoincrementing integer  1 to 32767
serial  4 bytes autoincrementing integer    1 to 2147483647
bigserial   8 bytes large autoincrementing integer  1 to 9223372036854775807

一般创建序列的时候,大部分人可能会使用int4类型的,或者是serial类型,该类型上限是2147483647,对于一些使用环境,可能很就会达到该上限值。报错如下:

ERROR:  nextval: reached maximum value of sequence "xxxxx" (2147483647)

我们可以在日常监控中监控序列的剩余值,然后根据阈值抛出告警,可以使用以下查询:

SELECTseqs.relname AS sequence,format_type(s.seqtypid, NULL) sequence_datatype,CONCAT(tbls.relname, '.', attrs.attname) AS owned_by,format_type(attrs.atttypid, atttypmod) AS column_datatype,pg_sequence_last_value(seqs.oid::regclass) AS last_sequence_value,TO_CHAR((CASE WHEN format_type(s.seqtypid, NULL) = 'smallint' THEN(pg_sequence_last_value(seqs.relname::regclass) / 32767::float)WHEN format_type(s.seqtypid, NULL) = 'integer' THEN(pg_sequence_last_value(seqs.relname::regclass) / 2147483647::float)WHEN format_type(s.seqtypid, NULL) = 'bigint' THEN(pg_sequence_last_value(seqs.relname::regclass) / 9223372036854775807::float)END) * 100, 'fm9999999999999999999990D00%') AS sequence_percent,TO_CHAR((CASE WHEN format_type(attrs.atttypid, NULL) = 'smallint' THEN(pg_sequence_last_value(seqs.relname::regclass) / 32767::float)WHEN format_type(attrs.atttypid, NULL) = 'integer' THEN(pg_sequence_last_value(seqs.relname::regclass) / 2147483647::float)WHEN format_type(attrs.atttypid, NULL) = 'bigint' THEN(pg_sequence_last_value(seqs.relname::regclass) / 9223372036854775807::float)END) * 100, 'fm9999999999999999999990D00%') AS column_percent
FROMpg_depend dJOIN pg_class AS seqs ON seqs.relkind = 'S'AND seqs.oid = d.objidJOIN pg_class AS tbls ON tbls.relkind = 'r'AND tbls.oid = d.refobjidJOIN pg_attribute AS attrs ON attrs.attrelid = d.refobjidAND attrs.attnum = d.refobjsubidJOIN pg_sequence s ON s.seqrelid = seqs.oid
WHEREd.deptype = 'a'AND d.classid = 1259;

如下一个例子:

create table test(id serial primary key, value integer);
select setval('test_id_seq', 2000000000);
#通过上面的查询可以看到使用了93.13%sequence   | sequence_datatype | owned_by | column_datatype | last_sequence_value | sequence_percent | column_percent
-------------+-------------------+----------+-----------------+---------------------+------------------+----------------test_id_seq | integer           | test.id  | integer         |          2000000000 | 93.13%           | 93.13%

虽然正常序列的取值是正数,但是也可以使用负数填充:

postgres=# alter sequence test_id_seq no minvalue start with -1 increment -1 restart;
ALTER SEQUENCE
postgres=# select nextval('test_id_seq');nextval
----------1
(1 row)postgres=# select nextval('test_id_seq');nextval
----------2
(1 row)

如果使用序列的列没有特别含义,正数值使用即将耗尽的时候,业务上可以使用负数表示,而且序列值消耗没有特别大,那么可以考虑使用负数,因为这样调整不需要改变列的属性,只是调整的序列起始值。但是如果序列值消耗很快,那么也只有2倍的数值可以使用,这只是一个缓兵之计。

单独创建序列使用和直接使用序列类型的区别:

单独创建序列使用,删除表后,序列不会自动清理
使用序列类型smallserial,serial,bigserial,删除表的时候会一起删除对应序列

postgres=# create table t1(a serial);
CREATE TABLE
postgres=# \d t1 Table "public.t1"Column |  Type   | Collation | Nullable |            Default
--------+---------+-----------+----------+--------------------------------a     | integer |           | not null | nextval('t1_id_seq'::regclass)postgres=# drop table t1;
DROP TABLE
postgres=# \dsList of relationsSchema |    Name     |   Type   |  Owner
--------+-------------+----------+----------postgres=# create sequence seq_test;
CREATE SEQUENCE
postgres=# create table t1(a int default nextval('seq_test'));
CREATE TABLE
postgres=# drop table t1;
DROP TABLE
postgres=# \dsList of relationsSchema |    Name     |   Type   |  Owner
--------+-------------+----------+----------public | seq_test       | sequence | postgres

如果要手动创建序列,那么使用owned by是一个不错的方法

#如下是用owned by绑定到列,删除表的时候,也会随表删除
postgres=# create sequence seq_test;
CREATE SEQUENCE
postgres=# create table t1(id int default nextval('seq_test'));
CREATE TABLE
postgres=# alter sequence seq_test owned by t1.id;
ALTER SEQUENCE
postgres=# \d  t1Table "public.t1"Column |  Type   | Collation | Nullable |            Default
--------+---------+-----------+----------+-------------------------------id     | integer |           |          | nextval('seq_test'::regclass)postgres=# \dsList of relationsSchema |    Name     |   Type   |  Owner
--------+-------------+----------+----------public | seq01       | sequence | postgrespublic | seq_test    | sequence | postgrespublic | test_id_seq | sequence | postgres
(3 rows)postgres=# drop table t1;
DROP TABLE
postgres=# \dsList of relationsSchema |    Name     |   Type   |  Owner
--------+-------------+----------+----------public | seq01       | sequence | postgrespublic | test_id_seq | sequence | postgres
(2 rows)

如果为了解决耗尽的问题,序列所在列需要更改为bigint的时候,一定要注意,因为int更改为bigint,需要重写表。下面的方法不需要重写表,但是需要回填数据,可以参考一下:

#比如下面表需要更改id的类型为bigint,并且id为主键
postgres=# \d  testTable "public.test"Column |  Type   | Collation | Nullable |             Default
--------+---------+-----------+----------+----------------------------------id     | integer |           | not null | nextval('test_id_seq'::regclass)value  | integer |           |          |
Indexes:"test_pkey" PRIMARY KEY, btree (id)#添加一列id_new,类型为bigint,并在该列建立一个唯一索引
postgres=# alter table test add column id_new bigint;
ALTER TABLE
postgres=# CREATE UNIQUE INDEX CONCURRENTLY test_id_new ON test (id_new);
CREATE INDEX
#创建一个序列,并从表记录最大值开始,这里假设为2147483776,设置该序列为新列的默认填充,别绑定到该列
CREATE SEQUENCE test_id_new_seq START 2147483776 AS bigint;
ALTER TABLE test ALTER COLUMN id_new SET DEFAULT nextval ('test_id_new_seq');
alter sequence test_id_new_seq owned by test.id_new;#现在可以看到新旧两列都会有自动填充
postgres=# select * from test;id     | value |   id_new
------------+-------+------------2000000007 |       |2000000008 |       |2000000009 |       |2000000010 |       |2000000011 |       | 21474837762000000012 |       | 21474837772000000013 |       | 21474837782000000014 |       | 2147483779#接下来是最关键的一步,把所有操作放在一个事物
BEGIN;
ALTER TABLE test DROP CONSTRAINT test_pkey;  --删除旧列约束
ALTER TABLE test ALTER COLUMN id DROP DEFAULT;  --删除旧列默认值
ALTER TABLE test RENAME COLUMN id TO id_old; --需改旧列名字
ALTER TABLE test RENAME COLUMN id_new TO id; --修改新列名字为要使用的列
ALTER TABLE test ALTER COLUMN id_old DROP NOT NULL; --删除旧列的非空约束
ALTER TABLE test ADD CONSTRAINT id_not_null CHECK (id IS NOT NULL) NOT VALID;  --新列添加非空约束,但是暂时不生效
COMMIT;#上面新的列因为设置为not valid,所以null值是可以存在的,现在我们需要回填数据到新加的列
WITH unset_values AS (SELECTid_oldFROMtestWHEREid IS NULLLIMIT 5000)
UPDATEtest
SETid = unset_values.id_old
FROMunset_values
WHEREunset_values.id_old = test.id_old;#一旦回填数据完成,也就是上面语句更新的行数为0,我们就可以验证约束,然后转为主键,删除独立的not null约束
ALTER TABLE test VALIDATE CONSTRAINT id_not_null;
ALTER TABLE test ADD CONSTRAINT test_pkey PRIMARY KEY USING INDEX test_id_new;
ALTER TABLE test DROP CONSTRAINT id_not_null;
#最后删除旧列
ALTER table test drop column id_old;
#已经转换为bigint
postgres=# \d  testTable "public.test"Column |  Type   | Collation | Nullable |               Default
--------+---------+-----------+----------+--------------------------------------value  | integer |           |          | id     | bigint  |           | not null | nextval('test_id_new_seq'::regclass)
Indexes:"test_pkey" PRIMARY KEY, btree (id)

最后再在PostgreSQL10中,引入了identity column,用于解决序列的很多问题,所以现在推荐使用该方式。具体不再详述。
可以参考Postgresql鲜为人知的一些功能(一)

我们最后总结一下序列的正确使用姿势:

  1. 优先使用identity column方式,因为避免了很多坑
  2. 使用序列类型,最好直接使用bigserial,避免出现序列值耗尽的情况
  3. 如果是自己创建的序列,然后设置列的默认值,则使用owned by方式绑定到列。
  4. 如果序列使用的是int4类型的,消耗比较慢,在业务可接受的时候,可以选择负数,这样就有2倍的值可用。
  5. int修改为bigint一定要注意,不要直接改,因为会重写表,可以尝试通过本篇文章的方法进行列替换,回填旧数据的方式。

参考:
https://www.crunchydata.com/blog/the-integer-at-the-end-of-the-universe-integer-overflow-in-postgres
https://mp.weixin.qq.com/s/5kQRShiNBC5ViL6ttMFnFg
https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-INT
https://www.postgresql.org/docs/current/sql-createsequence.html

Postgresql中序列正确使用建议相关推荐

  1. Postgresql中如何正确删除role

    关于删除role的一些理论 首先,删除用户不能使用DROP ROLE - CASCADE,不能级联删除用户.也就是不能删除依赖的对象. 因为角色可以拥有数据库对象,并且可以拥有访问其他数据库对象的权限 ...

  2. 改善C#程序的建议3:在C#中选择正确的集合进行编码

    原文:改善C#程序的建议3:在C#中选择正确的集合进行编码 要选择正确的集合,我们首先要了解一些数据结构的知识.所谓数据结构,就是相互之间存在一种或多种特定关系的数据元素的集合.结合下图,我们看一下对 ...

  3. 作为Java开发工程师如何正确地对待和处理工作中的失误学习方法建议教训成长技巧[王大师]

    本人详解 作者:王文峰,参加过 CSDN 2020年度博客之星,<Java王大师王天师>作者 公众号:山峯草堂,非技术多篇文章,专注于天道酬勤的 Java 开发问题.中国国学.传统文化和代 ...

  4. 插入,在PostgreSQL中重复更新吗?

    本文翻译自:Insert, on duplicate update in PostgreSQL? Several months ago I learned from an answer on Stac ...

  5. postgresql学习_在PostgreSQL中学习这些快速技巧

    postgresql学习 PostgreSQL is one of the most popular open source SQL dialects. One of its main advanta ...

  6. Python中序列的累积计算

    [小白从小学Python.C.Java] [Python-计算机等级考试二级] [Python-数据分析] Python中序列的累积计算 cumsum()函数 选择题 以下python代码输出什么? ...

  7. PostgreSQL中的数据库实例、模式、用户(角色)、表空间

    2019独角兽企业重金招聘Python工程师标准>>> 本文参考:http://blog.csdn.net/kanon_lgt/article/details/5931522 htt ...

  8. Oracle中序列(Sequence)详解

    Oracle中序列(Sequence)详解 一 序列定义 序列(SEQUENCE)是序列号生成器,可以为表中的行自动生成序列号,产生一组等间隔的数值(类型为数字).不占用磁盘空间,占用内存. 其主要用 ...

  9. 下列有关python语言的说法正确的是-关于 Python 语言的注释,以下选项中描述正确的是( )...

    [多选题]以下选项属于 Python 整数类型的是( ) [其它]根据CAD原文件绘制别墅立面图:如图 别墅立面图002.dwg [单选题]字符串是一个连续的字符序列,用________方式打印出可以 ...

最新文章

  1. 《编写可维护的JavaScript》——1.7 直接量
  2. Apache CXF 3.0:CDI 1.1支持可替代Spring
  3. java8--IO(java疯狂讲义3复习笔记)
  4. JS trim的实现
  5. js中match的用法
  6. matlab画图,想让子图使用不同的色标
  7. Excel 常用快捷键总结(Alt系列)
  8. 引用传递(Java)
  9. 让memcached和mysql更好的工作
  10. 手机上有没有学python的软件-三款可以在安卓手机上运行Python代码的软件
  11. qq浏览器网页翻译_在线英文翻译、文档翻译,这几款翻译工具你值得拥有
  12. 树莓派linux负载均衡集群,在树莓派2上Nginx并发1W到底有多难
  13. [转][信息图表]Google十大高薪职位
  14. ESP32-WROOM-32E,WIFI基本功能实现,采坑经验
  15. 没有百万调音师,用大数据+AI 也能让用户的声音更动听
  16. 设计原则(安卓图片类)
  17. C++ 变量的生存期
  18. MySQL表查询关键字
  19. 商务智能-第六章 数据挖掘
  20. 注册ActiveX控件简单方法及控件未被正确授权解决方案

热门文章

  1. 哪个厂家的监控平台用的云服务器_监控云平台
  2. 【STM32F429开发板用户手册】第41章 STM32F429的LTDC应用之LCD汉字显示和2D图形显示
  3. (超详细)读取mnist数据集并保存成图片
  4. 书写博客: Markdown 语法讲解
  5. 校验原理及奇偶校验码
  6. 如何恢复微信账单里删除的记录
  7. 11.21的自动锁屏 ios_iOS11再现bug 闹铃定时不响 自动锁屏失效
  8. 安装系统之六 U盘装GHOST WIN7教程
  9. 使用QQ第三方登录时,手机应用和网站应用对同一个QQ号,获取到的openid不一样
  10. 多进程+多线程爬取链家武汉二手房价