MySQL 鎖問題

 

1 獲取鎖等待情況

 

可以通過檢查table_locks_waitedtable_locks_immediate狀態變量來分析系統上的表鎖定爭奪:

mysql> show status like 'Table%';

+----------------------------+----------+

| Variable_name | Value |

+----------------------------+----------+

| Table_locks_immediate | 105 |

| Table_locks_waited | 3 |

+----------------------------+----------+

2 rows in set (0.00 sec)

 

可以通過檢查Innodb_row_lock狀態變量來分析系統上的行鎖的爭奪情況:

mysql> show status like 'innodb_row_lock%';

+----------------------------------------+----------+

| Variable_name | Value |

+----------------------------------------+----------+

| Innodb_row_lock_current_waits | 0 |

| Innodb_row_lock_time | 2001 |

| Innodb_row_lock_time_avg | 667 |

| Innodb_row_lock_time_max | 845 |

| Innodb_row_lock_waits | 3 |

+----------------------------------------+----------+

5 rows in set (0.00 sec)

 

 

另外,針對Innodb類型的表,如果需要察看當前的鎖等待情況, 可以設置InnoDB Monitors,然後通過Show innodb status察看,設置的方式是:

CREATE TABLE innodb_monitor(a INT) ENGINE=INNODB;

 

監視器可以通過發出下列語句來被停止:DROP TABLE innodb_monitor;

 

設置監視器後,在show innodb status的顯示內容中,會有詳細的當前鎖等待的信息,包括表名、鎖類型、鎖定記錄的情況等等,便於進行進一步的分析和問題的確定。

打開監視器以後,默認情況下每15秒會向日志中記錄監控的內容,如果長時間打開會導致.err文件變得非常的巨大,所以我們在確認問題原因之後,要記得刪除監控表以關閉監視器。或者通過使用--console選項來啓動服務器以關閉寫日誌文件。

 

 

2 什麼情況下使用表鎖

 

表級鎖在下列幾種情況下比行級鎖更優越:

 

1. 很多操作都是讀表。

2. 在嚴格條件的索引上讀取和更新,當更新或者刪除可以用單獨的索引來讀取得到時:

3. UPDATE tbl_name SET column=value WHERE unique_key_col=key_value;

4. DELETE FROM tbl_name WHERE unique_key_col=key_value;

5. SELECT INSERT 語句併發的執行,但是隻有很少的 UPDATE DELETE 語句。

6. 很多的掃描表和對全表的 GROUP BY 操作,但是沒有任何寫表。

 

 

 

3 什麼情況下使用行鎖

 

行級鎖定的優點:

1. 當在許多線程中訪問不同的行時只存在少量鎖定衝突。

2. 回滾時只有少量的更改。

3. 可以長時間鎖定單一的行。

行級鎖定的缺點:

1. 比頁級或表級鎖定佔用更多的內存。

2. 當在表的大部分中使用時,比頁級或表級鎖定速度慢,因爲你必須獲取更多的鎖。

3. 如果你在大部分數據上經常進行GROUP BY操作或者必須經常掃描整個表,比其它鎖定明顯慢很多。

4. 用高級別鎖定,通過支持不同的類型鎖定,你也可以很容易地調節應用程序,因爲其鎖成本小於行級鎖定。

 

 

4 insert select …帶來的問題

 

 

當使用insert...select...進行記錄的插入時,如果select的表是innodb類型的,不論insert的表是什麼類型的表,都會對select的表的紀錄進行鎖定。

對於那些從oracle遷移過來的應用, 需要特別的注意, 因爲oracle並不存在類似的問題,所以在oracle的應用中insert...select...操作非常的常見。如果遷移到mysql

數據庫後不進行相應的調整,那麼在進行這個操作期間,對需要select的表實際上是進行的全表掃描導致的所有記錄的鎖定, 將會對應用的其他操作造成非常嚴重的影響。

 

究其主要原因,是因爲mysql在實現複製的機制時和oracle是不同的,如果不進行select表的鎖定,則可能造成從數據庫在恢復期間插入結果集的不同,造成主從數據的不一致。 如果不採用主從複製, 關閉binlog並不能避免對select紀錄的鎖定,因此,如果需要進行可能會掃描大量數據的insert...select操作,我們推薦使用select...into outfileload data infile的組合來實現,這樣是不會對紀錄進

行鎖定的。

 

 

5 next-key鎖對併發插入的影響

 

在行級鎖定中, InnoDB 使用一個名爲next-key locking的算法。 InnoDB以這樣一種方式執行行級鎖定: 當它搜索或掃描表的索引之時, 它對遇到的索引記錄設置共享或獨佔鎖定。因此,行級鎖定事實上是索引記錄鎖定。

 

InnoDB對索引記錄設置的鎖定也影響索引記錄之前的“ 間隙”。如果一個用戶對一個索引上的記錄R有共享或獨佔的鎖定,另一個用戶 不能緊接在R之前以索引的

順序插入一個新索引記錄。這個間隙的鎖定被執行來防止所謂的“ 幽靈問題”。可以用next-key鎖定在你的應用程序上實現一個唯一性檢查:如果你以共享模

式讀數據,並且沒有看到你將要插入的行的重複,則你可以安全地插入你的行,並且知道在讀過程中對你的行的繼承者設置的next-key鎖定與此同時阻止任何人對你的

行插入一個重複。因此, the next-key鎖定允許你鎖住在你的表中並不存在的一些東西。

 

 

 

 

 

6 隔離級別對併發插入的影響

 

 

REPEATABLE READInnoDB的默認隔離級別。帶唯一搜索條件使用唯一索引的SELECT ... FOR UPDATE, SELECT ... LOCK IN SHARE MODE, UPDATE DELETE語句只鎖定找到的索引記錄,而不鎖定記錄前的間隙。用其它搜索條件,這些操作採用next-key鎖定,用next-key鎖定或者間隙鎖定鎖住搜索的索引範圍,並且阻止其它用戶的新插入。

 

在持續讀中,REPEATABLE READ 有一個與READ COMMITTED隔離級別重要的差別:在這個級別,在同一事務內所有持續讀讀取由第一次讀所確定的同一快照。

這個慣例意味着如果你在同一事務內發出數個無格式SELECT語句,這些SELECT語句對相互之間也是持續的。

 

READ COMMITTED隔離級別是一個有些象Oracle的隔離級別。所有SELECT ...FOR UPDATESELECT ... LOCKIN SHARE MOD語句僅鎖定索引記錄,而不鎖定記錄前的間隙,因而允許隨意緊挨着已鎖定的記錄插入新記錄。 UPDATEDELETE語句使用一個帶唯一搜索條件的唯一的索引僅鎖定找到的索引記錄,而不包括記錄前的間隙。在範圍類型UPDATEDELETE語句,InnoDB必須對範圍覆蓋的間隙設置next-key鎖定或間隙鎖定以及其它用戶做的塊插入。 這是很必要的, 因爲要讓MySQL複製和恢復起作用,“ 幽靈行” 必須被阻止掉。

 

如果應用是從基於ORACLE的應用遷移到MYSQL數據庫的,那麼建議使用該隔離級別提供數據庫服務,因爲該隔離級別是最接近ORACLE的默認隔離級別的,遷移可能遇到的鎖問題最小。

 

 

 

7 如何減少鎖衝突

 

 

1. Myisam類型的表:

1) Myisam類型的表可以考慮通過改成Innodb類型的表來減少鎖衝突。

2) 根據應用的情況,嘗試橫向拆分成多個表或者改成Myisam分區對減少鎖衝突也會有一定的幫助。

 

 

2. Innodb類型的表:

1) 首先要確認,在對錶獲取行鎖的時候,要儘量的使用索引檢索紀錄,如果沒有使用索引訪問,那麼即便你只是要更新其中的一行紀錄,也是全表鎖定的。 要確保sql是使用索引來訪問紀錄的,必要的時候,請使用explain檢查sql的執行計劃, 判斷是否按照預期使用了索引。

2) 由於mysql的行鎖是針對索引加的鎖, 不是針對紀錄加的鎖, 所以雖然是訪問不同行的紀錄,但是如果是相同的索引鍵,是會被加鎖的。應用設計的時候也要注意,這裏和Oracle有比較大的不同。

3) 當表有多個索引的時候, 不同的事務可以使用不同的索引鎖定不同的行, 當表有主鍵或者唯一索引的時候, 不是必須使用主鍵或者唯一索引鎖定紀錄, 其他普通索引同樣可以用來檢索紀錄,並只鎖定符合條件的行。

4) SHOW INNODB STATUS來確定最後一個死鎖的原因。查詢的結果中,包括死鎖的事務的詳細信息,包括執行的SQL語句的內容,每個線程已經獲得了什麼鎖,在等待什麼鎖,以及最後是哪個線程被回滾。

詳細的分析死鎖產生的原因,可以通過改進程序有效的避免死鎖的產生。

5) 如果應用並不介意死鎖的出現,那麼可以在應用中對發現的死鎖進行處理。

6) 確定更合理的事務大小,小事務更少地傾向於衝突。

7) 如果你正使用鎖定讀, ( SELECT ... FOR UPDATE... LOCK IN SHARE MODE),試着用更低的隔離級別,比如READ COMMITTED

8) 以固定的順序訪問你的表和行。則事務形成良好定義的查詢並且沒有死鎖。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章