理解事務的ACID和隔離級別

2021-07-24 13:56:33 字數 2622 閱讀 1204

事務(transaction)是訪問並可能更新資料庫中各種資料項的乙個程式執行單元(unit),事務是乙個資料庫概念。但我理解資料庫也是一款軟體,只是遵循了資料庫sql標準,理解事務,先看看資料庫軟體的邏輯結構,這是mysql的邏輯結構:

可見mysql支援多客戶端訪問,對應到mysql裡面就是多執行緒(通常乙個客戶端對應mysql內部乙個處理執行緒),底層的儲存引擎就是負責向持久化介質(硬碟,儲存陣列)讀取和寫入資料,顯然這些資料是被各個內部執行緒共享的,就需要關注執行緒安全問題,舉個栗子,有一張資料表car,裡面2個字段,分別是cars_beijing和cars_shanghai,表示在北京的汽車數量以及和上海的汽車數量,現在乙個執行緒要幹的事是完成把乙個汽車從北京搬到上海,那麼,很明顯就是cars_beijing–,cars_shanghai++,用2條sql語句表示:

update car set cars_beijing=cars_beijing-1 where id = 1;

update car set cars_shanghai=cars_shanghai-1 where id = 1;

現在用這個例子來理解acid,

1. 原子性(atomicity),就是說這個事務要麼不執行,要麼全部執行,就是上面2條語句,不允許只執行一條而不執行第二天語句的情況發生。

2. 一致性(consistency),就是說資料庫從乙個一致性狀態轉移到另乙個一致性狀態,怎麼理解呢,原子性是表示不允許只執行一條而不執行第二條的情況發生,那麼一致性就是說要麼第一條第二條都執行成功(所謂執行成功就是對資料庫持久化資料產生了影響),要麼就第一條第二條都執行失敗(都不對資料庫持久化資料產生影響),不允許一條成功,一條失敗的情況。

3. 隔離性(isolation)理解隔離性,就是隔離另乙個執行緒(事務)的操作,比如執行緒a正在執行這個事務cars_beijing–,cars_shanghai++,執行緒b則正在查詢cars_beijing和cars_shanghai的值,隔離性就要保證執行緒b只能查詢到事務完全沒有執行或者完全成功執行的值,不允許執行緒b查詢到只執行了cars_beijing–而沒有執行cars_shanghai++的值

4. 永續性(durability),這個比較好理解,就是事務一旦提交,所修改的資料就被持久化,即使掉電也不會丟失。

前面說了隔離性是防止其他執行緒讀到事務的中間值,但實際上這會分成情況,因為有很多種不同的中間值,也就對應了sql標準中的4個隔離級別。

理解隔離級別之前,先搞清楚兩個問題。

第乙個是要實現事務的acid性,其實和多執行緒程式設計中對公共資源的執行緒安全性原理差不多,怎麼來保證這個acid哩,當然就是對各執行緒的公共資源加鎖了,執行緒要訪問公共資源,就需要先獲取公共資源的鎖,這裡的公共資源就是資料庫裡面的資料,我們知道關係型資料庫中的資料是按表-行來分的,這裡我是可以選擇是對行加鎖還是對錶加鎖,或者乾脆對整個資料庫加鎖的問題。這就是鎖的級別。

第二個是多個執行緒對公共資料的操作其實也可以分成很多種情況,比如執行緒a在執行cars_beijing–,cars_shanghai++這個事務的時候,事務b可能正在讀取相應欄位的cars_beijing值,也可能多次讀取讀取相應欄位的cars_beijing值,還有可能在統計car表的有多少條記錄或者增加記錄等等。

知道了鎖和其他執行緒對公共資料表的操作組合,我們再來理解sql標準中的4個隔離級別,就會很容易了

1. 未提交讀(read uncommitted),就是不做隔離控制,可以讀到「髒資料」,比如上面的cars_beijing–,cars_shanghai++,這個隔離級別允許其他執行緒讀取到只做了cars_beijing–而沒有做cars_shanghai++時候的值。顯然這個隔離級別沒有太大意義,現實中沒有人會用,除非這個應用只有讀取,沒有任何寫入。

2. 提交讀(read committed),提交讀就是不允許讀取事務沒有提交的資料,簡單的說,就是上面的cars_beijing–,cars_shanghai++,不允許讀取到只做了cars_beijing–,而沒有做cars_shanghai++的記錄。這個隔離級別是大多數資料庫(除了mysql)的預設隔離級別。

3. 可重複讀(repeatable read),什麼是不可重複讀,就是事務a去做cars_beijing–,cars_shanghai++之前,事務b啟動了,先讀取了一次事務a要修改的值,這個時候事務a修改了記錄,但是事務b在事務a修改完後又讀取了同一記錄值,顯然,這導致事務b相同的讀取操作卻讀取了不同值,這就是不可重複讀。可重複讀就是禁止這種情況發生,比如對需要修改的資料加排他鎖,事務b需要讀取這個記錄, 那麼整個事務b沒有完成之前,都允許事務a啟動。可重複讀的隔離級別是mysql預設的隔離級別。

4. 可序列化(serialzable),就是多個執行緒(事務)完全不併發,序列執行,當然不會有任何隔離問題,顯而易見效率也最低,一般不採用

還有乙個問題,就是有什麼問題可序列化能解決而可重複讀不能解決?那就是幻讀問題,什麼叫幻讀,例如第乙個事務對乙個表中的資料進行了修改,比如這種修改涉及到表中的「全部資料行」。同時,第二個事務也修改這個表中的資料,這種修改是向表中插入「一行新資料」。那麼,以後就會發生操作第乙個事務的使用者發現表中還有沒有修改的資料行,就好象發生了幻覺一樣。可重複讀由於只鎖定需要修改的記錄,如果乙個事務的任務是增加乙個記錄,這個事務是不會被修改事務阻塞的,所有可重複讀的隔離級別是不能解決幻讀問題的

理解事務的4種隔離級別

資料庫事務的隔離級別有4種,由低到高分別為read uncommitted read committed repeatable read serializable 而且,在事務的併發操作中可能會出現髒讀,不可重複讀,幻讀。下面通過事例一一闡述它們的概念與聯絡。read uncommitted 讀未提...

理解事務的4種隔離級別

資料庫事務的隔離級別有4種,由低到高分別為read uncommitted read committed repeatable read serializable 而且,在事務的併發操作中可能會出現髒讀,不可重複讀,幻讀。下面通過事例一一闡述它們的概念與聯絡。read uncommitted 讀未提...

理解事務的4種隔離級別

資料庫事務的隔離級別有4種,由低到高分別為read uncommitted read committed repeatable read serializable 而且,在事務的併發操作中可能會出現髒讀,不可重複讀,幻讀。下面通過事例一一闡述它們的概念與聯絡。read uncommitted 讀未提...