最近の奇妙なロックの問題(バージョン5.1.37)に関するMySQLサポートの使用に関する私のメモは次のとおりです。
変更される行に到達するためにトラバースされたすべての行とインデックスエントリはロックされます。内容は次のとおりです:
http://dev.mysql.com/doc /refman/5.1/en/innodb-locks-set.html
「ロック読み取り、UPDATE、またはDELETEは、通常、SQLステートメントの処理でスキャンされるすべてのインデックスレコードにレコードロックを設定します。ステートメントに行を除外するWHERE条件があるかどうかは関係ありません。InnoDBは正確なWHERE条件を覚えていませんが、スキャンされたインデックス範囲しかわかりません。...ステートメントに適したインデックスがなく、MySQLがステートメントを処理するためにテーブル全体をスキャンする必要がある場合、テーブルのすべての行がロックされます。 turnは、他のユーザーによるテーブルへのすべての挿入をブロックします。」
です。多くの場合役立つ回避策は、次のことです。
whichevertableをUPDATEして、primarykeyが入っている場所に設定します(制約がprimarykeyで順序付けられているwhereevertableからprimarykeyを選択します)。
内部選択はロックを取得する必要がないため、更新のために行う作業が少なくなります。 order by句は、更新がInnoDBの物理的な順序と一致するように主キーの順序で行われることを保証します。これは、それを行うための最速の方法です。
あなたの場合のように、多数の行が含まれる場合、選択結果をフラグ列が追加された一時テーブルに格納する方がよい場合があります。次に、フラグが設定されていない一時テーブルから選択して、各バッチを取得します。たとえば1000または10000の制限で更新を実行し、更新後にバッチのフラグを設定します。制限により、ロックの量が許容レベルに保たれますが、選択した作業は1回だけ実行する必要があります。各バッチの後にコミットして、ロックを解除します。
更新の各バッチを実行する前に、インデックス付けされていない列の選択された合計を実行することによって、この作業を高速化することもできます。これにより、ロックを取得せずにデータページがバッファプールにロードされます。その後、ディスクの読み取りが行われないため、ロックはより短い期間続きます。
これは常に実用的であるとは限りませんが、実用的である場合は非常に役立ちます。バッチで実行できない場合は、データがバッファプールに収まるほど小さい場合は、少なくとも最初にselectを試してデータをプリロードすることができます。
可能であれば、READCOMMITTEDトランザクション分離モードを使用してください。参照:
http://dev.mysql.com/doc/refman /5.1/en/set-transaction.html
ロックを減らすには、(デフォルトのステートメントベースのバイナリロギングではなく)行ベースのバイナリロギングを使用する必要があります。
2つの既知の問題:
-
サブクエリは、理想的に最適化されていない場合があります。この場合、それは望ましくない依存サブクエリでした-サブクエリを使用するために私が行った提案は、そのため、この場合の代替案と比較して役に立たないことが判明しました。
-
削除と更新には、selectステートメントと同じ範囲のクエリプランがないため、結果を測定せずに適切に最適化して、実行内容を正確に把握することが難しい場合があります。
これらは両方とも徐々に改善しています。このバグは、更新に利用できる最適化を改善した1つの例ですが、変更は重要であり、大きな悪影響がないことを確認するためにQAが実施されています:
http://bugs.mysql.com/bug.php?id=36569