破坏你的PG(1): 让它尽快OOM, 或者让表急剧膨胀

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)::integer1), ''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(1996from generate_series(11000as 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(1096where 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(4096from generate_series(11000as 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(4096where 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     ]







请使用浏览器的分享功能分享到微信等