Mysql

使用多個 WHERE 列最小化對 UPDATE 的鎖定

  • July 18, 2019

我有一個導致鎖定超時的有問題的查詢:

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

我這裡有兩個問題:

  1. 我有多個伺服器同時送出查詢,它們相互鎖定,有時我會出現鎖定超時甚至死鎖。最佳情況下,伺服器應該更新互斥行,因此根本不應該發生鎖定。有沒有辦法可以跳過更新中的任何鎖定行?
  2. 如果我無法避免鎖定,並且我已經有 col1 的索引和 col2 的另一個索引,Innodb 會鎖定滿足 WHERE 子句中任何條件的所有行還是只鎖定同時滿足這兩個條件的行?如果答案是前者,我可以一起為兩列添加索引還是我還需要刪除我擁有的索引(分別為每列)?

在 each 之前,您需要自己鎖定所有行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 UPDATE儘管如此,您必須在和之間交替UPDATE
  • 由於您同時擁有row1row2在 WHERE 子句中,因此您應該有一個包含兩列的索引。
  • 有一個警告:如果這些列被索引,預計會有些慢,因為列正在更新並且 BTREE 索引頁正在每行更新。您還應該期望 ibdata1 的插入緩衝區部分快速增長(請參閱 InnoDB 地圖

我有很多關於SELECT ... FOR UPDATE和的文章SELECT ... LOCK IN SHARED MODE

更新 2013-03-17 19:21 EDT

由於您有 9 個 WebServers 訪問數據庫伺服器,請試試這個

在 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 標頭檔,以唯一標識哪台機器執行哪個版本的查詢。

引用自:https://dba.stackexchange.com/questions/36885