1. 程式人生 > >事務特性&安全問題&隔離級別

事務特性&安全問題&隔離級別

事務特性 (ACID)

  • 原子性(Atomicity):事務中的邏輯要全部執行,不可分割。否則回滾。
  • 一致性(consistency):事務執行前後,資料的完整性保持不變。

    舉個栗子:張三有1000元,李四有1000元。張三給李四轉500元,轉完之後,張三還剩500元,李四有1500元。轉賬前後,張三和李四的錢的總和都是2000元。不會發生變化。

  • 隔離性(isolation):一個事務在執行的過程中不應該受到其他事務的影響。
  • 永續性(durability):事務執行完成後,資料都應持久化下來。

其中的隔離性又可以分為不同的隔離級別。用於應對不同層次讀的安全問題(當然也有寫的安全問題,但是這裡不展開)。

安全問題

問題出現的場景:張三有1000元,李四有1000元。張三給李四轉500元,這裡面其實是兩步,首先第一步張三扣500元,接著第二步李四加500元。但是如果我們在這兩步之間進行一次查詢,會查到張三有500元,李四有1000元,這裡就會出問題。這個問題就是髒讀問題

  • 髒讀:一個事務讀到了另一個事務尚未提交的資料。

    解決髒讀問題,我們需要提高隔離級別,很容易想到:不讓一個事務讀到另一個事務尚未提交的資料即可,那很自然的想到一個隔離級別就是“讀已提交”。這個隔離級別其實就是字面意思:只能讀其他事務已提交的資料,自然能夠應付髒讀的問題。


再考慮一個場景:張三有1000元,李四有1000元。張三給李四轉500元,前面說了這裡面其實是兩步,如果我們開啟一個事務去查詢張三和李四的賬戶,在第一步之前,我們查詢張三和李四的賬戶分別為1000元、1000元。在第二步之後,也就是提交之後,我們再查詢張三和李四的賬戶現在為500元、1500元。
這就引出了下面的這個問題:在同一個事務中兩次查詢同一個資料的結果卻不同。也就是不可重複讀,因為讀出來的結果不同嘛。

  • 不可重複讀:一個事務讀到了另一個事務提交的資料,導致多次查詢結果不一致。

    其實這不一定算問題,因為在一個事務中,另一個事務對資料庫的更新實時的能夠查詢出來算問題嗎?很多資料庫的預設級別是不應對這個問題的,比如oracle sqlserver的預設隔離級別都只是“讀已提交”,只能應付髒讀問題。並不能應對不可重複讀問題。只有mysql的預設隔離級別是可以應付不可重複讀問題的。那麼到底不可重複讀的真正缺點或者問題在哪呢?或者說如果不能滿足可重複讀,有啥實際危害的例子?(從v2ex摘幾個貼在下面)

    1.我覺得把因為不知道自己讀到的資料是不是正確的。他要是一開始拿 1000 去做計算,算完心想咱們讀個 A 看看,讀了一看呀,A 怎麼變成 900 了?難道我前面做的計算都白做了麼!
    2.事務 A:select 最近 1 個月註冊的新使用者,把他們的使用者組改成 GroupA
    事務 B:註冊了一個新使用者,commit
    事務 A:select 最近 1 個月註冊的新使用者,給他們傳送一條私信:恭喜你升級為 GroupA !
    不一致本身沒有危害。有危害的是不知道會不一致。
    3.mysql 預設 rr 主要是為了主從同步時,採用邏輯 sql 同步時的一致性,因為主庫的 sql 是併發執行的,會有兩個事務一起再跑,從庫同步是單執行緒的,不會有兩個事務同時在跑,如果不是 rr,出現樓主的栗子說的情況時,主從資料就不一致了

    那麼如何解決不可重複讀問題呢? mysql使用了MVCC–一種多版本併發控制機制。簡單來說,MVCC是通過儲存資料在某個時間點的快照來實現的,簡單來說,直覺上,在一個事務中,先將資料儲存為一個快照,在再次讀取資料時,讀取這個快照,也就是舊值。自然能保證在一個事務中前後讀取同一個資料的一致性。
    還有比較正常的方法是通過鎖來實現,當一個事務訪問某些資料時,會對這個表進行加表級鎖,如此以來,其他想要更改這個表中的資料的事務,必須等我們的事務完成之後才能更改。


但是上述也說了,應對不可重複讀問題通過加表級鎖的方式是可以避免其他事務在一個事務查詢資料時對這些資料進行更改的,但是它能防止我們往那個表中插入一條資料嗎?答案是不能。因此這就引出了一個新的問題,幻讀。所謂幻讀,簡單來說就是第二次查的資料比第一次查的資料多一條,並且這一條是奇奇怪怪的。
對於mysql來說,MVCC同樣可以應對幻讀的問題,還是因為他讀的是老資料!是快照!並不會因為你插入某條資料他就會幻讀,因為他根本讀不到。

  • 幻讀:一個事務讀到了另一個事務已提交的插入的資料,導致多次查詢結果不一致。

    怎麼解決呢?mysql不用管,因為他有MVCC。對用鎖來實現的,只需將鎖更新為行級鎖即可。也就是Serializable。但是Serializable的效率比較低


隔離級別

  • 讀未提交:能夠讀未提交的資料,隔離級別最低
  • 讀已提交:只能讀已提交的資料
  • 可重複讀:能重複讀取資料,而資料不會改變
  • Serializable:可序列化。隔離級別最高