web-dev-qa-db-ja.com

複数のWHERE列を持つUPDATEでのロックを最小限に抑える

ロックのタイムアウトを引き起こしている問題のあるクエリがあります。

UPDATE <some_table> SET col1=<some value> 
WHERE col1 IS NULL AND col2 > <some value> 
ORDER BY col2 
LIMIT 100    

ここに2つの問題があります。

  1. 複数のサーバーが同時にクエリをコミットしていて、お互いにロックアウトしていて、ロックタイムアウトやデッドロックが発生することがあります。最適には、サーバーは相互に排他的な行を更新して、ロックがまったく発生しないようにする必要があります。更新でロックされた行をスキップできる方法はありますか?

  2. ロックを回避できず、col1のインデックスとcol2のインデックスがすでにある場合、InnodbはWHERE句の任意の条件を満たすすべての行をロックしますか、または両方の条件を満たす行のみをロックしますか?答えが前者の場合、2つの列のインデックスを一緒に追加できますか、または(各列に個別に)持っているインデックスも削除する必要がありますか?

3
Noha

UPDATEの前に、自分ですべての行をロックする必要があります。

MySQLのドキュメントSELECT ... LOCK FOR UPDATE を参照してください。これにより、通過するすべての行に対して排他ロックが実行されます。次に、テーブルに対して必要なUPDATEをフォローアップできます。

あなたの特定のケースでは、これを行うでしょう:

SELECT * FROM <some_table>
WHERE row1 IS NULL AND row2 > <some value> 
ORDER BY row2 LIMIT 100;
UPDATE <some_table> SET row1=<some value> 
WHERE row1 IS NULL AND row2 > <some value> 
ORDER BY row2 LIMIT 100    

インデックス作成

  • データをクエリするすべての可能な方法をサポートするには、テーブルに完全にインデックスを付ける必要があります。それにもかかわらず、SELECT ... FOR UPDATEUPDATEを交互に使用する必要があります。
  • WHERE句にrow1row2の両方があるため、両方の列を含むインデックスが必要です。
  • 警告が1つあります。これらの列にインデックスが付けられている場合、列が更新され、BTREEインデックスページが行ごとに更新されるため、多少の遅延が予想されます。また、ibdata1の挿入バッファーセクションの急激な増加も予想されます( InnoDBマップを参照してください )。

SELECT ... FOR UPDATESELECT ... LOCK IN SHARED MODEをテーマに多くの投稿があります。

UPDATE 2013-03-17 19:21 EDT

DBサーバーにアクセスする9つのWebサーバーがあるので、これを試してください。

WebServer1で実行

SELECT * FROM <some_table>
WHERE row1 IS NULL AND row2 > <some value> 
ORDER BY row2 LIMIT 0,100;
UPDATE <some_table> SET row1=<some value> 
WHERE row1 IS NULL AND row2 > <some value> 
ORDER BY row2 LIMIT 0,100;  

WebServer2で実行

SELECT * FROM <some_table>
WHERE row1 IS NULL AND row2 > <some value> 
ORDER BY row2 LIMIT 100,100;
UPDATE <some_table> SET row1=<some value> 
WHERE row1 IS NULL AND row2 > <some value> 
ORDER BY row2 LIMIT 100,100;  

WebServer3で実行

SELECT * FROM <some_table>
WHERE row1 IS NULL AND row2 > <some value> 
ORDER BY row2 LIMIT 200,100;
UPDATE <some_table> SET row1=<some value> 
WHERE row1 IS NULL AND row2 > <some value> 
ORDER BY row2 LIMIT 200,100;  

WebServer9に至るまで、

SELECT * FROM <some_table>
WHERE row1 IS NULL AND row2 > <some value> 
ORDER BY row2 LIMIT 800,100;
UPDATE <some_table> SET row1=<some value> 
WHERE row1 IS NULL AND row2 > <some value> 
ORDER BY row2 LIMIT 800,100;  

PHP一意のヘッダーファイルを配置して、どのマシンがどのバージョンのクエリを実行するかを特定する必要があります。

2
RolandoMySQLDBA