原子性:由于操作失敗導緻的資料碎片錯誤;
一緻性:由于并發導緻的資料庫資料錯誤(與預期不一緻);
隔離性:由于并發導緻的目前使用資料(應用端)錯誤;
事務在當今的企業系統無處不在,即使在高并發環境下也可以提供資料的完整性。一個事務是一個隻包含所有讀/寫操作成功的集合。如下圖:
一個事務本質上有四個特點ACID:
- Atomicity原子性
- Consistency一緻性
- Isolation隔離性
- Durability耐久性
原子性
原子性任務是一個獨立的操作單元,是一種要麼全部是,要麼全部不是的原子機關性的操作。
一緻性
一個事務可以封裝狀态改變(除非它是一個隻讀的)。事務必須始終保持系統處于一緻的狀态,不管在任何給定的時間并發事務有多少。
一緻性有下面特點:
- 如果一個操作觸發輔助操作(級聯,觸發器),這些也必須成功,否則交易失敗。
- 如果系統是由多個節點組成,一緻性規定所有的變化必須傳播到所有節點(多主複制)。如果從站節點是異步更新,那麼我們打破一緻性規則,系統成為“最終一緻性”。
- 一個事務是資料狀态的切換,是以,如果事務是并發多個,系統也必須如同串行事務一樣操作。
在現實中,事務系統遭遇并發請求時,這種串行化是有成本的, Amdahl法則描述如下:它是描述序列串行執行和并發之間的關系。
“一個程式在并行計算情況下使用多個處理器所能提升的速度是由這個程式中串行執行部分的時間決定的。”
大多數資料庫管理系統選擇(預設情況下)是放寬一緻性,以達到更好的并發性。
隔離性
事務是并發控制機制,他們交錯使用時也能提供一緻性。隔離讓我們隐藏來自外部世界未送出的狀态變化,一個失敗的事務不應該破壞系統的狀态。隔離是通過用悲觀或樂觀鎖機制實作的。
耐久性
一個成功的事務将永久性地改變系統的狀态,是以在它結束之前,所有導緻狀态的變化都記錄在一個持久的事務日志中。如果我們的系統突然受到系統崩潰或斷電,那麼所有未完成已送出的事務可能會重演。
盡管一些資料庫系統提供多版本并發控制 MVCC, 他們的并發控制都是通過鎖完成,是以,鎖會增加執行的串行性,影響并發性。
SQL标準規定了四個隔離水準:
- READ_UNCOMMITTED
- READ_COMMITTED
- REPETABLE_READ
- SERIALIZABLE
隔離級别 | 髒讀 | 非重複讀 | Phantom read |
allowed | |||
prevented | |||
髒讀發生在:當一個事務允許讀取一個被其他事務改變但是未送出的狀态時,這是因為并沒有鎖阻止讀取,如上圖,你看到第二個事務讀取了一個并不一緻的值,不一緻的意思是,這個值是無效的,因為修改這個值的第一個事務已經復原,也就是說,第一個事務修改了這個值,但是未送出确認,卻被第二個事務讀取,第一個事務又放棄修改,悔棋了,而第二個事務就得到一個髒資料。
反複讀同一個資料卻得到不同的結果,這是因為在反複幾次讀取的過程中,資料被修改了,這就導緻我們使用了stale資料,這可以通過一個共享讀鎖來避免。這是隔離級别READ_COMMITTED會導緻可重複讀的原因。設定共享讀鎖也就是隔離級别提高到REPETABLE_READ。
Phantom 讀
當第二個事務插入一行記錄,而正好之前第一個事務查詢了應該包含這個新紀錄的資料,那麼這個查詢事務的結果裡肯定沒有包含這個剛剛新插入的資料,這時幻影讀發生了,通過變化鎖和predicate locking避免。
下圖是主流資料庫的預設隔離級别:
Database | Default isolation Level |
Oracle | |
MySQL | |
Microsoft SQL Server | |
PostgreSQL | |
DB2 | CURSOR STABILITY (a.k.a READ_COMMITTED) |
READ_COMMITED 是正确的選擇,因為SERIALIZABLE雖然能在不同僚務發生時避免stale資料,也就是避免上述丢失剛剛修改的資料,但是性能是最低的,因為是一種最大化的串行。
https://www.jdon.com/concurrent/acid-database.html
------------------越是喧嚣的世界,越需要甯靜的思考------------------
合抱之木,生于毫末;九層之台,起于壘土;千裡之行,始于足下。
積土成山,風雨興焉;積水成淵,蛟龍生焉;積善成德,而神明自得,聖心備焉。故不積跬步,無以至千裡;不積小流,無以成江海。骐骥一躍,不能十步;驽馬十駕,功在不舍。锲而舍之,朽木不折;锲而不舍,金石可镂。蚓無爪牙之利,筋骨之強,上食埃土,下飲黃泉,用心一也。蟹六跪而二螯,非蛇鳝之穴無可寄托者,用心躁也。