首先的話就是關于ACID,最重要的就是原子性了,這是基礎。
原子性是指事務包含的所有操作,要么全部完成,要么全部不完成。如果不能保證原子性,可能會出現以下問題:
數據不一致:事務中的部分操作可能對數據做出了更改,而其他操作由于某種原因(如系統故障、操作錯誤等)未能完成,導致數據狀態不一致。
資源泄露:如果一個操作分配了資源(如內存或文件描述符)而未能成功地釋放或回滾,可能會導致資源泄露。
系統可靠性下降:當多個事務相互依賴時,一個事務的部分完成可能導致其他事務無法繼續,影響系統的整體可靠性。
假如你在網上購物,支付環節需要從你的銀行賬戶扣款,并將相應金額增加到商家賬戶。如果扣款成功,但增款失敗,沒有原子性的保護,你的賬戶會減少相應的金額,而商家卻沒有收到錢,導致交易不公平。
MySQL怎么保證原子性的?
MySQL通過事務(Transaction)來保證原子性。事務是一個不可分割的工作單位,事務內的操作要么全部完成,要么全部不完成,這就是原子性。
在MySQL中,為了保證事務的原子性,它使用了以下兩種主要的技術:
Undo Log(撤銷日志): Undo Log記錄了事務執行前的舊數據信息,當事務執行過程中出現錯誤,或者用戶執行ROLLBACK語句進行事務回滾時,可以利用Undo Log中的信息將數據庫恢復到事務開始前的狀態
Redo Log(重做日志): 當MySQL異常宕機時,Redo Log可以用來恢復正在執行中的事務。它記錄了事務中所有的修改操作,用于在MySQL重啟后,重新執行這些操作以保證數據一致性。
這兩種日志機制的結合使用,即在出現錯誤回滾中使用Undo Log,和在系統崩潰時利用Redo Log進行恢復,使MySQL可以成功保證事務的原子性。
需要注意的是只有使用了InnoDB存儲引擎的MySQL支持事務,MyISAM存儲引擎并不支持事務。
如果無法保證隔離性會怎么樣?
如果數據庫無法保證事務的隔離性,將會出現以下并發問題:
臟讀(Dirty Reads):當一個事務可以讀取另一個事務未提交的數據時,若后者回滾,則前者讀取到的就是不正確的數據。
不可重復讀(Non-repeatable Reads):在一個事務的兩次查詢之間,另一個并發事務進行了更新操作,導致第一個事務兩次讀到的數據不一致。
幻讀(Phantom Reads):當一個事務重新讀取之前查詢過的數據范圍時,發現有其他事務新增的記錄,看似產生了"幻影"數據。
丟失更新(Lost Updates):兩個事務同時讀取相同的數據并更新它,可能會導致其中一個事務的更新被覆蓋。
例如,如果銀行系統無法保證隔離性,在處理多個客戶的轉賬操作時可能會出現資金計算錯誤。一個客戶的轉賬可能會基于另一個客戶正在處理但未完成的交易,最終導致資金總額的不一致。
MySQL怎么保證隔離性的?
MySQL保證隔離性主要是通過所謂的“隔離級別”實現的。隔離性是指并發執行的事務之間不相互影響,每個事務都感覺好像自己在獨占系統。
在MySQL中,支持四種隔離級別:
讀未提交 (READ UNCOMMITTED):在該級別下,一個事務可以讀取到另一個事務修改但還未提交的數據,也被稱為"臟讀"。這種隔離級別并發性最好,但是一般很少使用,因為它可能讀取到未提交的數據。
讀已提交 (READ COMMITTED):這是大多數數據庫系統的默認隔離級別(但不是MySQL默認的)。它滿足了隔離的基本要求,一個事務只能讀取已經提交的數據。
可重復讀(REPEATABLE READ):在同一事務內的查詢都能夠看到一致的快照數據。也就是說,在事務開始后,無論其他事務如何修改數據,本事務都能看到同樣的數據。MySQL默認的隔離級別就是REPEATABLE READ。在該級別下,除了防止"臟讀",還防止了"不可重復讀"。
串行化(SERIALIZABLE):這是最高的隔離級別,它要求所有事務都串行執行。也就是說,同一時間只能有一個事務在執行。雖然可以提供最高級別的隔離性,但是在并發環境中性能開銷也是最大的。
在InnoDB存儲引擎中,另一個叫做"多版本并發控制(MVCC)"的技術也對事務的隔離色提供支持。每次對數據的更新操作,InnoDB都會為該數據創建一個新的版本,而不是在原數據上直接修改,這樣就可以通過讀取舊版本的數據來避免阻塞讀操作,增強并發性能,從而保證隔離性。
如下圖所示為隔離級別與并發問題的關系:
如果無法保證持久性會怎么樣?
如果無法保證持久性,那么在事務提交后,如果發生系統崩潰或者其他故障,那么這個事務的修改結果可能會丟失,沒有被永久的保存在數據庫中。
持久性是指當事務被認為是已經結束時,對數據的改變就是永久性的。該特性保證了即使在系統崩潰或者電源故障后,已經提交的數據仍然可以保持,不會丟失。
比如:假設某電商網站使用MySQL數據庫來存儲用戶訂單信息。這個訂單首先被創建并存儲在數據庫的內存緩沖區中,等待最終被寫入到磁盤上的數據庫文件,在訂單信息被寫入磁盤之前,停電了。這時候當系統重啟后,訂單信息將丟失
MySQL是如何保證持久性的?
MySQL通過"寫前日志"(Write-Ahead-Logging,即WAL)策略,確保了事務的持久性。持久性是指一旦事務完成(即,commit),對數據的修改就是永久性的。即便發生系統崩潰,修改的數據也不會丟失。
具體實現如下:
Redo Log(重做日志): 這是MySQL中最核心的保證持久性的機制。當InnoDB數據庫引擎進行任何改變數據的操作時,首先會把這些操作寫入到內存中的Redo Log Buffer,然后再按照一定頻率(如每秒、事務提交時、buffer滿了等)將這些操作永久的記錄到磁盤的Redo Log中,而不是直接將修改操作寫到磁盤的數據文件。當數據庫異常重啟時,雖然數據文件沒有來得及持久化的更改可能丟失,但是redo log已經記錄了所有改變數據的操作,MySQL可以通過_redo_操作進行數據恢復,以保證數據的持久性。
Binlog(二進制日志): 這是MySQL服務器層的持久性保證機制,在每次事務提交的時候,會把事務的所有操作(包括SQL語句)寫入到Binlog中,同時寫入磁盤保證持久性。在數據主從同步或者數據恢復時,可以重放Binlog中記錄的所有操作。
這兩種機制,尤其是Redo Log,保證了MySQL的持久性,即使在數據庫崩潰后也能通過重播保證不丟失
?