これで数日間髪を引き裂いています。しばらくの間、本番システムでパフォーマンスの問題を引き起こす排他的なデータベース ロックに問題がありました。詳しく調べてみると、排他ロックを保持しているクエリが Hibernate の遅延読み込みによって生成された選択であることがわかりました。
Spring トランザクション管理を使用@Transactional( readOnly= "true")
しており、サービス エントリ ポイントで定義されています。転送オブジェクトにマッピングされたエンティティを使用して、リクエストごとのセッション モデルを使用します。データベースのデフォルトの分離レベルはコミット読み取りです。JDBC ドライバーは、コミットされた読み取りで構成されます。以下を使用して、問題の実際のトランザクションの分離レベルを確認しました。
select current_setting('transaction_isolation')
コミットされた読み取りを返します。JPA を使用して Hibernate マッピングを構成しています。トランザクションを明示的にアップグレードする場所はどこにもありません。この特定のトランザクションでは、select ステートメントのみを実行しています。Hibernate SQL ロギングをオンにすると、次のいずれも表示されません。
select ... for update
単純な select ステートメントのみがログに記録されています。
ここでは、2 つのうちの 1 つが起こっているようです。コミットされた読み取りの私の理解は完全にオフであり、コミットされた読み取りの分離レベルは、選択を実行するトランザクションの期間中、排他的な行レベルのロックを保持する必要があります。または、何か他のことが起こっていて、トランザクションによって保持されているロックを誤ってアップグレードしています。
どんな助けでも大歓迎です。
編集1:
わかりました、これは長く曲がりくねった道でした。これはロックとはまったく関係がないことがわかりました。ロックを検出するために使用していたクエリは古く、「virtualxid」のロック タイプを示しています。掘り下げてみると、virtualxid はすべてのトランザクションがそれ自体で取得するロックであることがわかります。これは、この議論とは関係のない PostgreSQL の内部的な理由によるものです。真の排他的ロックをテストする別の監視クエリを cron で作成しましたが、まだ確認していません。
これは、「virtualxid」ロックを監視するために使用しているクエリです。これは、この時点で実行時間の長いクエリ モニターに似ています。
SELECT pg_stat_activity.datname, pg_locks.mode, pg_locks.locktype, pg_locks.granted, pg_stat_activity.usename,pg_stat_activity.query,age(now(),pg_stat_activity.query_start) AS "age", pg_stat_activity.pid
FROM pg_stat_activity,pg_locks
LEFT OUTER JOIN pg_class ON (pg_locks.relation = pg_class.oid)
WHERE
age(now(),pg_stat_activity.query_start) > interval '1 minute' AND
pg_stat_activity.datname <> 'postgres' AND
pg_locks.pid=pg_stat_activity.pid AND
pg_stat_activity.query not like '%autovacuum%' AND
pg_stat_activity.query not like '%COPY%stdout%'
order by query_start;
そして、これが私たちが得ているいくつかの出力です:
<redacted> | ExclusiveLock | virtualxid | t | <redacted> | SELECT current_timestamp | 01:03:51.809594 | 22578
current_timestamp を選択するだけで、1 時間以上実行されます!!!
とにかく、興味のある人にとっては、これらの不思議な長時間実行クエリが時折データベース接続プールを使い果たしているように見え始めました. そのため、接続プールの制限を引き上げたので、ライブ サイトは正常に機能するようになりました。アプリ側のタイムアウトと再試行ロジックを重要なプロセスに配置して、時折の問題を処理しています。そして最近では、これらの異常に実行されるクエリの 1 つにサービスを提供するために、少なくとも 1 つのデータベース スレッドが停止することがよくあります。間違いなく理想的ではありません:(
コストベースの自動バキュームを有効にして、これが問題に役立つかどうかを確認します.
編集2:
これは非常に長い旅であることが判明しました。これで終わりかもしれません。この動作に対応して、上記で導入したデータベース クエリの監視に加えて、バッチ処理のエラー レポートを強化しました。いくつかのインテリジェントなタイムアウトとともに、これにより、特定のアプリケーションのユースケースを長時間実行されるデータベース クエリに関連付けることができました。これにより、本番環境で見られるエラーに対応して、特定の使用法で JVM ノードがハングするのを防ぐことができました。
また、あるプロセスで長時間実行される読み取り専用の TX が、同じデータベースに接続している他のプロセスをハングアップさせるという問題も解決できました。これは、物事が少し奇妙になるところです。同じデータベースに接続するすべての Java プロセスに対して、hibernate-memcached を使用して、hibernate の 2 番目のレベルのキャッシュを共有 memcached サーバーに移動していました。異常なハング動作が発生するたびに、JVM プロセスに大量の memcached クライアント スレッドが存在していました。
hibernate-memcached モジュールを削除した後、2 番目のレベルのキャッシュを ehcache に戻したところ、複数の JVM を弱体化させる奇妙なハングがなくなったことに気付きました。TX 内で本来よりも多くのことが起こっていることを知らせる電子メールを時折受け取ります。大規模な場合、これらの長い TX が多すぎるため、単一の JVM プロセスがハングアップすることがあります。しかし、1 つの JVM 内のプロセスが他の JVM に何らかの影響を与えることはなくなりました。以前は、悪い TX 動作を示す最初のノードを強制終了するまで、追加のノードが応答しなくなることがわかりました。
これは意味がありません。しかし、その後、この問題は決してしませんでした:)
-- ティム