InnoDB與MyISAM的最大不一樣有兩點:html
一是支持事務(TRANSACTION);二是採用了行級鎖。mysql
MyISAM不支持事務、行鎖和外鍵,訪問速度快(表級鎖,可同時讀,不可寫),多使用於多讀寫少或對事務完整性沒有要求的狀況;算法
MEMORY:將全部的數據保存在RAM中,對錶的大小有限制;sql
MERGE:用於將一系列等同的MyISAM表以邏輯方式組合在一塊兒,並做爲一個對象引用它們;數據庫
事務是由一組SQL語句組成的邏輯處理單元,事務具備如下4個屬性,一般簡稱爲事務的ACID屬性。session
4種隔離級別比較數據結構
隔離級別併發 |
讀數據一致性post |
髒讀優化 |
不可重複讀 |
幻讀 |
未提交讀(Read uncommitted) |
最低級別,只能保證不讀取物理上損壞的數據 |
是 |
是 |
是 |
已提交度(Read committed) |
語句級 |
否 |
是 |
是 |
可重複讀(Repeatable read) |
事務級 |
否 |
否 |
是 |
可序列化(Serializable) |
最高級別,事務級 |
否 |
否 |
否 |
數據庫的事務隔離越嚴格,併發反作用越小,但付出的代價也就越大,由於事務隔離實質上就是使事務在必定程度上 「串行化」進行,這顯然與「併發」是矛盾的。同時,不一樣的應用對讀一致性和事務隔離程度的要求也是不一樣的,好比許多應用對「不可重複讀」和「幻讀」並不敏感,可能更關心數據併發訪問的能力。
1、PROPAGATION_REQUIRES_NEW:建立新事務,不管當前存不存在事務,都建立新事務。
2、PROPAGATION_REQUIRED:支持當前事務,若是當前存在事務,就加入該事務,若是當前沒有事務,就建立一個新事務。
3、PROPAGATION_SUPPORTS:支持當前事務,若是當前存在事務,就加入該事務,若是當前不存在事務,就以非事務執行。‘
4、PROPAGATION_MANDATORY:支持當前事務,若是當前存在事務,就加入該事務,若是當前不存在事務,就拋出異常。
5、PROPAGATION_NOT_SUPPORTED:以非事務方式執行操做,若是當前存在事務,就把當前事務掛起。
6、PROPAGATION_NEVER:以非事務方式執行,若是當前存在事務,則拋出異常。
7、PROPAGATION_NESTED:若是當前存在事務,則在嵌套事務內執行。若是當前沒有事務,則執行與PROPAGATION_REQUIRED相似的操做。
InnoDB存儲引擎既支持行級鎖,也支持表級鎖,默認狀況下采用行級鎖。
InnoDB的鎖類型有:
另外,爲了容許行鎖和表鎖共存,實現多粒度鎖機制,InnoDB還有兩種內部使用的意向鎖(Intention Locks),這兩種意向鎖都是表鎖。
InnoDB行鎖模式兼容性列表
鎖 |
X |
IX |
S |
IS |
X |
衝突 |
衝突 |
衝突 |
衝突 |
IX |
衝突 |
兼容 |
衝突 |
兼容 |
S |
衝突 |
衝突 |
兼容 |
兼容 |
IS |
衝突 |
兼容 |
兼容 |
兼容 |
意向鎖是InnoDB自動加的,不需用戶干預。對於UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及數據集加排他鎖(X);對於普通SELECT語句,InnoDB不會加任何鎖;
若將上鎖的對象當作一棵樹,那麼對最下層的對象上鎖,也就是對最細粒度的對象進行上鎖,那麼首先須要對粗粒度的對象上鎖。以下圖,當咱們要對記錄加上排他鎖時,那麼咱們先要對數據庫A、表、頁都加上意向排他鎖,最後再對記錄加上排他鎖,若其中任何一部分致使等待,那麼該操做須要等待粗粒度鎖的完成。
InnoDB存儲引擎有3種行鎖的算法設計,分別是:
Record Lock老是會去鎖住索引記錄。若是InnoDB存儲引擎表創建的時候沒有設置任何一個索引,這時InnoDB存儲引擎會使用隱式的主鍵來進行鎖定。
InnoDB中對於行的查詢都是採用Next-Key Lock鎖定算法。對於不一樣SQL查詢語句,可能設置共享的(Share) Next-Key Lock和排他的(exlusive) Next-Key Lock,其主要目的也是爲了解決幻行問題。
但當查詢的索引含有所有惟一屬性(主鍵或惟一索引)時InnoDB存儲引擎會對Next-Key Lock進行優化,將其降級爲Record Lock,即僅鎖住索引自己,而不是範圍,從而提升應用的併發性。
可是若是查詢的條件中包含的是輔助索引,則狀況會徹底不一樣,會產生以下鎖狀況:
若是查詢條件沒有索引,則全表掃描,且每條記錄之間加上了gap鎖,爲了防止幻讀。
下面是一個示例:
咱們知道InnoDB默認的事務隔離級別爲REPEATABLE READ模式,而REPEATABLE READ模式下,Next-Key Lock算法又是默認的行記錄鎖定算法,因此就能夠避免幻讀的現象。
InnoDB行鎖是經過給索引上的索引項加鎖來實現的,這一點MySQL與Oracle不一樣,後者是經過在數據塊中對相應數據行加鎖來實現的。
InnoDB這種行鎖實現特色意味着:只有經過索引條件檢索數據,InnoDB才使用行級鎖,不然,InnoDB將使用表鎖!
1)在不經過索引條件查詢的時候,InnoDB確實使用的是表鎖,而不是行鎖。
InnoDB存儲引擎的表在不使用索引時使用表鎖例子
session_1 |
session_2 |
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select * from tab_no_index where id = 1 ; +------+------+ | id | name | +------+------+ | 1 | 1 | +------+------+ 1 row in set (0.00 sec) |
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select * from tab_no_index where id = 2 ; +------+------+ | id | name | +------+------+ | 2 | 2 | +------+------+ 1 row in set (0.00 sec) |
mysql> select * from tab_no_index where id = 1 for update; +------+------+ | id | name | +------+------+ | 1 | 1 | +------+------+ 1 row in set (0.00 sec) |
|
mysql> select * from tab_no_index where id = 2 for update; 等待 |
session_1只給一行加了排他鎖,但session_2在請求其餘行的排他鎖時,卻出現了鎖等待!緣由就是在沒有索引的狀況下,InnoDB只能使用表鎖。當咱們給其增長一個索引後,InnoDB就只鎖定了符合條件的行。
InnoDB存儲引擎的表在使用索引時使用行鎖例子
session_1 |
session_2 |
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select * from tab_with_index where id = 1 ; +------+------+ | id | name | +------+------+ | 1 | 1 | +------+------+ 1 row in set (0.00 sec) |
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select * from tab_with_index where id = 2 ; +------+------+ | id | name | +------+------+ | 2 | 2 | +------+------+ 1 row in set (0.00 sec) |
mysql> select * from tab_with_index where id = 1 for update; +------+------+ | id | name | +------+------+ | 1 | 1 | +------+------+ 1 row in set (0.00 sec) |
|
mysql> select * from tab_with_index where id = 2 for update; +------+------+ | id | name | +------+------+ | 2 | 2 | +------+------+ 1 row in set (0.00 sec) |
2)因爲MySQL的行鎖是針對索引加的鎖,不是針對記錄加的鎖,因此雖然是訪問不一樣行的記錄,可是若是是使用相同的索引鍵,是會出現鎖衝突的。
InnoDB存儲引擎使用相同索引鍵的阻塞例子
session_1 |
session_2 |
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) |
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) |
mysql> select * from tab_with_index where id = 1 and name = '1' for update; +------+------+ | id | name | +------+------+ | 1 | 1 | +------+------+ 1 row in set (0.00 sec) |
|
雖然session_2訪問的是和session_1不一樣的記錄,可是由於使用了相同的索引,因此須要等待鎖: mysql> select * from tab_with_index where id = 1 and name = '4' for update; 等待 |
3)當表有多個索引的時候,不一樣的事務可使用不一樣的索引鎖定不一樣的行,另外,不管是使用主鍵索引、惟一索引或普通索引,InnoDB都會使用行鎖來對數據加鎖。
InnoDB存儲引擎的表使用不一樣索引的阻塞例子
session_1 |
session_2 |
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) |
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) |
mysql> select * from tab_with_index where id = 1 for update; +------+------+ | id | name | +------+------+ | 1 | 1 | | 1 | 4 | +------+------+ 2 rows in set (0.00 sec) |
|
Session_2使用name的索引訪問記錄,由於記錄沒有被索引,因此能夠得到鎖: mysql> select * from tab_with_index where name = '2' for update; +------+------+ | id | name | +------+------+ | 2 | 2 | +------+------+ 1 row in set (0.00 sec) |
|
因爲訪問的記錄已經被session_1鎖定,因此等待得到鎖。: mysql> select * from tab_with_index where name = '4' for update; |
4) 即使在條件中使用了索引字段,可是否使用索引來檢索數據是由MySQL經過判斷不一樣執行計劃的代價來決定的,若是MySQL認爲全表掃描效率更高,好比對一些很小的表,它就不會使用索引,這種狀況下InnoDB將使用表鎖,而不是行鎖。所以,在分析鎖衝突時,別忘了檢查SQL的執行計劃,以確認是否真正使用了索引。