資料庫的交易(事務)概念,最簡單的說法就是一個交易的所有SQL
只能全部成功,或是全部失敗兩種狀況
不能有前一個SQL成功,後面兩個SQL失敗
所以資料庫在設計上必須要有ACID的特性
Atomicity(原子性)
一個交易必須被視為一個不可分割的工作單元
交易中的操作要麼全部成功,要麼全部失敗回滾(rollback)。
Consistency(一致性)
數據庫總是從一個一致性的狀態轉換到另外一個一致性的狀態
以銀行交易為例,A轉帳200元給B,A減少200,B增加200
這符合銀行存款的性質-錢是不會憑空增加或減少的
Isolation(隔離性)
一個交易尚未提交(commit)之前,代表未完成
對其他交易來說是看不到他提交尚未成功時所做的動作的
Durability(持久性)
一旦事務提交,則其所作的修改就會永久的保存到數據庫中
即使此時數據庫崩潰,修改的數據也不會消失。
MVCC與隔離層級
隔離層級是RDBMS針對資料庫的標準規範
要談到隔離層級以及MVCC前,我們需要先了解交易過程中如果沒有進行lock
可能會產生哪些問題
更新遺失(lost update)
AB前後對一筆row data做更新,A更新完畢了,但是B卻更新失敗而rollback
這樣row data就會變回A未更新的狀態,但是A更新是成功的
讀取錯誤(dirty read)
A對row data進行更新但尚未commit,這時候B去讀到了尚未commit的資料
這時候的資料可能會是錯的,因為A的commit不是一定成功的,有可能會rollback
無法重複的讀取(unrepeatable read)
A的交易兩次讀取row data,中間卻被B交易更動了row data的資料
那A交易兩次就可能產生不一致的結果,這對交易來說是不允許的
幻讀(phantom read)
狀況如同unrepeatable read,只是問題出在資料筆數會有問題
而不是資料的值產生問題
為了處理以上這幾種交易產生的狀況,最簡單的方式就是每一筆的交易要完全隔離或獨立
例如A交易在進行時,其他交易就排隊,等待A交易完成後再進行B交易
但是實務上這種使用lock table來讓交易強制排隊的方式會嚴重的拖慢資料庫的效能
試想原本每秒預計要處理2000筆交易資料,結果某一筆交易程式執行花了三秒鐘
後面1999筆的交易通通要等三秒,在實務上這完全是不合理的狀況
因此實務上會根據資料讀寫更新的頻繁性,設定不同的交易隔離層級(transaction isolation level)
嚴謹的程度是由低到高
未提交讀取(read uncommited)
交易進行時,尚未提交的資料,對其他的交易來說是可以看的到的
提交讀取(read commited)
交易過程中只能看到其他交易已經提交的資料
可重複讀(repeatable read)
交易在讀取時不會限制其他讀取的交易,但是禁止針對這些資料做更新
或是其他交易更新先寫到暫存表格
序列化執行(serializable)
直接鎖住表格(lock table),如同前面說的,嚴重影響效能,但是安全性最高
隔離級別 讀取錯誤 無法重複讀取 幻讀
read uncommited yes yes yes
read commited no yes yes
repeatable read no no yes
serializable no no no
MVCC中文稱多版本並發控制,因為除非使用序列化執行
要不然一定都會碰到幻讀的問題,MVCC是一種lock的變形
實現了非阻塞的讀,寫操作也只鎖定必要的行,來解決幻讀的問題
Mysql、Postgresql、Oracle各家實現MCVV的做法不一樣
另外Postgresql與Oracle使用的是read commited層級
而Mysql使用的是repeatable read,因此在效能上Mysql應該是比較差的
但是如果你的資料庫並不需要用的交易的功能(或是AP曾能夠自行處理,或允許小錯誤)
Mysql的MyISAM型態(預設)的資料表是關閉了交易功能(InnoDB才開啟)
關閉了交易的mysql應該是三種裡面效能最快的(不過現在mariadb 10應該更勝一籌了)
這方面就是資料庫管理人員與AP開發人員在定schema的時候要考量的了
只能全部成功,或是全部失敗兩種狀況
不能有前一個SQL成功,後面兩個SQL失敗
所以資料庫在設計上必須要有ACID的特性
Atomicity(原子性)
一個交易必須被視為一個不可分割的工作單元
交易中的操作要麼全部成功,要麼全部失敗回滾(rollback)。
Consistency(一致性)
數據庫總是從一個一致性的狀態轉換到另外一個一致性的狀態
以銀行交易為例,A轉帳200元給B,A減少200,B增加200
這符合銀行存款的性質-錢是不會憑空增加或減少的
Isolation(隔離性)
一個交易尚未提交(commit)之前,代表未完成
對其他交易來說是看不到他提交尚未成功時所做的動作的
Durability(持久性)
一旦事務提交,則其所作的修改就會永久的保存到數據庫中
即使此時數據庫崩潰,修改的數據也不會消失。
MVCC與隔離層級
隔離層級是RDBMS針對資料庫的標準規範
要談到隔離層級以及MVCC前,我們需要先了解交易過程中如果沒有進行lock
可能會產生哪些問題
更新遺失(lost update)
AB前後對一筆row data做更新,A更新完畢了,但是B卻更新失敗而rollback
這樣row data就會變回A未更新的狀態,但是A更新是成功的
讀取錯誤(dirty read)
A對row data進行更新但尚未commit,這時候B去讀到了尚未commit的資料
這時候的資料可能會是錯的,因為A的commit不是一定成功的,有可能會rollback
無法重複的讀取(unrepeatable read)
A的交易兩次讀取row data,中間卻被B交易更動了row data的資料
那A交易兩次就可能產生不一致的結果,這對交易來說是不允許的
幻讀(phantom read)
狀況如同unrepeatable read,只是問題出在資料筆數會有問題
而不是資料的值產生問題
為了處理以上這幾種交易產生的狀況,最簡單的方式就是每一筆的交易要完全隔離或獨立
例如A交易在進行時,其他交易就排隊,等待A交易完成後再進行B交易
但是實務上這種使用lock table來讓交易強制排隊的方式會嚴重的拖慢資料庫的效能
試想原本每秒預計要處理2000筆交易資料,結果某一筆交易程式執行花了三秒鐘
後面1999筆的交易通通要等三秒,在實務上這完全是不合理的狀況
因此實務上會根據資料讀寫更新的頻繁性,設定不同的交易隔離層級(transaction isolation level)
嚴謹的程度是由低到高
未提交讀取(read uncommited)
交易進行時,尚未提交的資料,對其他的交易來說是可以看的到的
提交讀取(read commited)
交易過程中只能看到其他交易已經提交的資料
可重複讀(repeatable read)
交易在讀取時不會限制其他讀取的交易,但是禁止針對這些資料做更新
或是其他交易更新先寫到暫存表格
序列化執行(serializable)
直接鎖住表格(lock table),如同前面說的,嚴重影響效能,但是安全性最高
隔離級別 讀取錯誤 無法重複讀取 幻讀
read uncommited yes yes yes
read commited no yes yes
repeatable read no no yes
serializable no no no
MVCC中文稱多版本並發控制,因為除非使用序列化執行
要不然一定都會碰到幻讀的問題,MVCC是一種lock的變形
實現了非阻塞的讀,寫操作也只鎖定必要的行,來解決幻讀的問題
Mysql、Postgresql、Oracle各家實現MCVV的做法不一樣
另外Postgresql與Oracle使用的是read commited層級
而Mysql使用的是repeatable read,因此在效能上Mysql應該是比較差的
但是如果你的資料庫並不需要用的交易的功能(或是AP曾能夠自行處理,或允許小錯誤)
Mysql的MyISAM型態(預設)的資料表是關閉了交易功能(InnoDB才開啟)
關閉了交易的mysql應該是三種裡面效能最快的(不過現在mariadb 10應該更勝一籌了)
這方面就是資料庫管理人員與AP開發人員在定schema的時候要考量的了