換了工做以後,接近半年沒有發博客了(一直加班),emmmm.....今天好不容易有時間,記錄下工做中遇到的一些問題,接下來應該重拾知識點了。由於新公司工做中MySQL庫常常出現查詢慢,鎖等待,節點掛掉........等一系列問題。致使每一個程序員頭都很大,一味抱怨「爲何我就查一條數據這麼卡」,"我TM加了索引的啊,怎麼還怎麼慢"...........我想默默說的是,大部分MySQL出現鎖等待,查詢奇慢的狀況基本都是由於SQL寫的很差(有坑),或者數據表設計的不完善。對,不用想!這些全部的坑很大一部分都是本身形成的。那麼是什麼緣由形成的,大部分只是抱怨,而不去關注MySQL的一些細節問題,好比:MySQL行鎖的細節,什麼狀況下會使用表鎖等。因此今天先討論記錄下InnoDB特有的行鎖的一些細節,增強認識。html
InnoDB不一樣於MyISAM最大的兩個特色就是:一是支持事務,二是支持行鎖;毋庸置疑,由於這兩個特性大部分都採用InnoDB引擎,其中的支持行鎖就是InnoDB適合多併發優點所在,可是行鎖的一些細節沒有深刻理解過的話,可能會形成必定的誤解,形成「看似命中索引,走行鎖,結果倒是表鎖,最終致使鎖等待狀況」。程序員
經過給索引上的索引項加鎖來實現的,也就意味着:只有經過索引條件檢索數據,InnoDB才使用行級鎖,不然,InnoDB將使用表鎖。這一點在實際應用中特別須要注意,否則的話可能致使大量的鎖衝突,從而影響引起併發性能。數據庫
1)準備工做:建tab_no_index表,表中無任何索引,並插入數據併發
2)Session_1: 咱們給id=1的行加上排它鎖(for update),因爲id沒有索引,其實是表級鎖;post
3)Session_2:咱們給id=2的行加上排它鎖(for update),因爲id沒有索引,因此去申請表級鎖,可是卻出現了鎖等待!原因就是在沒有索引的狀況下,InnoDB只能使用表鎖。性能
備註:MySQL中的for update 僅適用於InnoDB(由於是隻有此引擎纔有行級鎖),而且必須開啓事務,在begin與commit之間才生效。for update是在數據庫中上鎖用的,能夠爲數據庫中的行上一個排它鎖。當一個事務的操做未完成時候,其餘事務能夠對這行讀取可是不能寫入或更新,只能等該事務Rollback, Commit, Lost connection…優化
1)準備工做:對id建索引以下url
2)Session_1:此時id是有索引的,咱們對id=1 and name=1的一行加排它鎖;spa
3)Session_2:訪問不一樣於Session_1的id=1, name=5行,可是索引鍵值是同樣的,照樣等待鎖,鎖衝突了。設計
1)準備工做:對tab_no_index追加name索引:alter table tab_no_index add index name(name);
2)Session_1:開啓事務對id=1的行加排它鎖,即對name=1與name=5兩個數據加鎖。
3)Session_2:開啓事務對name=2行加鎖,由於該數據沒有被加鎖,索引能夠得到鎖
4)Session_3:再對name=5的數據進行加鎖,因爲該數據記錄已被Session_1鎖定,因此等待得到鎖。
如咱們對實驗三對name='2'進行加鎖,誤覺得name是int類型,原本name是有索引的,可是最後結果致使表鎖:
具體請看MySQL的索引狀況。具體能夠參考以前個人一篇博文MySQL優化(1)--------經常使用的優化步驟、MySQL優化(2)--------經常使用優化
當用範圍條件而不是相等條件檢索數據,並請求共享或者排它鎖的時候,InnoDB會給符合條件的已有數據記錄的索引項加鎖;對於不在範圍內的但並不存在的記錄,叫作「間隙(GAP)」,InnoDB也會對這個間隙加鎖,這就是所謂的間隙鎖。
如:select * from where id>100 for update 對id大於100的數據對加鎖,可是此時數據中id只有1,2….100,101,不只對存在的101的記錄加鎖,還會對大於101不存在的數據的間隙加鎖。
此外,對使用相等條件請求給一個不存在的記錄加鎖,InnoDB也會使用間隙鎖,以下:
Session_1:對不存在的id=6的記錄加鎖
Session_2:插入id=6的記錄,也會出現鎖等待
對於InnoDB表,在絕大部分狀況下都應該使用行鎖,由於事務和行鎖每每是咱們之因此選擇InnoDB表的理由,但在個別狀況下也使用表級鎖;
1)事務須要更新大部分或所有數據,表又比較大,若是使用默認的行鎖,不只這個事務執行效率低,並且可能形成其餘事務長時間等待和鎖衝突;
2)事務涉及多個表,比較複雜,極可能引發死鎖,形成大量事務回滾。
使用表鎖須要注意幾點:
1)使用LOCK TABLES雖然能夠給InnoDB加表級鎖,表級鎖不是InnoDB存儲引擎層管理的,而是由其上一層MySQL Server負責的
2)在用LOCK TABLES對InnoDB表加鎖時須要注意,要將Autocommit設置爲0,不然MySQL不會給表加鎖;事務結束前,不要用UNLOCK TABLES釋放表鎖,由於UNLOCK_TABLES隱含提交事務;COMMIT或ROLLBACK並不能釋放用LOCK TABLES加表級鎖。
SET AUTOCOMMIT=0;
LOCK TABLES table1 WRITE, table2 READ,...;
[do something....]
COMMIT;
UNLOCK TABLES;