1、基本概念javascript
從操做的類型上來看,分爲讀鎖和寫鎖:java
讀鎖:共享鎖,對同一份數據,多個讀操做能夠同時進行且相互間不影響mysql
寫鎖:排它鎖,獨佔資源。在當前操做未完成以前,其餘寫操做必須等待。讀操做不影響。sql
排它鎖做用於innodb,且必須在事務塊中執行。在進行事務操做時,for update會對結果集中的每一行數據加排它鎖,其餘線程對於結果集中的數據進行修改操做,所有阻塞。session
從鎖數據的細粒度上來看,分爲行鎖和表鎖。併發
2、測試高併發
測試環境:mysql 5.5.六、Navicat for mysql。性能
新建表:測試
CREATE TABLE `tb_user` ( `id` int(10) NOT NULL AUTO_INCREMENT, `name` varchar(10) DEFAULT NULL, `password` varchar(10) DEFAULT NULL, `sex` char(1) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
開啓兩個查詢會話,模擬多請求。優化
一、表鎖
鎖的粒度偏大,開銷小,鎖錶快,可是發生鎖競爭的機率特別高,併發度低。
對於更新update、delete、insert自動加寫鎖。也可以下的顯式命令加鎖:
基本命令分析:
加鎖:LOCK TABLE tablename WRITE/READ;
釋放:UNLOCK TABLES;
查詢表是否加鎖:show open tables;
表鎖分析:SHOW STATUS LIKE 'table%';
結果返回兩個參數:Table_locks_immediate表示產生表級鎖定的次數,表示能夠當即獲取鎖的查詢次數,每當即獲取鎖值加1。
Table_locks_waited 出現表級鎖定爭用而發生等待的次數(不能當即獲取鎖的次數,每等待一次鎖值加1)。
1.一、讀鎖
不阻塞對加鎖表的讀操做,可是在當前會話中,不可對其餘表查詢。其餘會話的對加鎖表的更新,也會阻塞等待鎖釋放。
-- session01 加表級讀鎖 |
-- session02 |
-- 查詢鎖定的表 |
-- 查詢鎖定的表 |
-- 查詢其餘未鎖定表,報錯:[Err] 1100 - Table 'tbl_user_mycat' was not locked with LOCK TABLES |
-- 更新鎖定的表,會阻塞,直到鎖釋放後,再繼續完成執行操做 UPDATE tb_user SET `name`='heihei'; |
-- 釋放鎖 |
|
上述更新完成。 |
1.二、寫鎖
其餘會話中,不能對加鎖表進行讀寫操做。在釋放鎖以前,也不能對其餘未加鎖表進行讀寫操做。
-- session01 加表級寫鎖 |
-- session02 |
-- 查詢鎖定的表 -- 更新鎖定的表 |
-- 查詢其餘未鎖定表 |
-- 查詢其餘未鎖定表,報錯:[Err] 1100 - Table 'tbl_user_mycat' was not locked with LOCK TABLES |
-- 更新鎖定的表,會阻塞,直到鎖釋放後,再繼續完成執行操做 |
-- 釋放鎖 |
|
上述全部的對加鎖的表的讀寫操做,會執行完成 |
二、行鎖
鎖粒度較小(查詢結果集的記錄行),發生鎖競爭機率較低,併發度高。可是,可能會出現死鎖。一般,事務和行鎖是在確保數據準確的基礎上提升併發的處理能力。
基本命令分析:SHOW STATUS LIKE 'innodb_row_lock%';
Innodb_row_lock_current_waits:當前正在等待鎖定的數量
Innodb_row_lock_time:從系統啓動到如今鎖定總時間長度
Innodb_row_lock_time_avg:每次等待所花平均時間
Innodb_row_lock_time_max:從系統啓動到如今等待最長的一次所花時間
Innodb_row_lock_waits :系統啓動後到如今總共等待的次數
通常來講,關注Innodb_row_lock_waits(等待總次數)、Innodb_row_lock_time_avg(等待平均時長)比較高的時候,說明系統中競爭比較激烈,資源處理慢或者其餘什麼緣由,具體再查詢分析結果,制定相關的優化。
innodb在經過索引條件檢索數據的時候,會加行鎖。不然,都是加的表鎖。也就是說:innodb加行鎖是針對索引,而不是記錄行。沒有索引或者索引失效,都會升級爲表鎖。
對於update、delete和insert語句,innodb會自動的給結果集加排它鎖。select默認是不作任何操做的。固然,顯式的加鎖也是能夠滴:
共享鎖(讀鎖,多個讀鎖可同時進行,可是若事務中對讀鎖記錄作修改操做,頗有可能會發生死鎖):SELECT * from tb_user where id=10010 LOCK IN SHARE MODE;
排它鎖(寫鎖,在當前事務未commit以前,阻塞其餘的讀鎖和寫鎖):SELECT * from tb_user where id=10010 FOR UPDATE;
如下,2.1和2.2基於update來講明上述的自動加鎖機制。2.3和2.4舉例來講明顯式的共享鎖和排它鎖問題。
2.一、對於索引列的where,加行鎖:
-- SESSION01 開啓事務 |
|
-- SESSION02 開啓事務 |
|
COMMIT; | |
COMMIT; | |
2.二、對於不是索引列的where,加表鎖:
-- SESSION01 開啓事務 |
|
-- SESSION02 開啓事務 |
|
COMMIT; | |
-- session01提交commit,上述更新update完成操做 | |
COMMIT; |
2.三、共享鎖
-- SESSION01 開啓事務 |
|
-- SESSION02 開啓事務 |
|
-- 對讀鎖進行修改,等待session02提交commit,阻塞 UPDATE tb_user set name='testname1' WHERE id=10010; |
|
-- 報錯:[Err] 1213 - Deadlock found when trying to get lock; try restarting transaction UPDATE tb_user set name='testname1' WHERE id=10010; |
|
-- 操做繼續。session02中,mysql判斷出現死鎖,回滾session02後,session01繼續操做 |
2.四、排它鎖
-- SESSION01 開啓事務 |
|
-- SESSION02 開啓事務 |
|
-- 當前事務中可執行 |
-- 排它鎖,阻塞等待session01提交commit,釋放鎖 UPDATE tb_user set name='testname2' WHERE id=10010; |
COMMIT; |
|
-- session01提交commit,上述阻塞操做繼續執行完成 COMMIT; |
2.五、間隙鎖
當使用範圍查詢,且申請共享鎖或者排它鎖的時候,InnoDB會給符合條件的已有的結果集的索引加鎖。對於在範圍內可是不存在的的記錄值,叫作「間隙(GAP)」。InnoDB加鎖也會對這些間隙進行加鎖,成爲間隙鎖。
在鎖定一個很大的範圍以後,即便記錄不存在,也會被鎖定。這就致使,若是此時有其餘插入這些不存在的記錄的請求,會一直阻塞等待。很容易對性能產生影響。
3、使用和總結
一、Innodb默認使用的是行鎖。當未使用索引列查詢限定的時候會升級爲表鎖。
通常,在檢查分析鎖衝突的時候,必須explain查看sql的執行計劃。由於mysql在執行過程當中,並非必定會使用索引(explain查看執行計劃的時候,纔會有possible_key和key),有可能的狀況是mysql認爲全表掃描更快,那麼此時就不會用行鎖,而升級使用表鎖。
二、Innodb默認自動給更新操做加鎖