隔離級別 | 髒讀(Dirty Read) | 不可重複讀(NonRepeatable Read) | 幻讀(Phantom Read) |
---|---|---|---|
未提交讀(Read uncommitted) | 可能 | 可能 | 可能 |
已提交讀(Read committed) | 不可能 | 可能 | 可能 |
可重複讀(Repeatable read) | 不可能 | 不可能 | Innodb中不可能,後面解釋 |
可串行化(Serializable ) | 不可能 | 不可能 | 不可能 |
這二者有些類似。但不可重複讀重點在於update和delete,而幻讀的重點在於insert。若是使用鎖機制來實現這兩種隔離級別,在可重複讀中,該sql第一次讀取到數據後,就將這些數據加鎖,其它事務沒法修改這些數據,就能夠實現可重複讀了。但這種方法卻沒法鎖住insert的數據,因此當事務A先前讀取了數據,或者修改了所有數據,事務B仍是能夠insert數據提交,這時事務A就會發現莫名其妙多了一條以前沒有的數據,這就是幻讀,不能經過行鎖來避免。 剛纔提到鎖機制,那麼咱們最多見的就是悲觀鎖和樂觀鎖。mysql
爲了保證事務的隔離性,就須要一致性鎖定讀,就是每次操做數據時都去鎖住數據,無論哪一種操做(增、刪、改、查)都加鎖,以致於其餘事務操做這些數據sql
通常都是給表新增version字段,而後先經過查詢到該數據的version版本,以後數據修改時都將vsersion字段看成where條件去操做數據,而且將version字段修改爲version+1,若修改行數大於1表示修改爲功,反之則修改失敗。數據庫
CREATE TABLE `test1` ( `id` int(11) DEFAULT NULL, `name` varchar(255) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
BEGIN; -- 手動開啓一個事務,並在id = 1這條數據上加上排它鎖 SELECT * from test1 WHERE id = 1 for UPDATE;
BEGIN; -- 手動開啓另一個事務,此時給id=2的這條數據進行加排它鎖,結果會如何? SELECT * from test1 WHERE id = 2 for UPDATE
發現此時竟然查詢id=2的數據事務被卡住了。這是爲何呢?當表沒有建立索引時或者查詢語句沒有命中索引時,鎖住的是整個表的數據,由於沒有命中索引故其會去掃描全表數據。 當一張表沒有索引時,innoDB會建立一個隱藏主鍵索引,當經過隱藏的主鍵索引去檢索時,將該表中全部的隱藏索引檢索一遍 例子:若是手動開始事務,並在id=1的數據上手動加上排它鎖.若是此時再去查詢id=2的數據時,發現此語句卡住了。 故得出沒有創建索引的表,一旦鎖住數據及爲鎖住整張表。併發
CREATE TABLE `test2` ( `id` int(11) NOT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
BEGIN; -- 主鍵索引 SELECT * from test2 WHERE id = 1 FOR UPDATE;
BEGIN; -- 手動開啓其餘事務 SELECT * from test2 WHERE id = 5 for update;
此時說明,主鍵索引時只會鎖住匹配到的索引項,而不會影響其餘事務操做其餘索引測試
CREATE TABLE `test3` ( `id` int(11) NOT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `unique_name` (`name`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
BEGIN; -- 惟一索引 SELECT * from test3 where name = '李四' FOR update;
BEGIN; -- 惟一索引 SELECT * from test3 where id= 5 FOR update;
注意:此時SELECT * from test3 where id= 5 FOR update爲何執行卡住了?惟一索引鎖定時,先經過惟一索引而後找到對應主鍵索引,也就是輔助索引--->主鍵索引的一個過程,因此查詢id = 5的數據時也被鎖住了。 經過上面幾個例子發現mysql innodb是經過鎖住索引來實現行鎖的code
詳見下面InnoDB的行鎖,以下圖 臨界鎖的操做 blog
BEGIN; -- 臨界鎖,鎖住對應的範圍,防止幻讀。 -- 按道理此時應該鎖住,6,7,8(已存在),9,10 SELECT * from test2 WHERE id > 5 and id <11 FOR UPDATE;
BEGIN; -- 此時測試插入id=7 的值,按道理應該插入不進去,由於鎖住的範圍是(5,8]和(8,12] insert into test2 VALUES(7,'試試');
BEGIN; SELECT * from test2 WHERE id = 12 FOR UPDATE;
剛纔不是id>5 and id <11的麼?此時爲何id =12也被鎖了呢?由於此時鎖住的範圍是(5,8]和(8,12]排序
BEGIN; -- 間隙鎖,由於id =7 這條數據不存在,故鎖退化成了間隙鎖,那麼此時id=7 落在了(5,8)這個區間 SELECT * from test2 where id = 7 for UPDATE;
BEGIN; -- 由於鎖退化成了間隙鎖,那麼此時id=7 落在了(5,8)這個區間,故id =6 也一塊兒被鎖住了 INSERT into test2 VALUES(6,'卡卡');
注意: 測試在test2表中,也就是主鍵索引。索引
BEGIN; -- 在事務1中在id =5 的主鍵項鎖定 SELECT * from test2 where id = 5 for update;
在RR級別中,經過MVCC機制,雖然讓數據變得可重複讀(這就是上面爲何事務2也能讀取數據),但咱們讀到的數據多是歷史數據,是不及時的數據,不是數據庫當前的數據!這在一些對於數據的時效特別敏感的業務中,就極可能出問題。事務
對於這種讀取歷史數據的方式,咱們叫它快照讀 (snapshot read),而讀取數據庫當前版本數據的方式,叫當前讀 (current read)。很顯然,在MVCC中:
快照讀:就是select select * from table ....; 當前讀:特殊的讀操做,插入/更新/刪除操做,屬於當前讀,處理的都是當前的數據,須要加鎖。 select * from table where ? lock in share mode; select * from table where ? for update; insert; update ; delete; 事務的隔離級別實際上都是定義了當前讀的級別,MySQL爲了減小鎖處理(包括等待其它鎖)的時間,提高併發能力,引入了快照讀的概念,使得select不用加鎖。而update、insert這些「當前讀」,就須要另外的模塊來解決了
寫("當前讀") 事務的隔離級別中雖然只定義了讀數據的要求,實際上這也能夠說是寫數據的要求。上文的「讀」,實際是講的快照讀;而這裏說的「寫」就是當前讀了。 爲了解決當前讀中的幻讀問題,MySQL事務使用了Next-Key鎖。
二叉樹深度越高,I/O開銷越大。查詢速率較慢。 紅黑樹(特殊平衡二叉樹)即其子節點的高度差不能大於1,會發生自旋 B樹因爲是多路平衡查找樹,能夠將每一個磁塊的數據放入4KB的數據,充分利用了磁盤的空間,再加上B樹的高度下降,這樣就能快速訪問到數據 B+樹,左閉合B+樹,B+樹非葉子節點不保存數據相關信息,只保存關鍵字和子節點的引用,B+樹葉子節點是順序排序的。
myisam是索引和數據分開,innodb的數據是索引和數據一塊兒的,myisam沒有彙集索引,這裏也能夠看出誰查詢更快,誰寫入。 innodb由彙集索引。 innodb的行鎖經過臨界鎖(gap+recored)來實現機鎖。