每次鎖定的是一行數據的鎖機制就是行級別鎖定(row-level)。行級鎖定不是MySQL本身實現的鎖定方式,而是由其餘存儲引擎本身所實現的mysql
優勢sql
缺點併發
使用行級鎖定的主要有InnoDB存儲引擎,以及MySQL的分佈式存儲引擎NDBCluster分佈式
InnoDB的行級鎖定一樣分爲兩種類型:共享鎖和排他鎖,而在鎖定機制的實現過程當中爲了讓行級鎖定和表級鎖定共存,InnoDB也一樣使用了意向鎖(表級鎖定)的概念,也就有了意向共享鎖和意向排他鎖這兩種。性能
意向鎖的做用就是當一個事務在須要獲取資源鎖定的時候,若是遇到本身須要的資源已經被排他鎖佔用的時候,該事務能夠須要鎖定行的表上面添加一個合適的意向鎖。若是本身須要一個共享鎖,那麼就在表上面添加一個意向共享鎖。而若是本身須要的是某行(或者某些行)上面添加一個排他鎖的話,則先在表上面添加一個意向排他鎖。優化
意向共享鎖能夠同時並存多個,可是意向排他鎖同時只能有一個存在。因此,能夠說InnoDB的鎖定模式實際上能夠分爲四種:共享鎖(S),排他鎖(X),意向共享鎖(IS)和意向排他鎖(IX)spa
鎖模式的兼容性:.net
InnoDB行鎖是經過給索引上的索引項加鎖來實現的。因此,只有經過索引條件檢索數據,InnoDB才使用行級鎖,不然,InnoDB將使用表鎖。其餘注意事項:線程
隱式加鎖:設計
顯示加鎖:
用SELECT … IN SHARE MODE得到共享鎖,主要用在須要數據依存關係時來確認某行記錄是否存在,並確保沒有人對這個記錄進行UPDATE或者DELETE操做。
可是若是當前事務也須要對該記錄進行更新操做,則頗有可能形成死鎖,對於鎖定行記錄後須要進行更新操做的應用,應該使用SELECT… FOR UPDATE方式得到排他鎖。
InnoDB如何加表鎖:
在用 LOCK TABLES對InnoDB表加鎖時要注意,要將AUTOCOMMIT設爲0,不然MySQL不會給表加鎖;事務結束前,不要用UNLOCK TABLES釋放表鎖,由於UNLOCK TABLES會隱含地提交事務;COMMIT或ROLLBACK並不能釋放用LOCK TABLES加的表級鎖,必須用UNLOCK TABLES釋放表鎖。
SET AUTOCOMMIT=0; LOCK TABLES t1 WRITE, t2 READ, ...; [do something with tables t1 and t2 here]; COMMIT; UNLOCK TABLES;
既然都用表鎖了,怎麼不選擇MyISAM引擎呢!
nnodb的鎖定規則是經過在指向數據記錄的第一個索引鍵以前和最後一個索引鍵以後的空域空間上標記鎖定信息而實現的。 Innodb的這種鎖定實現方式被稱爲「 NEXT-KEY locking」 (間隙鎖),由於Query執行過程當中經過範圍查找的話,它會鎖定整個範圍內全部的索引鍵值,即便這個鍵值並不存在。
例:假如emp表中只有101條記錄,其empid的值分別是 1,2,…,100,101,下面的SQL:
mysql> select * from emp where empid > 100 for update;
是一個範圍條件的檢索,InnoDB不只會對符合條件的empid值爲101的記錄加鎖,也會對empid大於101(這些記錄並不存在)的「間隙」加鎖。
間隙鎖有一個比較致命的弱點,就是當鎖定一個範圍鍵值以後,即便某些不存在的鍵值也會被無辜的鎖定,而形成在鎖定的時候沒法插入鎖定鍵值範圍內的任何數據。在某些場景下這可能會對性能形成很大的危害
* 當Query沒法利用索引的時候, Innodb會放棄使用行級別鎖定而改用表級別的鎖定,形成併發性能的下降; * 當Quuery使用的索引並不包含全部過濾條件的時候,數據檢索使用到的索引鍵所指向的數據可能有部分並不屬於該Query的結果集的行列,可是也會被鎖定,由於間隙鎖鎖定的是一個範圍,而不是具體的索引鍵; * 當Query在使用索引定位數據的時候,若是使用的索引鍵同樣但訪問的數據行不一樣的時候(索引只是過濾條件的一部分),同樣會被鎖定
防止幻讀,以知足相關隔離級別的要求。
爲了數據恢復和複製的須要。
在實際應用開發中,尤爲是併發插入比較多的應用,咱們要儘可能優化業務邏輯,儘可能使用相等條件來訪問更新數據,避免使用範圍條件。
InnoDB除了經過範圍條件加鎖時使用間隙鎖外,若是使用相等條件請求給一個不存在的記錄加鎖,InnoDB也會使用間隙鎖。
執行SQL:mysql> show status like 'InnoDB_row_lock%';
mysql> show status like 'InnoDB_row_lock%'; +-------------------------------+-------+ | Variable_name | Value | +-------------------------------+-------+ | InnoDB_row_lock_current_waits | 0 | | InnoDB_row_lock_time | 0 | | InnoDB_row_lock_time_avg | 0 | | InnoDB_row_lock_time_max | 0 | | InnoDB_row_lock_waits | 0 | +-------------------------------+-------+
若是發現鎖爭用比較嚴重,還能夠經過設置InnoDB Monitors 來進一步觀察發生鎖衝突的表、數據行等,並分析鎖爭用的緣由。如:
設置監視器:mysql> create table InnoDB_monitor(a INT) engine=InnoDB;
查看:mysql> show engine InnoDB status;
中止查看:mysql> drop table InnoDB_monitor;
如何發現死鎖: 在InnoDB的事務管理和鎖定機制中,有專門檢測死鎖的機制,會在系統中產生死鎖以後的很短期內就檢測到該死鎖的存在
回滾較小的那個事務
在REPEATABLE-READ隔離級別下,若是兩個線程同時對相同條件記錄用SELECT…FOR UPDATE加排他鎖,在沒有符合該條件記錄狀況下,兩個線程都會加鎖成功。程序發現記錄尚不存在,就試圖插入一條新記錄,若是兩個線程都這麼作,就會出現死鎖。這種狀況下,將隔離級別改爲READ COMMITTED,就可避免問題。
判斷事務大小:事務各自插入、更新或者刪除的數據量
當產生死鎖的場景中涉及到不止InnoDB存儲引擎的時候,InnoDB是沒辦法檢測到該死鎖的,這時候就只能經過鎖定超時限制參數InnoDB_lock_wait_timeout來解決。
InnoDB存儲引擎因爲實現了行級鎖定,雖然在鎖定機制的實現方面所帶來的性能損耗可能比表級鎖定會要更高一些,可是在總體併發處理能力方面要遠遠優於MyISAM的表級鎖定的。當系統併發量較高的時候,InnoDB的總體性能和MyISAM相比就會有比較明顯的優點了。可是,InnoDB的行級鎖定一樣也有其脆弱的一面,當咱們使用不當的時候,可能會讓InnoDB的總體性能表現不只不能比MyISAM高,甚至可能會更差。
(1)要想合理利用InnoDB的行級鎖定,作到揚長避短,咱們必須作好如下工做:
(2)因爲InnoDB的行級鎖定和事務性,因此確定會產生死鎖,下面是一些比較經常使用的減小死鎖產生機率的小建議: