作者:汉斯·尤尔根·舍尔希(Hans-JürgenSchönig),从上世纪90年代就有应用PostgreSQL的教训,他是CYBERTEC公司的CEO与技术带头人,CYBERTEC是该畛域的市场领导者之一,自2000年以来已为寰球有数客户提供服务。他著有图书《Mastering PostgreSQL 9.6: A comprehensive guide for PostgreSQL 9.6 developers and administrators》和《Mastering PostgreSQL 11,Second Edition》,这两本英文图书均曾经由武汉大学彭煜玮老师翻译实现并均已出版,中文书名别离为《由浅入深PostgreSQL》、《精通PostgreSQL 11第二版》。

译者:类延良,任职于瀚高根底软件股份有限公司,PostgreSQL数据库技术爱好者,10g &11g OCM,OGG认证专家。

“out of shared memory”:你们中的有些人可能曾经在PostgreSQL中看到了该谬误音讯。然而它的真正含意是什么,如何预防呢?实际上,问题并不像乍看起来那样艰涩难懂。max_locks_per_transaction是您须要用来防止麻烦的要害配置参数。

out of shared memory:何时产生
PostgreSQL应用的大多数共享内存都是固定大小的。对于I/O缓存(shared buffers)以及许多其余组件来说,这都是正确的。这些组件之一与锁定无关。如果您在事务中创立一个表,PostgreSQL必须跟踪您的流动以确保并发事务不能删除您创立的表。跟踪流动很重要,因为您要确保DROP TABLE(或某些其余DDL)必须期待所有读取事务曾经终止。问题是,您必须将无关跟踪流动的信息存储在某个地位,而这正是您必须理解的内容。

让咱们运行一个简略的脚本:

BEGIN; SELECT 'CREATE TABLE a' || id || ' (id int);'FROM generate_series(1, 20000) AS id;gexec

该脚本的作用是启动一个事务并生成20000个 CREATE TABLE语句。它只是生成SQL,而后主动执行该SQL(gexec将前一条SQL语句的后果视为输出)。

让咱们看看SELECT语句产生了什么……

BEGIN ?column? ---------------------------- CREATE TABLE a1 (id int); CREATE TABLE a2 (id int); CREATE TABLE a3 (id int); CREATE TABLE a4 (id int); CREATE TABLE a5 (id int);...

当初让咱们看看PosgreSQL做了什么:

...CREATE TABLECREATE TABLEERROR:  out of shared memoryHINT:  You might need to increase max_locks_per_transaction.ERROR:  current transaction is aborted, commands ignored until end of transaction blockERROR:  current transaction is aborted, commands ignored until end of transaction blockERROR:  current transaction is aborted, commands ignored until end of transaction blockERROR:  current transaction is aborted, commands ignored until end of transaction block...

在创立了几千张表之后,PostgreSQL会报错:“out of shared memory”。您能够看到咱们在单个事务中创立了所有这些表。PostgreSQL必须锁定它们,最终耗尽内存。请记住:数据库应用固定大小的共享内存区域来保留这些锁。

一个逻辑问题是:这个内存区域的大小是多少?有两个参数起作用:

test=# SHOW max_connections; max_connections----------------- 100(1 row)test=# SHOW max_locks_per_transaction; max_locks_per_transaction--------------------------- 64(1 row)

咱们能够在共享内存中保留的锁的数量为max_connections x max_locks_per_transaction。请记住,行级锁与此不相干。您能够轻松地执行上面的SQL语句:

SELECT * FROM billions_of_rows FOR UPDATE;

下面的SQL语句不会用完内存,因为行锁存储在磁盘上,而不是RAM中。因而,锁的数量与表的数量是相干的–与行的数量无关。

查看pg_locks
您如何理解以后产生的状况?为了演示您能够做什么,我筹备了一个小例子:

test=# CREATE TABLE t_demo (id int);CREATE TABLE

首先,您能够创立一个简略的表。
您可能晓得,在PostgreSQL中,名称基本不相干。在外部,只有数字才重要。要获取简略表的对象ID,请尝试以下语句:

test=# SELECT oid, relkind, relname FROM    pg_class WHERE relname = 't_demo'; oid   | relkind | relname--------+---------+--------- 232787 | r       | t_demo(1 row)

在我的示例中,对象ID为232787。让咱们找出该数字在哪里弹出:

test=# BEGIN;BEGINtest=# SELECT * FROM t_demo; id----(0 rows)test=# xExpanded display is on.test=# SELECT * FROM pg_locks WHERE relation = '232787';-[ RECORD 1 ]------+----------------locktype           | relationdatabase           | 187812relation           | 232787page               |tuple              |virtualxid         |transactionid      |classid            |objid              |objsubid           |virtualtransaction | 3/6633pid                | 106174mode               | AccessShareLockgranted            | tfastpath           | t

因为咱们正在从表中读取数据,因而您能够看到PostgreSQL必须保留ACCESS SHARE LOCK,这能确保不会以侵害并发SELECT语句的形式删除或批改表(= DDL)。
在一个事务中解决的表越多,pg_locks的条目就越多。在大量并发的状况下,多个条目可能会成为问题。

PostgreSQL分区及其与“out of shared memory”的关系
如果您正在运行典型的应用程序,则因为相干锁的总数通常很少,因而内存不足谬误基本上很少产生。然而,如果您重大依赖过多的分区,则状况会有所不同。在PostgreSQL中,分区基本上是一个一般表,因而被视为一般表。因而,锁定可能成为问题。

让咱们看上面的例子:

BEGIN;CREATE TABLE t_part (id int) PARTITION BY LIST (id);SELECT 'CREATE TABLE t_part_' || id || ' PARTITION OF t_part FOR VALUES IN (' || id || ');'FROM    generate_series(1, 1000) AS id;gexecSELECT count(*) FROM t_part;

首先,创立一个父表。而后,增加1000个分区。为了简略起见,每个分区仅容许仅包容一行-然而临时不要放心。而后,执行一个简略的SELECT语句-保障能够读取所有分区。

以下清单显示了脚本生成的用于创立分区的SQL:

 ?column? -------------------------------------------------------------------- CREATE TABLE t_part_1 PARTITION OF t_part FOR VALUES IN (1); CREATE TABLE t_part_2 PARTITION OF t_part FOR VALUES IN (2); CREATE TABLE t_part_3 PARTITION OF t_part FOR VALUES IN (3); CREATE TABLE t_part_4 PARTITION OF t_part FOR VALUES IN (4); CREATE TABLE t_part_5 PARTITION OF t_part FOR VALUES IN (5);...

运行后

SELECT count(*) FROM t_part

申明,当初的重要察看是:

SELECT  count(*)FROM    pg_locksWHERE   mode = 'AccessShareLock'; count------- 1004(1 row)

PostgreSQL曾经须要超过1000个锁来执行此操作。因而,分区将减少此共享内存字段的使用率,并使“out of memory”的可能性更大。如果您正在应用大量分区,则能够更改max_locks_per_transaction。

最初
如果您对数据迷信和机器学习感兴趣,能够查看Kevin Speyer在“强化学习”上的帖子能够在这里找到
(https://www.cybertec-postgres...

原文链接:
https://www.cybertec-postgresql.com/en/postgresql-you-might-need-to-increase-max_locks_per_transaction/

更多精彩内容,请关注以下平台、网站:

中国PostgreSQL分会官网公众号(技术文章、技术流动):
开源软件联盟PostgreSQL分会

中国PostgreSQL分会技术问答社区:
www.pgfans.cn

中国PostgreSQL分会官方网站:
www.postgresqlchina.com