UPDATE
が原因で、デッドロックが発生しています。 クエリはテーブル内のすべての行をロックしており、使用されるインデックス(またはその欠如)に応じて、2つの異なるセッションがわずかに異なる順序でそれらをロックする可能性があります。 UPDATE
であることを忘れないでください 、DELETE
、およびSELECT ... FOR UPDATE
それらの行がすべてのWHERE
と一致するかどうかに関係なく、遭遇するすべての行をロックします 条件かどうか。したがって、それらを使用するときは、インデックス(理想的には主キー)を使用し、あいまいな条件や幅広い選択条件を回避することによって、それらができるだけ少ない行に遭遇するように努める必要があります。
ワークキューに関する私の提案は、ほぼ普遍的です。ロックは可能な限り少なく、常に決定論的な順序で行うことです。したがって、一般的に:
- 非ロック読み取りを使用します(通常の
SELECT
)あなたの労働者が行う方法を知っていて、現在請求されていないものを探すことによって行うべき仕事を見つけるために(lease_owner IS NULL AND lease_expiry IS NULL
-または同様のもの)。 - 1つの作業項目を選択します(または、勇気がある場合はいくつかを選択しますが、1つははるかに単純で、通常は完全に許容できるパフォーマンスを実現します)。
- 作業項目を更新します(要求するためですが、いずれの場合も更新する必要があります):
- トランザクションを開きます。
- 選択した作業項目を
SELECT ... FOR UPDATE
でロックします -請求されなくなった場合は、中止して別のものを選択します。 - 選択した作業項目を、ワーカーIDとそのリースの有効期限で更新します。
- すぐに取引をコミットします。
- リースされた作業項目の作業を開始します。
- 他のプロセスでは、別のポーラーが放棄された作業を探し、それを要求しません(上記と同じ更新プロセスを介して)。
この設計(1秒あたり数千のジョブ)を使用すると、本質的に競合や順序の問題が発生することなく、非常に高いスループットを簡単に得ることができます。他のポーラーと競合する可能性が低い作業を選択するための最適化は、単純で効果的です(たとえば、ジョブIDの係数など、ジョブの枯渇を回避するために選択されます)。重要なのは、仕事の選択における対立は大丈夫であることを覚えておくことです。 -中止して再試行するだけで、すべてが非常に迅速に進みます。
ワークキューアイテム/ジョブのすべてのロック書き込みは、単一行でのみ実行する必要があります。 主キーのみ 。