數據庫系統 - 事務

事務

事務是關係型數據庫中的一個概念,爲了保證一系列操作的原子性。簡單來說,比如一個轉賬操作,需要將你的賬戶口減掉,然後給對方賬戶加上對應的值,如果這兩個操作不是原子的,就可能出現你的賬戶扣減了,但是對方賬戶沒有加上對應值或者你的賬戶並沒有扣減,但是對方的賬戶卻已經加上了對應值。

總的來說,事務是滿足 ACID 特性的一組操作,可以通過 commit 提交一個事務,也可以通過 rollback 進行回滾

ACID

ACID 是事務需要滿足的基本特性,分別是 原子性 ( Atomicity ),一致性 ( Consistency ),隔離性 ( Isolation ),持久性 ( Durability )

  • 原子性
    事務是一個最小的,不可再分割的最小單元,在一個事務中,要麼全部提交成功,要麼全部失敗回滾
  • 一致性
    數據庫在事務執行的前後都保持一致性的狀態。在一致性狀態下,所有的事務對一個數據庫的讀取結果都是相同的
  • 隔離性
    事務之間是相互隔離的,一個事務所做的修改在提交之前對其他事務是不可見的
  • 持久性
    事務一旦提交,則將其修改永遠保存到數據庫系統中。即使系統發生崩潰,事務的執行結果也不能丟失 (safe-crash)

在 Mysql 的 InnoDB 引擎中,默認採用事務自動提交模式,如果不顯示地使用 start transaction 語句來開始一個事務,那麼每個查詢都會被當做一個事務並自動提交

被併發打破的事務隔離性

在沒有併發的情況下,事務的隔離性是有保證的,但是,一旦併發產生,事務的隔離性就很難保證了,可能會產生併發一致性問題。假設同時存在兩個事務,T1 和 T2,針對以下問題模擬操作

  • 丟失修改
    T1 對某一行做更新操作,事務還未提交,T2 對同一行進行更新操作,當 T1 再次查詢時已被修改爲 T2 的結果,此時,T1 的修改被 T2 覆蓋,丟失修改
  • 髒讀
    T1 對某行進行更新操作,set var = 50+50,事務未提交,T2 對同一行進行讀取,select var = 100,此時 T1 進行回滾,T2 讀取到的是髒數據
  • 不可重複讀
    T1 和 T2 同時讀取一行數據 var = 50,T1 對數據進行修改 set var = 100,T2 再讀取數據時 var = 100,T2 兩次讀取的記錄不同,不可重複讀
  • 幻影讀
    T1 讀取某個範圍,假設 1 - 100 行數據,此時 T2 在 1 - 100 中插入一個行,T1 再次讀取這個範圍時與第一次讀取結果不同,產生了幻影讀

由於併發的產生破壞了事務的 隔離性,所以,我們需要對併發進行控制來保證事務的隔離性。一般來說併發控制需要通過 封鎖 來實現

Mysql 中的鎖

在 Mysql 中,根據鎖的範圍,大致可以分爲 全局鎖、表鎖、行鎖

全局鎖和表鎖

全局鎖

全局鎖,就是對整個數據庫實例加鎖,Mysql 提供了一個命令:flush tables with read lock (FTWRL) 讓整個庫處於只讀狀態。當加上全局鎖之後,其他線程的 增刪查改、建表、改表語句和更新事務的提交語句 都會被阻塞,需要等待全局鎖的釋放

當我們在做 整庫的邏輯備份 的時候,可能就會想到,使用 FTWRL 來保證數據庫不會有其他更新,讓整個庫處於只讀狀態。這樣可能會導致兩個問題:

  • 如果是在主庫上備份,那麼備份期間都不能進行更新,業務就不能正常運行
  • 如果是在備庫上做備份,那麼備份期間,不能執行從主庫同步過來的 binlog,會導致主從延遲

爲了解決這個問題,Mysql 官方自帶的邏輯備份工具,使用 --single-transaction 這個參數的時候,導入數據前會啓動一個事務,確保拿到的是一致性視圖。而由於 MVCC 的支持,這個過程的數據是可以正常更新的。但是在不支持事務的引擎中,比如 MyISAM 中就不能使用 --singel-transaction 參數了

需要注意的是,一般不採用 set global readonly = true 的方式來讓 Mysql 進入只讀狀態

  • readonly 關鍵字可能還會被用來做其他的邏輯,比如判斷是主庫還是備庫
  • readonly 在客戶端異常斷開之後,會一直保持 readonly 狀態,風險較高

表鎖

在 Mysql 中,表級鎖有兩種,一種是 表鎖 一種是 元數據鎖 (Meat Data Lock - MDL)。表鎖可以使用 lock tables t1 read/write 申明

MDL 鎖不需要顯示地申明,在訪問一個表的時候會自動加上。它在 Mysql 5.5 中引入,當對一個表做增刪查改操作的時候,加 MDL 讀鎖;當對錶結構做變更的時候加 MDL 寫鎖

  • 讀鎖之間不互斥,所以可以多個線程對同一張表做增刪查改操作
  • 讀鎖和寫鎖,寫鎖之間是互斥的,用來保證表結構操作的安全性

行鎖 是 InnoDB 中特別支持的一個鎖粒度,之中涉及很多的鎖規則和事務的隔離級別,單獨用一篇來總結

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章