
1、前言介绍
要让它好,先让它坏,然后慢慢预防。要让PostgreSQL数据库快速OOM或者快速把磁盘空间用完,也需要好好设计一番。
2、具全实例
先看看如何快速OOM
postgres=# show max_locks_per_transaction;
max_locks_per_transaction
---------------------------
64
(1 row)
默认情况下一个事务最大拥有锁的数量不能超过64。
咱们把这些值都缩一缩:
vi postgresql.conf:
max_locks_per_transaction = 10
max_connections = 5
让最大锁数量变为10,connection数量最多为5个。
弄一个脚本,一次建300个表试试看:
\set VERBOSE verbose
do language plpgsql $$
declare
tcount int = 300;
begin
for i in 1..tcount loop
execute 'create table tab_'||i||'(id int)';
end loop;
end;
$$;
表建完了,似乎没事。再用另一个脚本把这些表都删掉:
postgres=# do language plpgsql $$
postgres$# declare
postgres$# tcount int = 300;
postgres$# begin
postgres$# for i in 1..tcount loop
postgres$# execute 'drop table tab_'||i;
postgres$# end loop;
postgres$# end;
postgres$# $$;
ERROR: out of shared memory
HINT: You might need to increase max_locks_per_transaction.
CONTEXT: SQL statement "drop table tab_175"
PL/pgSQL function inline_code_block line 6 at EXECUTE
表还没有清干净,就出现了OOM。这是因为lock数量用完了,没有内存可分了。
这里提示需要增加max_locks_per_transaction, 也就是要增加锁相关的共享内存大小。
好像在子事务特别多的逻辑里头,这个消耗量也很大,也容易出现这种现象。
实例2:让一个表的1000条记录,快速达到上G的磁盘消耗,并且没有机会回收。
准备:
CREATE OR REPLACE FUNCTION random_string( int ) RETURNS TEXT as $$
SELECT string_agg(substring('abcdefghijklmnopqrstuvwxyz', round(random() * 26 + 0.5)::integer, 1), '') FROM generate_series(1, $1);
$$ language sql;
drop table if exists t;
create table t (id int, col2 char(1996));
insert into t select n, random_string(1996) from generate_series(1, 1000) as n;
这样就有了一张表,共1000条记录。初如大小:
[22:40:07-postgres@centos1:/var/lib/pgsql/script/issues]$ psql mydb -c "select pg_total_relation_size('t')"
pg_total_relation_size
------------------------
2121728
(1 row)
给他们来点循环更新迭代:
begin;
do language plpgsql $$
declare
tcount int = 1000;
begin
for i in 1..tcount loop
update t set col2 = random_string(1096) where id = i;
end loop;
end;
$$;
commit;
让这个更新操作脚本为:bloat.sql,跑了一段时间以后,你会发现,它的大小定格在:3260416. 差不多不怎么涨了。稍分析一下,因为每页都有差不多死元组的空间可以供复用。不会一直狂涨下去。
我们换一下思路,要让它在update的情况下,不断的换新页,让原有的页剩余空间不足以容纳新的元组去插入。
alter table t alter col2 type char(4096);
alter table t alter col2 set storage main;
truncate t;
insert into t select n, random_string(4096) from generate_series(1, 1000) as n;
在此基础上,我们不断进行更新操作:
mydb=# update t set col2 = random_string(4096) where id = 2; \watch 1
UPDATE 1
Sun 02 Apr 2023 05:46:45 AM CST (every 1s)
UPDATE 1
另一个窗口监测大小变化 :
Sun 02 Apr 2023 05:47:48 AM CST (every 2s)
pg_total_relation_size
------------------------
8437760
(1 row)
Sun 02 Apr 2023 05:47:50 AM CST (every 2s)
pg_total_relation_size
------------------------
8454144
(1 row)
Sun 02 Apr 2023 05:47:52 AM CST (every 2s)
pg_total_relation_size
------------------------
8470528
大小不断的变大。
现弄一个脚本bloat2.sql:
do language plpgsql $$
declare
tcount int = 1000;
begin
for i in 1..tcount loop
update t set col2 = random_string(4096) where id = i;
end loop;
end;
$$;
Sun 02 Apr 2023 05:49:55 AM CST (every 2s)
pg_total_relation_size
------------------------
17260544
(1 row)
使用pgbench加压:
pgbench -f 2.bloat.sql mydb -T 300
Sun 02 Apr 2023 05:58:48 AM CST (every 2s)
pg_total_relation_size
------------------------
124534784
(1 row)
Sun 02 Apr 2023 05:58:50 AM CST (every 2s)
pg_total_relation_size
------------------------
133521408
(1 row)
表的空间大小不断的在增大。这上边只是固定更新某一行数据。如果更新多行,迭加效果会更好。
[ END ]
