A事務的原子性(Atomicity):事務是一個不可分割的單元,事務中的全部操做,要麼全作完,要麼全不作html
C 事務的一致性(Consistency):一致性,即在事務開始以前和事務結束之後,數據庫的完整性約束沒有被破壞。好比用戶A給用戶B轉帳,用戶B給用戶A轉帳,而約束就是兩我的的總金額仍是同樣的mysql
I 事務的隔離性(Isolation):多個事務併發訪問時,事務之間是隔離的,一個事務不該該影響其它事務運行效果。在下面會擴展MySQL事務隔離級別。laravel
D 事務的持久性(Durability):意味着在事務完成之後,該事務所對數據庫所做的更改便持久的保存在數據庫之中,並不會被回滾。sql
髒讀: 對於兩個事務A,B,事務A讀取了已經被B更新但尚未提交的字段,以後,若B回滾,T1讀取到的內容就是臨時無效的內容。數據庫
不可重複讀: 在事務A中,屢次對同一數據進行讀取,此時事務B也對該數據進行訪問,也許事務B的修改,事務A屢次得到的數據可能不同。安全
幻讀:事務A讀取與搜索條件相匹配的若干行。事務B以插入或刪除行等方式來修改事務A的結果集,而後再提交。這裏幻讀與不可重複讀的差異在於不可重複讀強調的是修改和刪除,而幻讀強調的是插入網絡
MySQL中存在四種隔離等級:併發
隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
未提交讀(Read uncommitted) | 可能 | 可能 | 可能 |
已提交讀(Read committed) | 不可能 | 可能 | 可能 |
可重複讀(Repeatable read) | 不可能 | 不可能 | 可能 |
可串行讀(Serializable) | 不可能 | 不可能 | 不可能 |
這裏思考一下,爲啥阻止幻讀的隔離級別比不可重複讀的高,由於InnoDB是行級鎖,不可重複讀是針對於對於正在修改或刪除的數據行加鎖,但仍是能夠對錶進行插入,因此可能出現幻讀,要避免幻讀就要把表的讀寫都變成表級鎖,才能避免幻讀,也所以變成了隔離等級爲「可串行讀」。數據庫設計
PS:以上內容以 悲觀鎖 的概念能夠更好理解,不過實際中出於性能考慮,是用以樂觀鎖 爲理論基礎的MVCC(多版本併發控制,Multi-Version Concurrency Control )高併發
悲觀鎖,正如其名,具備強烈的獨佔和排他特性。它指的是對數據被外界(包括本系統當前的其餘事務,以及來自外部系統的事務處理)修改持保守態度,所以,在整個數據處理過程當中,將數據處於鎖定狀態。悲觀鎖的實現,每每依靠數據庫提供的鎖機制(也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,不然,即便在本系統中實現了加鎖機制,也沒法保證外部系統不會修改數據)。
即在事務A中數據在進行讀取(共享鎖)的時候,其餘事務不能進行修改(排他鎖),當在事務A的數據進行修改(排他鎖)的時候,不能進行讀取(共享鎖)。
悲觀鎖大多數狀況下依靠數據庫的鎖機制實現,以保證操做最大程度的獨佔性。但隨之而來的就是數據庫 性能的大量開銷,特別是對長事務而言,這樣的開銷每每沒法承受。
而樂觀鎖就很好的解決了這個問題,樂觀鎖能夠有兩種方式實現,一種是version,一種是時間戳,這裏以version爲例,假設數據通常狀況下不會發生衝突,只有在提交的時候,纔會進行加鎖,並判斷這提交的事務的版本與當前數據庫的版本的對比,若是提交的數據版本號大於數據庫表當前版本號,則予以更新,不然認爲是過時數據,則把請求駁回。
樂觀鎖機制避免了長事務中的數據庫加鎖開銷,大大提高了大併發量下的系統總體性能表現。
若是是在高併發下,不少用戶容易出現衝突,即請求容易駁回
MVCC的實現沒有固定的規範,每一個數據庫中都會有不一樣的實現,這裏討論InnoDB的MVCC,其內部原理是經過樂觀鎖,在InnoDB中,會在每行數據後面添加兩個額外的隱藏的值來實現MVCC,一個是這個數據什麼時候被建立,一個是這數據什麼時候過時。在實際中,這裏存儲的是版本號,每開啓一個新的事務,事務的版本號就回增長。在可重讀Repeatable reads事務隔離級別下:
經過MVCC,雖然每行記錄都須要額外的存儲空間,更多的行檢查工做以及一些額外的維護工做,但能夠減小鎖的使用,大多數讀操做都不用加鎖,讀數據操做很簡單,性能很好,而且也能保證只會讀取到符合標準的行,也只鎖住必要行。
咱們無論從數據庫方面的教課書中學到,仍是從網絡上看到,大都是上文中事務的四種隔離級別這一模塊列出的意思,RR級別是可重複讀的,但沒法解決幻讀,而只有在Serializable級別才能解決幻讀。因而我就加了一個事務C來展現效果。在事務C中添加了一條teacher_id=1的數據commit,RR級別中應該會有幻讀現象,事務A在查詢teacher_id=1的數據時會讀到事務C新加的數據。可是測試後發現,在MySQL中是不存在這種狀況的,在事務C提交後,事務A仍是不會讀到這條數據。可見在MySQL的RR級別中,是解決了幻讀的讀問題的。參見下圖
這裏又引伸出兩個概念:快照讀和當前讀
快照讀:就是普通的select
當前讀:特殊的讀操做(是要獲取鎖的),例如插入/更新/刪除就是當前讀
事務的隔離級別實際上都是定義了當前讀的級別,MySQL爲了減小鎖處理(包括等待其它鎖)的時間,提高併發能力,引入了快照讀的概念,使得select不用加鎖。事務的隔離只定義了讀數據的要求,而寫的要求固然是"當前讀"。
這裏注意,InnoDB默認的是行級鎖,行級鎖都是基於索引的。在當前讀的查詢語句中,若是一條SQL語句用不到索引是不會使用行級鎖的,會使用表級鎖把整張表鎖住,這點須要注意。
在MySQL中,insert、update、delete語句默認會對涉及的數據集加排他鎖,在Read committed等級下select語句默認是不會加的,若是要加的話,則須要顯示在後面加 lock in share mode。在Repeatable read以及在Serializable隔離機制下,select是加共享鎖的。
我是MySQL菜鳥,若是對於本文中有什麼疑問或者問題,歡迎互相討論提升
參考內容:
《深刻理解樂觀鎖與悲觀鎖》
《Innodb中的事務隔離級別和鎖的關係》
《慕課網-數據庫設計那些事》
《MySQL經常使用數據存儲引擎區別》