Postgres Pro Standard 12.1发布

Postgres Pro Standard DBMS旨在比通过PostgreSQL更快地向用户交付产品。 那些尚未包括在PostgreSQL中但在其中可靠的功能,我们包括在Postgres Pro Standard中。 另外,Postgres Pro Standard包括一些我们客户需要的扩展,但在标准PostgreSQL发行版中不可用。

有时在Postgres Pro Standard中有例外,应用户的要求并为他们的满意,它包含了一些琐碎的功能,只有在Postgres Pro Enterprise中这才是好地方。 特别是下面的PTRACK。

不是全部,而是Postgres Professional开发了Standard中包含的其他扩展和实用程序中的相当一部分。 所有Postgres Pro补丁都是由我们自己的努力发明和实施的。 让我们从需要干预数据库引擎的改进开始。

Postgres Pro Standard在两个方面与PostgreSQL不同:程序集中的一组扩展和实用程序,以及内核本身。 一些有用的补丁已被应用到内核,以优化性能(例如,非制动锁检测器),并增加了实用程序和扩展的效率的补丁(例如,为了使pg_probackup充分发挥作用,应用了PTRACK 2.0补丁)。 Standard和PostgreSQL核心版本之间的差异被最小化,以实现最大的兼容性。 假设pg_pathman扩展是Standard的一部分,但可以从github下载,在PostgreSQL上构建和安装,不会有兼容性问题。
让我们从内核中的更改开始。

检查ICU版本


在PostgreSQL中,默认情况下,它们用于通过使用标准C库进行比较来比较字符串,但是也有可能出于相同目的使用IBM开发的ICU库。 该库对我们来说很有价值,主要是因为它提供了与平台无关的排序。 例如,这就是为什么在1C中使用它,而PostgreSQL“ for one-es”程序集已经使用此库很长时间了。

另外,通过ICU进行的字符串比较有时比通过libc进行的字符串比较快,并且它所知道的字符数也更多。 通常,有用的库。 从第一个版本(9.5)开始,Postgres Pro Standard就开始使用它。 在PostgreSQL中,从版本10开始就可以使用ICU。

该库很有用,但是您需要记住一些紧急情况。 假设DBMS用户已决定升级操作系统。 连同操作系统一起,还可以升级ICU库,并且排序中的单词顺序也会改变。 此后,所有索引将立即变得不可用:索引搜索将给出错误的结果。 在这种情况下,该基地表示ICU的版本已更改并停止。

但这是一个痛苦而艰难的决定。 经过讨论和对客户的调查后,决定软化行为。 现在仅检查COLLATION(排序规则)的版本。 如果数据库中使用的COLLATION版本已更改,则数据库在DBMS启动时会发出警告,但不会停止。 它还会在每个会话开始时提醒用户。

优化锁,联接和GROUP BY


死锁检测机制会降低性能。 Standard不再可用:内核补丁允许它不停机运行。 在对验证机制进行重大改进之后,这些问题仅出现在大量的内核和连接上。

在存在适当索引的情况下,改进对联接结果数量的估计。

现在,您可以使用合适的索引对字段进行分组和排序。 此功能最初包含在Standard 11.1.1和Enterprise 11.2.1中。 我们的标准12也有一个。

Postgres Professional的CTO Fedor Sigaev已向社区提供了这些有用的补丁程序,正在对其进行考虑,并希望将其包含在PG 13版本中。

我们用示例说明GROUP BY操作的优化:它们清晰易懂,并且易于复制。

该补丁的要点是Postgres并未优化GROUP BY中列出的字段的顺序。 执行时间取决于分组的顺序(具有相同的查询结果)。 有关黑客邮件列表的讨论中有详细信息。

如果要处理的第一列中的值是唯一的,则无需进行其他比较。 如果从另一列开始,则必须进行比较。


进行测试:

DROP TABLE IF EXISTS btg; SELECT i AS id, i/2 AS p, format('%60s', i%2) AS v INTO btg FROM generate_series(1, 1000000) i; 


在文本字段v中,将生成60个空格,后跟数字0或1。条目如下所示:

 SELECT * FROM btg ORDER BY id DESC LIMIT 3; id | p | v ---------+--------+-------------------------------------------------------------- 1000000 | 500000 | 0 999999 | 499999 | 1 999998 | 499999 | 0 (3 rows) 


 VACUUM btg; ANALYSE btg; SET enable_hashagg=off; SET max_parallel_workers= 0; SET max_parallel_workers_per_gather = 0; 


将结果分组:

 VACUUM btg; EXPLAIN ANALYZE SELECT count(*) FROM btg GROUP BY p, v; 


PostgreSQL计划:

  QUERY PLAN ------------------------------------------------------ GroupAggregate (cost=204036.84..218981.05 rows=494421 width=73) (actual time=843.999..1194.985 rows=1000000 loops=1) Group Key: p, v -> Sort (cost=204036.84..206536.84 rows=1000000 width=65) (actual time=843.990..946.769 rows=1000000 loops=1) Sort Key: p, v Sort Method: external sort Disk: 73320kB -> Seq Scan on btg (cost=0.00..22346.00 rows=1000000 width=65) (actual time=0.158..151.645 rows=1000000 loops=1) Planning time: 0.317 ms Execution time: 1250.086 ms (8 rows) 


现在以相反的顺序:v,然后只有p:

 EXPLAIN ANALYZE SELECT count(*) FROM btg GROUP BY v, p; QUERY PLAN ------------------------------------------------ GroupAggregate (cost=204036.84..218981.05 rows=494421 width=73) (actual time=2552.477..3353.890 rows=1000000 loops=1) Group Key: v, p -> Sort (cost=204036.84..206536.84 rows=1000000 width=65) (actual time=2552.469..3111.516 rows=1000000 loops=1) Sort Key: v, p Sort Method: external merge Disk: 76264kB -> Seq Scan on btg (cost=0.00..22346.00 rows=1000000 width=65) (actual time=0.082..126.578 rows=1000000 loops=1) Planning time: 0.060 ms Execution time: 3411.048 ms (8 rows) 


事实证明,相反的情况明显较慢。 这是因为第一字段v的值分布很小。 您必须对其余字段(此处-字段p)进行大量检查。

让我们看看同一查询如何与为处理列选择最佳顺序的补丁一起使用:

  QUERY PLAN ---------------------------------------------------------------- GroupAggregate (cost=237400.11..252417.09 rows=501698 width=73) (actual time=415.541..703.647 rows=1000000 loops=1) Group Key: p, v -> Sort (cost=237400.11..239900.11 rows=1000000 width=65) (actual time=415.533..507.785 rows=1000000 loops=1) Sort Key: p, v Sort Method: external merge Disk: 73488kB -> Seq Scan on btg (cost=0.00..22346.00 rows=1000000 width=65) (actual time=0.059..139.587 rows=1000000 loops=1) Planning Time: 0.123 ms Execution Time: 742.118 ms (8 rows) 


并以相反的顺序:

  QUERY PLAN ------------------------------------------------------ GroupAggregate (cost=237400.11..252417.09 rows=501698 width=73) (actual time=414.322..714.593 rows=1000000 loops=1) Group Key: p, v -> Sort (cost=237400.11..239900.11 rows=1000000 width=65) (actual time=414.312..517.707 rows=1000000 loops=1) Sort Key: p, v Sort Method: external merge Disk: 76384kB -> Seq Scan on btg (cost=0.00..22346.00 rows=1000000 width=65) (actual time=0.071..129.835 rows=1000000 loops=1) Planning Time: 0.140 ms Execution Time: 753.031 ms (8 rows) 


该计划说那里和那里的处理顺序是相同的:排序键:p,v。 因此,时间大致相同。 现在比较使用索引时发生的情况。

 CREATE INDEX ON btg(p, v); SET enable_seqscan=off; SET enable_bitmapscan=off; VACUUM btg; EXPLAIN ANALYZE SELECT count(*) FROM btg GROUP BY v, p ; 


在PostgreSQL中:

  QUERY PLAN --------------------------------------------------------- GroupAggregate (cost=0.55..74660.04 rows=494408 width=73) (actual time=0.013..391.317 rows=1000000 loops=1) Group Key: p, v -> Index Only Scan using btg_p_v_idx on btg (cost=0.55..62216.16 rows=999974 width=65) (actual time=0.009..120.298 rows=1000000 loops=1) Heap Fetches: 0 Planning time: 0.078 ms Execution time: 442.923 ms (6 rows) 


并以相反的顺序:

  QUERY PLAN ------------------------------------------------------ GroupAggregate (cost=243904.22..258848.04 rows=494408 width=73) (actual time=2558.485..3352.240 rows=1000000 loops=1) Group Key: v, p -> Sort (cost=243904.22..246404.16 rows=999974 width=65) (actual time=2558.478..3110.242 rows=1000000 loops=1) Sort Key: v, p Sort Method: external merge Disk: 76264kB -> Index Only Scan using btg_p_v_idx on btg (cost=0.55..62216.16 rows=999974 width=65) (actual time=0.011..133.563 rows=1000000 loops=1) Heap Fetches: 0 Planning time: 0.093 ms Execution time: 3409.335 ms (9 rows) 


现在处于标准状态:

  QUERY PLAN -------------------------------------------------------------- GroupAggregate (cost=0.55..74196.82 rows=501685 width=73) (actual time=0.150..412.174 rows=1000000 loops=1) Group Key: p, v -> Index Only Scan using btg_p_v_idx on btg (cost=0.55..61680.16 rows=999974 width=65) (actual time=0.134..149.669 rows=1000000 loops=1) Heap Fetches: 0 Planning Time: 0.175 ms Execution Time: 448.635 ms (6 rows) 


并以相反的顺序:

  QUERY PLAN ------------------------------------------------------------- GroupAggregate (cost=0.55..74196.82 rows=501685 width=73) (actual time=0.014..307.258 rows=1000000 loops=1) Group Key: p, v -> Index Only Scan using btg_p_v_idx on btg (cost=0.55..61680.16 rows=999974 width=65) (actual time=0.008..89.204 rows=1000000 loops=1) Heap Fetches: 0 Planning Time: 0.054 ms Execution Time: 337.766 ms (6 rows) 


时间又是相同的,这很自然:实际上,动作是相同的。

引导时替换空字节


Postgres Pro不接受数据中的零字节(0x00),因此必须使用COPY FROM替换它们, 否则将出现错误 。 这是客户从CSV文件导入数据时遇到的真正问题。 它的解决方案是用给定的ASCII字符替换空字节。 它必须与执行COPY FROM时使用的QUOTE和DELIMITER字符不同。 否则,结果可能是意外的。 默认情况下,变量nul_byte_replacement_on_import(字符串)“ \ 0”的值,即不执行任何替换。

等待LSN


LSN是日志中序列号 ,即,指向WAL中位置的指针(日志序列号)。 WAITLSN命令正在等待播放指定的LSN。 如果应用程序可同时使用主数据库和副本数据库,则需要确保它们不时同步。 WAITLSN是PostgrePro中的一种进程间机制,该机制在同步复制期间控制同步。 默认情况下,等待时间是无限的。 您可以通过按Ctrl + C或停止postgres服务器来中止等待。 您也可以通过添加TIMEOUT提示来设置超时,或者通过使用NOWAIT提示来检查目标LSN的状态而无需等待。
假设应用程序执行特定操作,从主服务器上的DBMS接收LSN号,现在要确保副本服务器上的操作将与主服务器同步,即 应用程序可以确保它在向导中记录的内容已经到达副本并可以读取。 默认情况下,通常无法保证。 WAITLSN允许您控制此交互,并从默认情况下从INFINITELY到TIMEOUT和NOWAIT选择睡眠模式。

从以前的recovery.conf重新读取变量


在SIGHUP信号上,PostgreSQL重新读取postgresql.conf,但不读取recovery.conf。 标准版和企业版10.4.1中引入了一个相对较新的内核补丁。 被迫重新读取和恢复。 但是在Postgres 12中根本没有recovery.conf文件:来自该文件的所有变量都被传输到postgresql.conf。 但是,尽管重新读取了整个文件,但是SIGHUP并未重新定义来自recovery.conf的变量,而是需要重新启动Postgres。 在Standard中,这不是必需的:读取并重新定义所有内容。

PTRACK支持


PTRACK 2.0是标准和企业版11及更早版本的重新设计的PTRACK机制。 在DBMS级别,由于内核补丁,它可以工作,现在ptrack扩展已添加到补丁中 。 PTRACK 2.0跟踪数据页更改,并提供检索此信息的界面。 它既可以用于诊断目的,例如,可以了解实例相对于指定时间点的“突变”强度,可以在日志(LSN)中设置为序列号,还可以创建增量备份。

通常,增量备份过程中最困难,最“昂贵”的部分是将已更改页面的子集与集群中整个页面集隔离开。 由于服务器可以执行此任务并快速提供有关已更改页面的信息,因此可以大大减少使用PTRACK进行增量备份的时间。

PTRACK 2.0使用共享内存中指定大小的哈希表,该哈希表与ptrack.map文件定期同步。

由于内部操作机制的根本改变以及与旧版本不兼容的用户界面,ptrack扩展仅在PostgresPro Standard和Enterprise的第12版中可用,并且将在PostgreSQL 12上作为补丁和扩展使用。

在Windows的psql中编辑命令


使用WinEditLine实现了Windows psql中编辑输入命令的高级支持。 现在,您可以同时显示不同字母的字符(尤其是西里尔字母通常显示在非俄语Windows上)。

统一包装结构



所有Linux发行版的二进制软件包的结构都是统一的,以便简化它们之间的迁移,并允许将几种不同的基于PostgreSQL的产品一起安装而不会发生任何冲突。 可以文档的第16章中找到。

现在有关扩展:

dump_stat


它最早出现在9.5。 传输或还原数据时,通常不会传输累积的统计信息。 如果使用ANALYZE命令重新组装它,那么它将对整个集群而不是指定的数据库执行。 对于大型数据库,这可能需要大量额外时间。

dump_stat扩展提供了允许您卸载和恢复pg_statistic表内容的功能。 在执行数据上载/恢复时,可以使用dump_stat将现有统计信息传输到新服务器,而不必为整个集群运行ANALYZE命令。

dump_statistic函数卸载pg_statistic系统目录的内容。 它为pg_statistic中的每个元组生成一个INSERT,但那些包含有关information_schema和pg_catalog模式中的表的统计信息的元组除外。

jsquery


回想一下, 这是使用JSON(B)而不是JS 的扩展 。 它提供了一组处理这些数据类型的功能。 这是一种特殊的查询语言,可有效地使用索引(JSON)搜索索引(B)。 在中心上的文章中,您可以看到jsquery的一些示例以及使用JSON(B)的替代方法,例如JSONPath(均为我们公司的开发)。

online_analyze


此扩展提供了一组函数,可立即更新表中的INSERT,UPDATE,DELETE或SELECT INTO操作后指定的统计信息。 扩展程序的作者是Fedor Sigaev。

要使用online_analyze模块,必须加载共享库:

 LOAD 'online_analyze'; 


可以自定义统计信息更新。 例如,设置表大小的百分比或行更改的最小(阈值)数量,然后将立即收集统计信息。

pg_pathman


Postgres Professional中的pg_pathman扩展创建于PostgreSQL内核之前,并且实现了相当完整的用于创建分区的功能集。 因此,可以使用一个和另一个机制来完成许多带有节的操作。 仅建议不要混合由声明性分区和pg_pathman创建的部分。

但是,许多pg_pathman操作仍然更快,并且PostgreSQL中缺少一些功能。 例如,自动创建(剪切)节。 在PostgreSQL中,必须定义每个部分的边界。 如果我们填写的数据事先未知,则可以分散多少部分,应该分散多少,那么简单地设置间隔并让软件自己剪切部分就很方便-所需的多。 pg_pathman知道,PostgreSQL不知道。 但是,从PG 11开始,有一个默认部分(默认),您可以在其中转储所有不属于指定边界的部分的所有记录。

与PostgreSQL社区的领导者达成了一项基本协议,即将来最好的pg_pathman的独特功能将落入main分支。 但是在那之前,pg_pathman可以使应用程序数据库管理员和应用程序程序员的生活更加轻松。

创建扩展:

 CREATE EXTENSION pg_pathman; 


pg_pathman允许您将大表分成多个部分,并提供一个方便的API-一组用于创建部分和使用它们的函数。 例如,使用功能

 create_range_partitions(relation REGCLASS, expression TEXT, start_value ANYELEMENT, p_interval INTERVAL, p_count INTEGER DEFAULT NULL, partition_data BOOLEAN DEFAULT TRUE); 

我们可以问

 SELECT create_range_partitions('log', 'dt', NULL::date, '1 month'::interval); 


之后,我们添加以下部分:

 SELECT add_range_partition('log', NULL, '2017-01-01'::date, 'log_archive', 'ts0'); SELECT add_range_partition('log', '2017-01-01'::date, '2017-02-01'::date, 'log_1'); SELECT add_range_partition('log', '2017-02-01'::date, '2017-03-01'::date', log_2'); 


存档日志将在ts0表空间中创建,其余默认情况下为。 但是您不能显式指定节,而是通过设置间隔和一步创建节来信任此DBMS操作:

 SELECT create_range_partitions('log', 'dt', '2017-01-01'::date, '1 month'::interval); 


在一个简单的表上,它将如下所示:

 CREATE TABLE pg_pathmania(id serial, val float); INSERT INTO pg_pathmania(val) SELECT random() * 1000 FROM generate_series(1, 1000); SELECT create_range_partitions('pg_pathmania', 'id', 0, 50); test_parti=# \d+ pg_pathmania Table "public.pg_pathmania" Column | Type | Collation | Nullable | Default | Storage | S tats target | Description --------+------------------+-----------+----------+-----------------------+---------+------+------ id | integer | | not null | nextval('pg_pathmania_id_seq'::regclass) | plain | | val | double precision | | | | plain | | Child tables: pg_pathmania_1, pg_pathmania_10, pg_pathmania_11, pg_pathmania_12, pg_pathmania_13, pg_pathmania_14, pg_pathmania_15, pg_pathmania_16, pg_pathmania_17, pg_pathmania_18, pg_pathmania_19, pg_pathmania_2, pg_pathmania_20, pg_pathmania_21, pg_pathmania_3, pg_pathmania_4, pg_pathmania_5, pg_pathmania_6, pg_pathmania_7, pg_pathmania_8, pg_pathmania_9 


在PostgreSQL中,我们必须与我们自己的团队一起创建每个部分。 当然,在这种情况下,他们编写了一个脚本,该脚本会自动生成所需的DDL代码。 您无需在pg_pathman中编写脚本,所有内容都已经存在。 但这不是最有趣的。 我们将插入一条记录,该记录不仅不会在现有的任何部分中获得ID的认可,而且不会落入最近的一条:

 INSERT INTO pg_pathmania(id, val) VALUES (2000, 277.835794724524); 


再次,用\ d + pg_pathmania检查表的内容:

 Child tables: pg_pathmania_1, pg_pathmania_10, ... pg_pathmania_39, pg_pathmania_4, pg_pathmania_40, pg_pathmania_41, 


这是发生的情况:pg_pathman看到id = 2000的记录不属于已经创建的部分,计算了需要创建多少记录,知道了表之前被分区的RANGE间隔,并创建了新记录所在的部分,当然,旧部分的上边界和新部分的下边界之间的所有部分。 这非常方便,并且在更新数据的拆分字段的值预测不佳的情况下,这是pg_pathman的重要优势。

pg_query_state


我们开发的此扩展程序使我们能够找出服务过程中请求当前状态。 它自9.5版起就存在,并且是由于客户管理员的众多请求而产生的。

事实是,EXPLAIN ANALYZE允许您查看从计划树的每个节点收集的执行统计信息,但是仅在查询完成后才收集这些统计信息。 但是,在生活中,有些情况下您需要查看请求尚未完成并且可能不会结束的情况。 pg_query_state允许您查看在外部服务过程中运行的查询的当前统计信息。 在这种情况下,结果输出的格式几乎与通常的EXPLAIN ANALYZE命令的输出相同。

实用程序:

pgBouncer


这是一个非常流行的连接器 ,在这里谈论它很奇怪。 只是它是Standard的一部分,对于Vanilla PostgreSQL,必须将其单独安装。

pg_probackup


pg_probackup是我们最受欢迎的开发之一。 这是一个备份和恢复管理器,主要由Anastasia Lubennikova,Grigory Smolkin和用户社区开发和更新。

pg_probackup的竞争优势:具有块粒度(8KB)的增量备份,三种增量备份模式(PAGE,DELTA,PTRACK),按需备份完整性检查,PostgreSQL集群验证,备份压缩,部分恢复等。

PTRACK增量复制模式依赖于重新设计的机制PTRACK 2.0 扩展了相同的名称 ,它已经变得更快,并且无疑是pg_probackup模式中最快,最“便宜”的模式。

pg_repack


pg_repack是一个流行的实用程序,其操作类似于VACUUM FULL或CLUSTER 。 它不仅可以重新打包表,消除空隙,而且还知道如何恢复聚集索引的物理顺序。 与CLUSTER和VACUUM FULL不同,它可以“随时随地”执行这些操作,而无需排他的表锁定,并且通常可以高效地工作。 它不包含在原始版本中。

pg_variables


关于此扩展我们的员工Ivan Frolkov 发表了一篇有趣的文章 。 扩展的原因是使用中间结果有时会带来不便且昂贵。 本文探讨了替代方法。 其中最常见的是临时表。

作为一个临时数据仓库,pg_variables扩展比临时表(本文中有pgbench测试)要高效得多,并且更方便:数据集由“包-变量”对定义,可以将其作为参数传递,从函数返回等。有用于处理变量的设置/获取功能。 因此,例如,您可以存储许多变量(package是包的名称,小数点后的表达式是该包中的变量:

 SELECT pgv_set_int('package','#'||n,n), n FROM generate_series(1,1000000) AS gs(n); 


变量具有一个有趣的特性:不是错误或优势,而是一个功能:扩展装置存储的数据存在于事务外部-在修复事务和回滚时都将保存它们; 而且,即使执行单独的命令,也可以获得部分数据:

 SELECT pgv_insert('package', 'errs', row(n)) FROM generate_series(1,5) AS gs(n) WHERE 1.0/(n-3)<>0; ERROR: there is a record in the variable "errs" with same key test_parti=# SELECT * FROM pgv_select('package','errs') AS r(i int); i --- 1 2 (2 rows) 


一方面,这不是很方便-在某些情况下有必要删除不正确输入的数据,但在另一些情况下却可能非常有用-例如即使在事务回滚的情况下也保存一些数据。 该文档包含详细信息。

总之,还有更多扩展:

sr_plan,plantuner


sr_plan 保存和恢复查询计划。 包括这样:

 SET sr_plan.write_mode = true; 


之后,所有后续查询的计划将存储在sr_plans表中,直到将此变量设置为false。 保存所有请求(包括重复请求)的计划。

plantuner 支持提示,让调度程序在执行查询时连接或断开指定的索引。 GUC仅有两个变量:enable_index / desable_index:

 SET plantuner.disable_index='id_idx2'; 


全文搜索的扩展名:shared_ispell,pg_tsparser


shared_ispell扩展允许您在共享内存中放置字典 ,它是Standard而不是PostgreSQL。 我们的hunspell-dict集合包含以下语言的字典:

  • hunspell_en_us,
  • hunspell_fr,
  • hunspell_nl_nl,
  • hunspell_ru_ru


pg_tsparser扩展替代的文本搜索分析器 。 此扩展名更改了标准文本解析策略,用于包含下划线以及由下划线分隔的数字和字母的单词。 除了默认返回的单词的各个部分之外,pg_tsparser还返回整个单词。 这对于像这样的技术文档或文章非常重要,在其中可以找到程序代码,并且其中包含诸如“ pg_tsparser”,“ pg_probackup”,“ jsonb_build_object”之类的词。 该解析器不仅将这些单词感知为一组组件,而且还感知为单个标记,从而提高了搜索质量。

1C扩展


  • mchar是与Microsoft SQL Server兼容的可选数据类型;
  • fulleq-提供附加的等于运算符以与Microsoft SQL Server兼容;
  • fasttrun — - , pg_class.


, PostgresPro Standard PostgreSQL. , , , , .

Source: https://habr.com/ru/post/zh-CN479618/


All Articles