能夠經過檢查table_locks_waited和table_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選項來啓動服務器以關閉寫日誌文件。
mysql
表級鎖在下列幾種狀況下比行級鎖更優越:算法
不少操做都是讀表。sql
在嚴格條件的索引上讀取和更新,當更新或者刪除能夠用單獨的索引來讀取獲得時:數據庫
UPDATE tbl_name SET column=value WHERE unique_key_col=key_value;安全
DELETE FROM tbl_name WHERE unique_key_col=key_value;服務器
SELECT 和 INSERT 語句併發的執行,可是隻有不多的 UPDATE 和 DELETE 語句。多線程
不少的掃描表和對全表的 GROUP BY 操做,可是沒有任何寫表。併發
行級鎖定的優勢:oracle
當在許多線程中訪問不一樣的行時只存在少許鎖定衝突。spa
回滾時只有少許的更改。
能夠長時間鎖定單一的行。
行級鎖定的缺點:
比頁級或表級鎖定佔用更多的內存。
當在表的大部分中使用時,比頁級或表級鎖定速度慢,由於你必須獲取更多的鎖。
若是你在大部分數據上常常進行GROUP BY操做或者必須常常掃描整個表,比其它鎖定明顯慢不少。
用高級別鎖定,經過支持不一樣的類型鎖定,你也能夠很容易地調節應用程序,由於其鎖成本小於行級鎖定。
當使用insert...select...進行記錄的插入時,若是select的表是innodb類型的,不論insert的表是什麼類型的表,都會對select的表的紀錄進行鎖定。
對於那些從oracle遷移過來的應用,須要特別的注意,由於oracle並不存在相似的問題,因此在oracle的應用中insert...select...操做很是的常見。例如:有時候會對比較多的紀錄進行統計分析,而後將統計的中間結果插入到另一個表,這樣的操做由於進行的很是少,因此可能並無設置相應的索引。若是遷移到mysql數據庫後不進行相應的調整,那麼在進行這個操做期間,對須要select的表其實是進行的全表掃描致使的全部記錄的鎖定,將會對應用的其餘操做形成很是嚴重的影響。
究其主要緣由,是由於mysql在實現複製的機制時和oracle是不一樣的,若是不進行select表的鎖定,則可能形成從數據庫在恢復期間插入結果集的不一樣,形成主從數據的不一致。若是不採用主從複製,關閉binlog並不能避免對select紀錄的鎖定,某些文檔中提到能夠經過設置innodb_locks_unsafe_for_binlog來避免這個現象,當這個參數設置爲true的時候,將不會對select的結果集加鎖,可是這樣的設置將可能帶來很是嚴重的隱患。若是使用這個binlog進行從數據庫的恢復,或者進行主數據庫的災難恢復,都將可能和主數據庫的執行效果不一樣。
所以,咱們並不推薦經過設置這個參數來避免insert...select...致使的鎖,若是須要進行可能會掃描大量數據的insert...select操做,咱們推薦使用select...into outfile和load data infile的組合來實現,這樣是不會對紀錄進行鎖定的。
在行級鎖定中,InnoDB 使用一個名爲next-key locking的算法。InnoDB以這樣一種方式執行行級鎖定:當它搜索或掃描表的索引之時,它對遇到的索引記錄設置共享或獨佔鎖定。所以,行級鎖定事實上是索引記錄鎖定。
InnoDB對索引記錄設置的鎖定也映像索引記錄以前的「間隙」。若是一個用戶對一個索引上的記錄R有共享或獨佔的鎖定,另外一個用戶 不能緊接在R以前以索引的順序插入一個新索引記錄。這個間隙的鎖定被執行來防止所謂的「幽靈問題」。
能夠用next-key鎖定在你的應用程序上實現一個惟一性檢查:若是你以共享模式讀數據,而且沒有看到你將要插入的行的重複,則你能夠安全地插入你的行,而且知道在讀過程當中對你的行的繼承者設置的next-key鎖定與此同時阻止任何人對你的行插入一個重複。所以,the next-key鎖定容許你鎖住在你的表中並不存在的一些東西。
REPEATABLE READ是InnoDB的默認隔離級別。帶惟一搜索條件使用惟一索引的SELECT ... FOR UPDATE, SELECT ... LOCK IN SHARE MODE, UPDATE 和DELETE語句只鎖定找到的索引記錄,而不鎖定記錄前的間隙。用其它搜索條件,這些操做採用next-key鎖定,用next-key鎖定或者間隙鎖定鎖住搜索的索引範圍,而且阻止其它用戶的新插入。
在持續讀中,有一個與READ COMMITTED隔離級別重要的差異:在這個級別,在同一事務內全部持續讀讀取由第一次讀所肯定的同一快照。這個慣例意味着若是你在同一事務內發出數個無格式SELECT語句,這些SELECT語句對相互之間也是持續的。
READ COMMITTED隔離級別是一個有些象Oracle的隔離級別。全部SELECT ... FOR UPDATE和SELECT ... LOCK IN SHARE MOD語句僅鎖定索引記錄,而不鎖定記錄前的間隙,於是容許隨意緊挨着已鎖定的記錄插入新記錄。UPDATE和DELETE語句使用一個帶惟一搜索條件的惟一的索引僅鎖定找到的索引記錄,而不包括記錄前的間隙。
在範圍類型UPDATE和DELETE語句,InnoDB必須對範圍覆蓋的間隙設置next-key鎖定或間隙鎖定以及其它用戶作的塊插入。這是很必要的,由於要讓MySQL複製和恢復起做用,「幽靈行」必須被阻止掉。
若是應用是從基於ORACLE的應用遷移到MYSQL數據庫的,那麼建議使用該隔離級別提供數據庫服務,由於該隔離級別是最接近ORACLE的默認隔離級別的,遷移可能遇到的鎖問題最小。
對Myisam類型的表:
1) Myisam類型的表能夠考慮經過改爲Innodb類型的表來減小鎖衝突。
2) 根據應用的狀況,嘗試橫向拆分紅多個表或者改爲Myisam分區對減小鎖衝突也會有必定的幫助。
對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) 以固定的順序訪問你的表和行。則事務造成良好定義的查詢而且沒有死鎖。