數據庫部分,我開篇就進入事務部分的討論。由於我以爲這塊是數據庫比較核心重要的內容。sql
大學學過數據庫原理的,基本上都能脫口說出數據庫的四大特性:ACID。但是真正深刻理解了嗎?咱們再來從新看一下這四大特性:數據庫
- 原子性( Atomicity ):一個事務中是包含多個CRUD操做的,所以事務的第一個要求就是,這些操做應該所有成功或所有失敗,怎麼保證呢?很簡單,執行過程當中有異常,回滾操做便可。生活中常見的轉帳場景,A給B轉300塊錢,假設在同一個數據庫操做,第一條sql:A的帳戶減去300,第二條sql:B的帳戶增長300,兩個sql同時成功轉帳成功,同時失敗轉帳失敗;
- 一致性( Consistency ):對於一致性,我是這麼理解的:就是事務前和事務後,數據庫是從一個正確狀態變動到了另外一個正確狀態,不然事務回滾。怎麼解釋這句話呢?咱們還用上面轉帳的例子,若是A的帳戶裏只有280塊錢了,沒有任何限制的狀況下數據庫是能執行成功的,執行後的結果是A帳戶餘額-20,但是咱們都知道這在業務上是不容許的,也就是說若是這樣操做數據庫就從以前的正確狀態變動到了一個錯誤的狀態上來。爲保證一致性, A的帳戶餘額>=300的時候,咱們才能容許成功。保障一致性我目前想到兩個方法,一是數據庫保證,事務執行後,經過數據庫機制來檢測是否狀態都正確(觸發器),二是經過應用程序保證,這個就簡單了,應用層增長業務校驗便可。
- 隔離性( Isolation ):隔離性是說多個事務執行的時候是互不干擾的,本身執行本身的,誰也沒法看到其餘事務的數據。可是這裏就會引起好多問題了,若是Java裏多線程帶來的複雜性同樣,這裏後面會展開討論。
- 持久性( Durability ):一個事務完成後,執行結果是要持久化保存的。其實這裏就是要求必須進行數據庫的commit操做,數據庫的commit就是從回退段中永久性的寫入數據表空間的操做。
同時知足以上四個特性時,纔是一個完整的事務。再進一步思考一種這樣的狀況 ,大量的併發狀況下,有兩個這樣的事務,事務T1中是這樣的語句:多線程
1. select state from A where id=1; 併發
2. update B set status = '0' where id=1;性能
事務T2中是這樣的語句:
3. update A set state='1' where id=1; spa
4. select status from B where id=1;線程
已知當前 A表中id=1的 state='0',B表中id=1的status='1',若是兩個事務是併發執行的,那麼語句1的select查到的結果是什麼?語句4select查到的結果是什麼?答案是不知道,由於咱們不知道語句1先執行仍是語句3先執行,2和4同理。那麼有沒有辦法控制呢?有,那就是引入事務的隔離級別。這也是咱們大學學過的內容。先說說四個隔離級別吧:事務
- 讀未提交(read uncommited):事務能夠讀到其餘事務已修改未提交的數據。這種隔離級別下,若是語句在時間順序上執行的是 一、3,那麼1的結果就是state='0',若是順序是三、1,那麼1的結果就是state='1';若是讀到了事務T2未提交的數據,而T2又由於執行錯誤回滾,事務T1讀到的數據就是髒數據,這種狀況稱爲髒讀。
- 讀已提交(read commited):事務讀到的都是其餘事務已提交過的數據。這種隔離級別下,若是語句1執行時,T2未提交,則語句1讀到state='0',若是T2提交則讀到state='1',不會讀到未提交的數據了。可是,新的問題來了,若是T1中有又增長了語句5,語句5跟語句1同樣,那麼1和5的結果可能不同嗎?答案是有這種機率。若是 執行時間順序是這樣的,1 ,事務T2提交,5,這樣1和5讀到的數據就不一致了,同一個事務中同一條語句讀到了不一樣的數據?這種狀況就稱之爲不可重複讀。有辦法解決嗎?有!就是語句1讀取的時候,把讀到的數據加鎖,不容許再修改,直至事務提交或回滾再釋放鎖。
- 可重複讀(read repeatable):事務內部對數據的讀取先後是一致的。這種隔離級別解決了不可重複讀的狀況,實現方案前面也提到了,就是加鎖。可是還有一種狀況,你是鎖不住的,那就是T2中有insert怎麼辦?語句1的where 條件去掉,這樣語句1 就是查詢全部數據了,這時候把讀出來的數據都鎖住,別的事務不容許操做,但這時候T2給A表新增了一條數據,並在事務T1提交前提交的。事務T1覺得處理了全部的A表數據,但發現多出來一條其餘數據。這種狀況稱爲幻讀
- 串行讀( Serializable):徹底串行化讀,使用表級鎖,讀和寫都會阻塞。這種狀況下,數據庫的並不是能力受限、性能不高,但數據的正確性有保障。
總結下事務的隔離,咱們整理出下表:ci
隔離級別 |
髒讀(Dirty Read) |
不可重複讀(NonRepeatable Read) |
幻讀(Phantom Read) |
未提交讀(Read uncommitted) |
可能 |
可能 |
可能 |
已提交讀(Read committed) |
不可能 |
可能 |
可能 |
可重複讀(Repeatable read) |
不可能 |
不可能 |
可能 |
可串行化(Serializable ) |
不可能 |
不可能 |
不可能 |