Debian Linux(8)を搭載したSSDベースのクアッドコア仮想プライベートサーバー(VPS)でPostgreSQL 9.4.15を実行しています。関連するテーブルには、約200万件のレコードがあります。
レコードが頻繁に挿入され、さらに頻繁に(少なくとも数秒ごとに)更新されます。私の知る限りでは、これらの操作を迅速に実行するために適切なすべてのインデックスを用意していますが、それらのほとんどの時間は瞬時に実行されます(ミリ秒単位)。
ただし、約1時間ごとに、UPDATE
クエリの1つは、10秒以上など、過度に時間がかかります。そして、これが発生すると、それは通常、「ブロック」されるクエリの「バッチ」のようなものであり、すべてがほぼ同時に終了します。クエリの1つ、または他のバックグラウンド操作(バキュームなど)がすべてをブロックしているようです。
テーブルitems
には多くの列がありますが、問題に関連する可能性があるのは次のものだけだと思います。
id INTEGER NOT NULL
_(主キー)search_vector TSVECTOR
_last_checkup_at TIMESTAMP WITHOUT TIME ZONE
_そして、これらは関連するインデックスです:
items_pkey PRIMARY KEY, btree (id)
items_search_vector_idx gin (search_vector)
items_last_checkup_at_idx btree (last_checkup_at)
最後に、「接続リーク」警告がログファイルに出力されるたびに_pg_stat_activity
_(すべてのアクティブなPostgres接続/クエリのリスト)の内容をダンプする小さなスクリプトを一緒にリギングした後、考えられる原因のクエリ/列(問題がVPSの誤動作のように外部にあるものではないと想定)。大まかに言って、これらは何度も何度も表示されるように見えるクエリの種類です。
UPDATE items SET last_checkup_at = $1 WHERE items.id = 123245
_UPDATE items SET search_vector = [..] WHERE items.id = 78901
_それらは少し言い換えられていますが、関連するものが欠けていることは間違いありません。 (他のテーブルで)他のクエリが表示されることもありますが、それらは通常、その組み合わせに巻き込まれるのが「不運」だったように見えます。
現在、最初のクエリ(_last_checkup_at
_の設定)はほとんどの場合に表示される傾向がありますが、_search_vector
_を設定するクエリはevery時間のように見えます。 (それに加えて、一般的に発行される最初のクエリのインスタンスはおそらくさらに多くあり、偶然に存在する可能性が高くなります。)
(私はここで解決策を知っていると思いますが、それをバッグに入れていても、他の人のためにここでインシデントを文書化したかったのです...ディープダイブ。)
問題はPostgresの「FASTUPDATE
」メカニズムにあるようです。
FASTUPDATE
は GIN
インデックスで使用可能な設定 であり、これを有効にすると、インデックス(UPDATE
sとおそらくINSERT
sによっても引き起こされる)が "キューに入れられる"ようになります。次に、この「キュー」が大きくなりすぎると、保留中のエントリがGIN
インデックスに適切に統合されます。
FASTUPDATE
の目的は、インデックスの更新を高速化すること(驚きではありません)ですが、残念ながら、時折UPDATE
クエリが非常に遅くなります。私の場合、ヒットを前もって取得することをお勧めします(主に、ログの「遅いクエリ」の警告を回避するため)。
FASTUPDATE
は明らかにデフォルトで有効になっており、PostgreSQL 8.4以降で使用できます。私は次のようにそれを無効にすることができました:
ALTER INDEX items_search_vector_idx SET (FASTUPDATE=OFF);
これを書いている時点で、私はほぼ1週間もそのように実行しており、遅いクエリはほとんどありません。 (時間がかかると思われるクエリは別として、他にはほとんど気づきませんでした。)
また、Postgresメーリングリストの 関連するスレッド で関連情報を見つけることもできます。興味深いことに、Postgres開発者の1人(Tom Lane)は、FASTUPDATE
の保留中のアイテムの処理を「同時挿入をブロックすることは想定されていなかった」と示唆していますが、それが正しいかどうかはわかりません。私の場合、いくつかのクエリが「バックアップ」され、すべてが一度に完了するのがわかります。