1. 程式人生 > >【資料庫】對資料庫事務的思考

【資料庫】對資料庫事務的思考

紙上得來終覺淺,絕知此事要躬行!

一 思考

  1. 事務是解決什麼問題的?
  2. 為了解決這些問題,事務使用了哪些策略或手段?
  3. 髒讀,幻讀,不可重複讀,概念理解

二 基礎知識

1. 設定事務等級

  • read uncommitted:小名等不及,總能看到最新資料,無論改動是否提交
  • read commited:小名老實人,目不斜視,只看提交後的更新
  • repeatable read:小名一根筋,它檢視過的資料,每次查詢結果不變,不管其它事務是否提交修改
  • serializable:小名等等等,別人改了,它就等改動提交後再看;它看了,別人必須等它提交了再改
select @@tx_isolation;
set
session transaction isolation level read uncommitted; set session transaction isolation level read committed; set session transaction isolation level repeatable read; set session transaction isolation level serializable;

##2. 資料操作

create database test;
use test;

create table person (
    name varchar
(30), age int ); insert into person values('xiaoerhei', 18); update person set age=age+1 where name='xiaoerhei'; select * from person;

3. 檢視資料庫狀態

show processlist;
show open tables from test;

三 併發場景梳理

事務級別

  • ru : read uncommitted
  • rc : read committed
  • rr : repeatable read
  • sz : serializable

事務組合:

在 2 個併發事務的情況下,共有 16 種不同併發事務的組合

    [
        (ru, ru), (ru, rc), (ru, rr), (ru, sz),
        (rc, ru), (rc, rc), (rc, rr), (rc, sz),
        (rr, ru), (rr, rc), (rr, rr), (rr, sz),
        (sz, ru), (sz, rc), (sz, rr), (sz, sz)
    ]

一般情況下,業務介面的事務級別都是一樣的,比如 spring 中進行事務管理時,相同的 service 介面,一般配置為相同的事務級別。

因此,[ (ru, ru), (rc, rc), (rr, rr), (sz, sz)] 這四種事務組合更常見。

事務流程說明:

  • 事務名稱: A,B
  • $ : start transaction
  • U : update/insert/delete
  • Q : select
  • R :rollback
  • # : commit

設計時,事務的內容要儘量小而美,而不是大而全。事務越大,併發時流程的可能性越多,問題越複雜。下面僅列舉一些典型的例子。

示例:

    $(A, B) -> U(A) -> Q(B) -> #(A, B)
    $(A, B) -> Q(A) -> U(B) -> #(A, B)
    $(A, B) -> U(A) -> U(B) -> #(A, B)


    $(A, B) -> Q(B) -> U(A) -> Q(B) -> #(A, B)
    $(A, B) -> Q(B) -> U(A) -> #(A) -> Q(B) -> #(B)
    $(A, B) -> Q(B) -> U(A) -> #(B) -> R(A) -> #(A)

    
    $(A, B) -> U(A) -> U(B) -> R(A) -> #(B)

    $(A) -> U(A) ->$(B) -> Q(B) -> #(A, B)
    $(A) -> Q(A) ->$(B) -> U(B) -> #(A, B)

備註: $(A, B) -> U(A) -> Q(B) -> #(A, B) 的意思是,事務同時開啟,事務 A 更新某資料 D,然後事務 B 查詢 D 的資訊,然後事務提交。

結果說明:

  • SD : sql done, 執行 sql 完成;
  • SB : sql block, 執行 sql 阻塞一定時間後完成;
  • SE : sql error, 執行 sql 阻塞超時異常;
  • DR : dirty read,髒讀
  • UR : unrepeatable read,不可重複讀
  • PR : phantom read,幻讀
  • OD : old data, 老資料

備註

sql 異常是否會引起事務終結並回滾,是可以配置的,SET XACT_ABORT = ON/OFF

四 示例分析

法無常法,因地制宜,要結合實際的應用場景去分析利弊,進而判斷是否滿足需求

scenario (ru, ru) (ru, rc) (ru, rr) (ru, sz)
$(A, B) -> U(A) -> Q(B) -> #(A, B)
$(A, B) -> Q(A) -> U(B) -> #(A, B)
$(A, B) -> U(A) -> U(B) -> #(A, B)
$(A, B) -> Q(B) -> U(A) -> Q(B) -> #(A, B)
$(A, B) -> Q(B) -> U(A) -> #(A) -> Q(B) -> #(B)
$(A, B) -> Q(B) -> U(A) -> #(B) -> R(A) -> #(A)
$(A, B) -> U(A) -> U(B) -> R(A) -> #(B)
(A)>U(A)>(A) -> U(A) ->(B) -> Q(B) -> #(A, B)
(A)>Q(A)>(A) -> Q(A) ->(B) -> U(B) -> #(A, B)

1 (ru, rc)

  1. $(A, B) -> U(A) -> Q(B) -> #(A, B)

    • B 讀取資料的實時性會受到影響。
    • 儘管 A 已經修改了資料,但是 B 要等到 A commit 之後才能看到更新。
  2. $(A, B) -> Q(B) -> U(A) -> #(A) -> Q(B) -> #(B)

    • A 更新並提交後,B 再次查詢被更新了的資料
    • B 兩次查詢的結果不一致
  3. $(A, B) -> U(A) -> U(B) -> #(A, B)

    U(A)

        insert into person values('sven', 30);
    

    U(B)

        update person set age=age+1;
    
    • 這種情況下,sven 的年齡是不會被加 1 的。
    • 如果 A,B 對同一資料進行更改時,B 會阻塞。

2 (ru, rr)

  1. $(A, B) -> Q(B) -> U(A) -> #(A) -> Q(B) -> #(B)

    • 儘管 A 已經提交了更新,B 兩次查詢的結果仍然一致

3 (ru, sz)

  1. $(A, B) -> U(A) -> Q(B) -> #(A, B)

    • 資料被 A 更新後,B 查詢該資料時會阻塞,直到 A 事務提交

(未完待續 …)