数百万行をPostgreSQL 9.5データベースに挿入して、メモリ使用量が絶えず増加しているのを観察しています。テーブルはそれほど大きくなく、実行される操作(挿入によってPl/Python関数がトリガーされる)はそれほど高価ではないはずなので、なぜこれが発生するのでしょうか。
現在、PostgreSQLは使用可能な合計60 GBのうち約50 GBを使用しています。特にプロセスがメモリ不足になるのではないかと心配しているため、PostgreSQLが50 GBをどのように使用しているかを理解したいと思います。
[更新]今夜、PostgreSQLはメモリを使い果たし、OSによって強制終了されました。
$ pg_top
last pid: 13535; load avg: 1.26, 1.41, 1.42; up 2+02:57:11 19:29:26
3 processes: 1 running, 2 sleeping
CPU states: 12.4% user, 0.0% Nice, 0.1% system, 87.4% idle, 0.0% iowait
Memory: 63G used, 319M free, 192M buffers, 28G cached
DB activity: 2 tps, 0 rollbs/s, 0 buffer r/s, 100 hit%, 42 row r/s, 0 row w/s
DB I/O: 0 reads/s, 0 KB/s, 0 writes/s, 0 KB/s
DB disk: 98.0 GB total, 41.9 GB free (57% used)
Swap: 38M used, 1330M free, 12M cached
Re-run SQL for analysis:
PID USERNAME PRI Nice SIZE RES STATE TIME WCPU CPU COMMAND
8528 postgres 20 0 50G 39G run 18.3H 97.55% 99.35% postgres: postgres my_db ::1(51692) EXECUTE
11453 postgres 20 0 16G 157M sleep 0:06 0.00% 0.00% postgres: postgres my_db ::1(51808) idle
13536 postgres 20 0 16G 17M sleep 0:00 0.00% 0.00% postgres: postgres postgres [local] idle
$ top
top - 21:51:48 up 2 days, 5:19, 4 users, load average: 1.40, 1.31, 1.23
Tasks: 214 total, 2 running, 212 sleeping, 0 stopped, 0 zombie
%Cpu(s): 12.4 us, 0.0 sy, 0.0 ni, 87.4 id, 0.0 wa, 0.0 hi, 0.0 si, 0.1 st
KiB Mem : 65969132 total, 341584 free, 40964108 used, 24663440 buff/cache
KiB Swap: 1400828 total, 1361064 free, 39764 used. 17366148 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
8528 postgres 20 0 54.563g 0.043t 4.886g R 99.0 69.3 1236:27 postgres
$ htop
PID USER PRI NI VIRT RES SHR S CPU% MEM% TIME+ Command
8528 postgres 20 0 54.8G 43.8G 5028M R 98.3 69.7 20h43:51 postgres: postgres my_db ::1(51692) EXECUTE
8529 postgres 20 0 54.8G 43.8G 5028M S 0.0 69.7 0:00.04 postgres: postgres my_db ::1(51692) EXECUTE
8530 postgres 20 0 54.8G 43.8G 5028M S 0.0 69.7 0:00.04 postgres: postgres my_db ::1(51692) EXECUTE
8531 postgres 20 0 54.8G 43.8G 5028M S 0.0 69.7 0:00.03 postgres: postgres my_db ::1(51692) EXECUTE
8532 postgres 20 0 54.8G 43.8G 5028M S 0.0 69.7 0:00.04 postgres: postgres my_db ::1(51692) EXECUTE
8533 postgres 20 0 54.8G 43.8G 5028M S 0.0 69.7 0:00.07 postgres: postgres my_db ::1(51692) EXECUTE
8534 postgres 20 0 54.8G 43.8G 5028M S 0.0 69.7 0:00.06 postgres: postgres my_db ::1(51692) EXECUTE
8535 postgres 20 0 54.8G 43.8G 5028M S 0.0 69.7 0:00.06 postgres: postgres my_db ::1(51692) EXECUTE
8270 postgres 20 0 15.5G 5915M 5913M S 0.0 9.2 1:16.71 postgres: checkpointer process
11453 postgres 20 0 15.5G 4990M 4968M S 0.0 7.7 0:33.91 postgres: postgres my_db ::1(51808) idle
8268 postgres 20 0 15.5G 398M 397M S 0.0 0.6 0:42.65 /usr/lib/postgresql/9.5/bin/postgres -D /var/lib/postgresql/9.5/main -c config_file=/etc/postgresql/9.5/main/postgresql.conf
8271 postgres 20 0 15.5G 124M 122M S 0.0 0.2 0:11.12 postgres: writer process
439 root 20 0 68464 34500 30156 S 0.0 0.1 0:05.07 /lib/systemd/systemd-journald
8272 postgres 20 0 15.5G 21232 19488 S 0.0 0.0 1:11.16 postgres: wal writer process
my_db=# -- https://wiki.postgresql.org/wiki/Disk_Usage#General_Table_Size_Information
oid | table_schema | table_name | row_estimate | total_bytes | index_bytes | toast_bytes | table_bytes | total | index | toast | table
-----------+--------------------+-------------------------+--------------+-------------+-------------+-------------+-------------+------------+------------+------------+------------
123037947 | public | my_second_table | 9482 | 233570304 | 36601856 | 107692032 | 89276416 | 223 MB | 35 MB | 103 MB | 85 MB
123037936 | public | my_table | 4.42924e+06 | 4362895360 | 104685568 | 8192 | 4258201600 | 4161 MB | 100 MB | 8192 bytes | 4061 MB
my_db=# SELECT c.relname,
my_db-# pg_size_pretty(count(*) * 8192) as buffered, round(100.0 * count(*) / (SELECT setting FROM pg_settings WHERE name='shared_buffers')::integer,1) AS buffers_percent,
my_db-# round(100.0 * count(*) * 8192 / pg_relation_size(c.oid),1) AS percent_of_relation,
my_db-# round(100.0 * count(*) * 8192 / pg_table_size(c.oid),1) AS percent_of_table
my_db-# FROM pg_class c
my_db-# INNER JOIN pg_buffercache b
my_db-# ON b.relfilenode = c.relfilenode
my_db-# INNER JOIN pg_database d
my_db-# ON (b.reldatabase = d.oid AND d.datname = current_database())
my_db-# GROUP BY c.oid,c.relname
my_db-# ORDER BY 3 DESC
my_db-# LIMIT 10;
relname | buffered | buffers_percent | percent_of_relation | percent_of_table
---------------------------------+------------+-----------------+---------------------+------------------
my_table | 3995 MB | 26.0 | 100.0 | 100.0
my_table_pkey | 98 MB | 0.6 | 100.0 | 100.0
my_second_table | 85 MB | 0.6 | 100.1 | 45.3
pg_toast_123037947 | 73 MB | 0.5 | 100.1 | 100.0
pg_toast_123037947_index | 30 MB | 0.2 | 100.1 | 100.0
my_second_table_parent_id_idx | 22 MB | 0.1 | 100.1 | 100.0
my_second_table_pkey | 13 MB | 0.1 | 100.2 | 100.0
pg_constraint_oid_index | 16 kB | 0.0 | 100.0 | 100.0
sql_languages | 40 kB | 0.0 | 500.0 | 83.3
pg_transform_type_lang_index | 8192 bytes | 0.0 | 100.0 | 100.0
my_db=# SELECT COUNT(*) FROM pg_stat_activity;
count
-------
2
$ Sudo pmap -p 8528
8528: postgres: postgres my_db ::1(51692) EXECUTE
000000e0cd2b7000 6168K r-x-- /usr/lib/postgresql/9.5/bin/postgres
000000e0cdabc000 132K r---- /usr/lib/postgresql/9.5/bin/postgres
000000e0cdadd000 48K rw--- /usr/lib/postgresql/9.5/bin/postgres
000000e0cdae9000 316K rw--- [ anon ]
000000e0ce548000 592K rw--- [ anon ]
000000e0ce5dc000 35663940K rw--- [ anon ]
…
$ less postgres.conf
# …
max_connections = 20
shared_buffers = 15GB
work_mem = 384MB
maintenance_work_mem = 2GB
fsync = off
synchronous_commit = off
full_page_writes = off
max_wal_size = 8GB
min_wal_size = 4GB
checkpoint_completion_target = 0.9
effective_cache_size = 45GB
top
とhtop
は後で呼び出されることに注意してください。
AFTER INSERT...FOR EACH ROW
として定義されたトリガーは、挿入されたすべての行の情報をキューに入れ、ステートメントの最後で各行のトリガーを起動します。したがって、1つのステートメントで数百万のレコードを挿入すると、そのキューは大量のメモリを消費します。
BEFORE INSERTはこれを行わず、各行が挿入される直前に各行のトリガー関数を実行し、何もキューに入れません。可能であれば、BEFOREトリガーに書き換えます。