iT邦幫忙

2023 iThome 鐵人賽

DAY 11
0

前情提要

前面我們分別介紹了 sxlock、mvcc、wal 機制,
但可別忘了這一章節主要介紹的是 ACID,
我們將回顧前面並且將每個 ACID 的實作與知識點個別點出來,
並強調注意重點與達成方式。

在了解原理前的 ACID

先來介紹什麼是 ACID 的基本介紹。
ACID 為四個單字的字首,分別代表

  • Atomicity 原子性: 每次的操作都是全部成功,或是全部失敗滾回,沒有其他選項。
  • Consistency 一致性: 每一筆資料都遵守著目標表的制定的規則。
  • Isolation 隔離性: 防止多個事務並發執行時由於交叉執行而導致數據的不一致。
  • Durability 永久性: 事務處理結束後,對數據的修改就是永久的,即便系統故障也不會丟失。

大部分的情況下,大多都是直接記下來,
但不知道為什麼需要這個,也不清楚對於使用者來說有什麼樣的影響,
那麼應該要反向思考,如果少了 ACID,會有什麼樣的影響。

Remark: BigO(1)的資料操作,不代表是 Atomic,單純代表該操作中沒有 loop。

ACID 目的重點

在沒有任何目的下介紹理論,一切都是死記硬背,有了目標才是努力的方向。
將會介紹如果沒有 ACID 會如何?ACID 的目標與重點。

Remark: ACID 指的的操作單位為一個 tx,並非每一句 sql commands

Atomicity 原子性

目標重點: 失敗滾回

Atomic 的操作單位為一個 tx,目的就是想要整筆 tx 都能全部成功或滾回,
全部成功基本上中間沒發生任何問題的話,就照著順序做,然後存進硬碟之中,並最重要的關注點。
而全部失敗滾回才是,中間已經執行了一堆 sql commands,
該如何取消回滾到執行之前的狀態,就如同沒發生一樣是值得探討的。

如果沒有 Atomic:

如果沒有 atomic 的回滾機制,
就代表著如果操作到一半想要反悔或者發生錯誤,想要 rollback 回去上一個版本,
那麼就必須自行恢復原狀,在恢復原狀之前,
中間可能會被其他 tx 做一些操作,抓到錯誤的資料或更改成不正確的數值,造成非常大的困擾。

Atomic 達成方式:

要達到 commit 時全部成功,rollback 全部失敗,而且中間所以 sql 的操作都必須是有效的。主要有兩派(mvcc,sxlock)。

postgres(mvcc)

在 postgres 中,因 postgres 為真正 mvcc 的資料庫,
所以每次的 write 操作都會真的加入一筆紀錄,如 update 會新增新的 record 後隱藏舊的 record。
而在這其中的所有 records 在 clog 之中為 TRANSACTION_STATUS_IN_PROGRESS來表示尚未被 Commit,其他 tx 也無法讀取的到。
要是 rollback 了,clog 的 TRANSACTION_STATUS_IN_PROGRESS 改為 TRANSACTION_STATUS_ABORTED,讓其他 tx 無法讀到。
而 rollback 中間殘留的那些標記,則需要使用 vacuum 來做清除。

mysql(sxlock)

在 mysql 中,因 mysql 為 sxlock 的資料庫,所以每次的 write 操作都是會覆蓋原始的資料的,如 update 會鎖上該筆 record,然後修改。
而在其中 mysql 會將那些 sql commands 寫進 redo log 之中,
同時將那些 sql commands 的反操作寫入 undo log 之中(如 redo log: insert,undo log: delete)。
要是 rollback 了,那些 Records 上的鎖尚未釋放,所以不會讓其他 tx 修改到,至少確保資料安全。
然後就讀取 undo log,依序的反操作,直到還原到原狀為止,然後釋放鎖。

Consistency 一致性

目標重點: 每一筆資料都合規

Consistency 的目的很簡單,就是在存資料進 table 之前,每一筆資料都符合 table 的規定。
不符合規定的就拒絕操作,讓整個 tx rollback。

如果沒有 Consistency:

Consistency 是一個在做 write 操作之前的驗證機制,
如果沒有,db 就如同寫檔案一樣自由的讀寫,當要做計算或 query 操作時,
反而資料轉換工作交給使用者處理,反而寫出一堆例外狀況,
不如就在新增資料時,就先將規則把關好。

Consistency 達成方式

當收到 sql commands 時,dbms 會先將 sql 作 parser 來解析 sql 語句,
然後經過約束檢查,通過這個檢查後,才會將資料寫進 wal 等後續的工作。
來讓每一筆 records 都符合相同的 table 約束。

Isolation 隔離性

目標重點: 透過設定 isolation level 來限制資料存取

因 db 用交錯操作(interleaved)增加 throughput,
而想要讓交錯操作(interleaved)的設計之下達到與同循序操作(Serialized)相同的結果,
就必須想辦法 讓 tx 之間所使用到的 Records 像是隔離般互不影響(並非 tx 彼此)。
但許多情境下不需要像是同循序操作一樣,而且 throughput 也大幅下降,
所以大部分 db 製造出 4 個 isolation level,來做出一些通融。

如果沒有 Isolation:

如果沒有 Isolation level(並非沒有 lock),
那麼每次的操作都將所有使用到的資料都上互斥鎖,
直接採用最高層級的 isolation level。

如果連 lock 都沒有,那麼將會發生一堆讀取問題,
Dirty readnon repeatable readPhantom read 等怪怪的事情發生。

Isolation 達成方式

要製作 Isolation level。主要有兩派(mvcc,sxlock)。

postgres(mvcc)

透過樂觀鎖(Optimistic Lock)再加上 clog,來達到 isolation level。

mysql(sxlock)

透過共享鎖與互斥鎖的概念來鎖定 Records,依照不同 isolation level 來上不同的鎖,達到 isolation level。

Durability 永久性

目標重點: db回覆commit完成後就算寫入硬碟途中發生crash都還是救的回來

Durability 目的就是因為 memory 的寫入與 disk 的寫入有極大的時間差,而且週邊設備的 I/O 具有極大的不確定性,
需要避免意外的發生,最好的避免就是就算發生也可以應付的來,就算寫入 Disk 時發生任何意外,都可以恢復到上一個步驟,
然後將已經 commit 的 sql 都能順利的重新寫成功。

如果沒有 Durability

如果沒了 Durability,那麼只要在寫入 disk 途中發生任何意外,整個 db 就不能用了,
一方面是無法復原到上位 crash 前的狀態,另一方面就算復原了上次寫到一半的剩餘的 sql 也遺失了。

Durability 達成方式

主要是使用預寫日誌 Write Ahead Log (Wal)機制,將所有 sql 操作都先寫入 wal log 之中,然後再修改 memory 中的資料,
最後再一次性的寫入 Disk 中,寫完後在 wal log 中加入 check point,告知下次寫入 disk 時可以從這裡之後開始寫。
至於為什麼就只是加個 wal log,就能達到永久性(Durability)的效果呢?
假設在寫 disk 時,途中發生 crash,memory 也被清空了,但因為每次的 sql 都會先寫進 wal log 之中,再去修改 memory,
那麼就只需要由最後一個 check point 那裡開始恢復,其餘的直接進 disk 拿即可。還沒進 disk 的 sql 也可以透過 wal 抓取資料寫進 disk。
wal log 其實也是放在 disk 中,不過就如同草稿般簡易的寫入,不像寫進 table 中有如此多的規定。

結語

有了上面的概念,自然就可以理解資料庫系統為了增加 Transaction 的 throughput,
所以制訂了一些規則(如 ACID),讓後續的人可以照著這些守則實做出理想的料庫系統,
讓一般的麻瓜也能輕易的達到高效且安全的 CRUD 操作。


上一篇
[ACID] Mysql WAL機制
下一篇
[ACID] 其實也不用ACID
系列文
CRUD仔的一生(上集)32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言