200行のテーブルがあります。しかし、そこに表示されているライブタプルの数はそれ以上です(約60K)。
select count(*) from subscriber_offset_manager;
count
-------
200
(1 row)
SELECT schemaname,relname,n_live_tup,n_dead_tup FROM pg_stat_user_tables where relname='subscriber_offset_manager' ORDER BY n_dead_tup
;
schemaname | relname | n_live_tup | n_dead_tup
------------+---------------------------+------------+------------
public | subscriber_offset_manager | 61453 | 5
(1 row)
しかし、pg_stat_activityとpg_locksからわかるように、開いている接続を追跡することはできません。
SELECT query, state,locktype,mode
FROM pg_locks
JOIN pg_stat_activity
USING (pid)
WHERE relation::regclass = 'subscriber_offset_manager'::regclass
;
query | state | locktype | mode
-------+-------+----------+------
(0 rows)
私もこのテーブルで完全な真空を試しました、以下は結果です:
こちらが出力です。
vacuum FULL VERBOSE ANALYZE subscriber_offset_manager;
INFO: vacuuming "public.subscriber_offset_manager"
INFO: "subscriber_offset_manager": found 0 removable, 67920 nonremovable row versions in 714 pages
DETAIL: 67720 dead row versions cannot be removed yet.
CPU 0.01s/0.06u sec elapsed 0.13 sec.
INFO: analyzing "public.subscriber_offset_manager"
INFO: "subscriber_offset_manager": scanned 710 of 710 pages, containing 200 live rows and 67720 dead rows; 200 rows in sample, 200 estimated total rows
VACUUM
SELECT schemaname,relname,n_live_tup,n_dead_tup FROM pg_stat_user_tables where relname='subscriber_offset_manager' ORDER BY n_dead_tup
;
schemaname | relname | n_live_tup | n_dead_tup
------------+---------------------------+------------+------------
public | subscriber_offset_manager | 200 | 67749
そして10秒後
SELECT schemaname,relname,n_live_tup,n_dead_tup FROM pg_stat_user_tables where relname='subscriber_offset_manager' ORDER BY n_dead_tup
;
schemaname | relname | n_live_tup | n_dead_tup
------------+---------------------------+------------+------------
public | subscriber_offset_manager | 68325 | 132
このテーブルへのアプリのクエリ方法
私たちのアプリケーションは通常、いくつかの行を選択し、いくつかのビジネス計算に基づいて、行を更新します。
select query-いくつかのIDに基づいて選択
select * from subscriber_offset_manager where shard_id = 1;
クエリを更新-この選択されたシャードIDの他の列を更新します
約20のスレッドがこれを並行して実行し、1つのスレッドが1つの行でのみ機能します。
もう1つの興味深い観察:-私が停止すると、Javaアプリを実行してから完全なバキュームを実行すると、正常に動作します(行数とライブタプルが等しくなります)。 Java app。から継続的に選択して更新すると、問題が発生します。–
問題/問題
これらのライブタプルは、時々、死んだタプルに移動し、しばらくしてから再びライブになります。
上記の動作により、多くのライブ/デッドタプルがあるため、テーブルからの選択には時間がかかり、サーバーの負荷が増加します。
VACUUM
が仕事をするのを妨げている3つのことがわかっています。
長時間実行トランザクション。
コミットされなかった準備済みトランザクション。
古い複製スロット。
詳細は 私のブログ投稿 を参照してください。
問題が発生しました☺。
問題を理解するには、次のフローを検討してください。
スレッド1-
タイプの多くのスレッドThread-1並列実行。
スレッド2-
一時的な解決策-pg_cancel_backendを使用してThread-2によって作成されたすべての接続を閉じると、バキュームが機能し始めます。
また、問題を何度も再現し、この解決策を試しましたが、うまくいきました。
今、まだ答えられていない以下の疑問があります。
もう少し心を吹く観察:
@Erwin Brandstetterおよび@Laurenz Albe(postgres/jdbcに関連するバグがあることがわかっている場合)。
結局ロックがあるかもしれません、あなたのクエリは誤解を招くかもしれません:
SELECT query, state,locktype,mode
FROM pg_locks
JOIN pg_stat_activity USING (pid)
WHERE relation = 'subscriber_offset_manager'::regclass
pg_locks.pid
はNULLにすることができます。その場合、結合によって行が削除されます。 Postgres 9.3のマニュアル:
このロックを保持または待機しているサーバープロセスのプロセスID、または準備されたトランザクションによってロックが保持されている場合はnull
大胆な強調鉱山。 (10ページでも同じです。)
簡単なクエリで何か得られますか?
SELECT * FROM pg_locks
WHERE relation = 'subscriber_offset_manager'::regclass;
これはVACUUM
が不平を言う理由を説明することができます:
DETAIL: 67720 dead row versions cannot be removed yet.
これは、アプリケーションロジック/クエリの問題を示し、必要以上の行をロックします。
私の最初のアイデアは長期実行トランザクションで、単純なSELECT
(低いACCESS SHARE
ロック)は、VACUUM
がそのジョブを実行するのをブロックできます。並列の20のスレッドは、連鎖してVACUUM
を無期限にロックアウトする可能性があります。トランザクション(およびそのロック)をできるだけ短くしてください。また、クエリが最適化され、必要以上の行をロックしないようにしてください。
注意すべきもう1つのこと: トランザクション分離 レベルSERIALIZABLE
またはREPEATABLE READ
VACUUM
のクリーンアップをさらに困難にします。デフォルト READ COMMITTED
モードの方が制限は少なくなりますが、説明したようにVACUUM
は引き続きブロックできます。
関連: