web-dev-qa-db-ja.com

postgresql内の多数のライブ/デッドタプル/バキュームが機能しない

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つの行でのみ機能します。

  • アプリはJava=で記述されており、hibernateを使用してdb操作を実行しています。
  • Postgresqlのバージョンは9.3.24

もう1つの興味深い観察:-私が停止すると、Javaアプリを実行してから完全なバキュームを実行すると、正常に動作します(行数とライブタプルが等しくなります)。 Java app。から継続的に選択して更新すると、問題が発生します。–

問題/問題

これらのライブタプルは、時々、死んだタプルに移動し、しばらくしてから再びライブになります。

上記の動作により、多くのライブ/デッドタプルがあるため、テーブルからの選択には時間がかかり、サーバーの負荷が増加します。

15
Sahil Aggarwal

VACUUMが仕事をするのを妨げている3つのことがわかっています。

  • 長時間実行トランザクション。

  • コミットされなかった準備済みトランザクション。

  • 古い複製スロット。

詳細は 私のブログ投稿 を参照してください。

3
Laurenz Albe

問題が発生しました☺。

問題を理解するには、次のフローを検討してください。

スレッド1-

  • 休止状態のセッションを開きます
  • Table-Aに対してクエリを実行します
  • subscriber_offset_managerから選択
  • 更新subscriber_offset_manager
  • セッションを閉じます。

タイプの多くのスレッドThread-1並列実行。

スレッド2-

  • これらのタイプのスレッドは並行して実行されます。
  • 休止状態のセッションを開きます
  • Table-Aに対していくつかの選択クエリを作成します
  • セッションを閉じません。(セッションリーク。)

一時的な解決策-pg_cancel_backendを使用してThread-2によって作成されたすべての接続を閉じると、バキュームが機能し始めます。

また、問題を何度も再現し、この解決策を試しましたが、うまくいきました。

今、まだ答えられていない以下の疑問があります。

  1. Postgresがテーブル "subscriber_offset_manager"に関連するデータを表示しないのはなぜですか。
  2. Psqlを使用してTable-Aでselectを実行した場合、Thread-2を実行する代わりに、この問題は再作成されません。
  3. postgresがjdbcでこのように機能する理由.

もう少し心を吹く観察:

  1. 別のセッションで "subscriber_offset_manager"に対してクエリを実行すると、イベントが発生します。
  2. スレッド2がいくつかの3番目のテーブル「Table-C」で作業していて、問題が発生している多くのインスタンスがここで見つかりました
  3. これらすべてのタイプod pg_stat_activityのトランザクション状態は "idle_in_transactionです。"

@Erwin Brandstetterおよび@Laurenz Albe(postgres/jdbcに関連するバグがあることがわかっている場合)。

5
Sahil Aggarwal

結局ロックがあるかもしれません、あなたのクエリは誤解を招くかもしれません:

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 READVACUUMのクリーンアップをさらに困難にします。デフォルト READ COMMITTEDモードの方が制限は少なくなりますが、説明したようにVACUUMは引き続きブロックできます。

関連:

2