主頁 > 後端開發 > MySQL(一):MySQL資料庫事務與鎖

MySQL(一):MySQL資料庫事務與鎖

2020-12-13 06:15:38 後端開發

基本概念

事務是指滿足ACID特性的的一組操作,可以通過Commit提交事務,也可以也可以通過Rollback進行回滾,會存在中間態和一致性狀態(也是真正在資料庫表中存在的狀態)

ACID

  • Atomicity【原子性】:事務被視為不可分割的最小單元,事務的所有操作要么全部提交成功,要么全部失敗回滾,回滾可以用回滾日志(undo Log)來實作,回滾日志記錄著事務所執行的修改操作,在回滾時反向執行這些修改操作即可

undoLog:為了滿足事務的原子性,在操作任何資料之前,首先將資料備份到Undo Log,然后進行資料的修改,如果出現了錯誤或者用戶執行了ROLLBACK陳述句,系統可以利用Undo Log中的備份將資料恢復到事務開始之前的狀態,與redo log不同的是,磁盤上不存在單獨的undo log檔案,它存放在資料庫內部的一個特殊段(segment)中,這稱為undo段(undo segment),undo段位于共享表空間內,Innodb為每行記錄都實作了三個隱藏欄位:6位元組的事務ID(DB_TRX_ID)7位元組的回滾指標(DB_ROLL_PTR)隱藏的ID

  • Consistency【一致性】:資料庫在事務執行前后都保持一致性狀態,在一致性狀態下,所有事務對同一個資料的讀取結果都是相同的
  • Isolation【隔離性】:一個事務所做的修改在最終提交前,對其他事務是不可見的
  • Durability【持久性】:一旦事務提交,則其所做的修改將會永遠保存到資料庫中,即使系統發生崩潰,事務執行的結果也不能丟失,系統發生崩潰可以用redoLog進行恢復,從而實作持久性,與undoLog記錄資料的邏輯修改不同,redoLog記錄的是資料頁的物理修改
  • 小結:
    1. 只有滿足一致性,事務的執行結果才是正確的,
    2. 在無并發的情況下,事務串行執行,隔離性一定能夠滿足,此時只要能夠滿足原子性,就一定能滿足一致性,
    3. 在并發情況下,多個事務并行執行,事務不僅要滿足原子性,還需要滿足隔離性,才能滿足一致性
    4. 事務滿足持久化是為了能夠應對系統崩潰的情況

AutoCommit

  • MySQL默認采用自動提交模式,也就是說,如果不顯示使用start transaction陳述句來開始一個事務,那么每個操作都會被當做一個事務并自動提交

事務隔離級別

  • 未提交讀【read uncommitted】:事務中的修改,即使沒有提交,對其他事務也是可見的
  • 提交讀【read committed】:一個事務只能讀取已經提交的事務所做的修改,換句話說,一個事務所做的修改在提交之前對其他事務是不可見的
  • 可重復讀【repeatable read】:保證在同一個事務中多次讀取同一資料的結果是一樣的
  • 可串行化【serializable】:強制事務串行執行,這樣多個事務互不干擾,不會出現并發一致性問題,該隔離級別需要加鎖實作,因為要使用加鎖機制保證同一時間只有一個事務執行,也就是保證事務串行執行

并發一致性問題

背景

在并發環境下,事務的隔離性很難保證,因此會出現很多并發一致性問題

主要場景

  • 丟失修改:丟失修改指一個事務更新操作被另外一個事務的更新操作替換,例如:T1 和 T2 兩個事務都對一個資料進行修改,T1 先修改并提交生效,T2 隨后修改,T2 的修改覆寫了 T1 的修改,
    業務場景:用戶修改地址有修改地址資訊和設定默認地址或者洗掉地址,這三個場景都是呼叫的同一個update陳述句,提供給用戶更新地址的介面需要支持用戶可設定默認地址,而不能將更新地址資訊和設定默認地址分開提供介面,如果分開提供,上層服務呼叫實際上是一下子呼叫兩個更新介面,這樣很容易會出現丟失修改的場景,
  • 讀臟資料:讀臟資料指在不同的事務下,當前事務可以讀取到另外事務未提交的資料,例如:T1 修改一個資料但未提交,T2 隨后讀取這個資料,如果 T1 撤銷了這次修改,那么 T2 讀取的資料是臟資料,
  • 不可重復讀:不可重復讀指在一個事務內多次讀取同一資料集合,在這一事務還未結束前,另一個事務也訪問了該同一資料集合并做了修改,由于第二個事務的修改,第一次事務的兩次讀取的資料可能不一致,例如:T2 讀取一個資料,T1 對該資料做了修改,如果 T2 再次讀取這個資料,此時讀取的結果和第一次讀取的結果不同,
  • 幻影讀:幻讀本質上也屬于不可重復讀的情況,T1讀取某個范圍的資料,T2在這個范圍內插入新的資料,T1再次讀取這個范圍的資料,此時讀取的結果和第一次讀取的結果不同
  • 小結
    產生并發不一致性問題的主要原因是破壞了事務的隔離性,解決方法是通過并發控制來保證隔離性,并發控制可以通過封鎖來實作,但是封鎖操作需要用戶自己控制,相當復雜,資料庫管理系統提供了事務的隔離級別,讓用戶以一種更輕松的方式處理并發一致性問題,

封鎖粒度:

  • 行級鎖:只封鎖需要修改的那部分資料或那行,不是封鎖所有資源,發生鎖爭用的可能小,系統并發程度高
  • 表級鎖:封鎖整個表,鎖定資料量太大,發生鎖爭用的概率大大加大,系統并發性能直線下滑

注意:加鎖就會消耗資源,鎖的各種操作【包括獲取鎖,釋放鎖,檢查鎖狀態都會增加系統開銷,因此封鎖的粒度越小,系統開銷越大】,在選擇封鎖粒度時,需要在鎖開銷和并發程度之間做一個權衡

封鎖型別

讀寫鎖

  • 互斥鎖,簡寫為X鎖,又稱寫鎖,
    一個事務對資料物件 A 加了 X 鎖,就可以對 A 進行讀取和更新,加鎖期間其它事務不能對 A 加任何鎖,
  • 共享鎖,簡寫為S鎖,又稱讀鎖,
    一個事務對資料物件 A 加了 S 鎖,可以對 A 進行讀取操作,但是不能進行更新操作,加鎖期間其它事務能對 A 加 S 鎖,但是不能加 X 鎖,

意向鎖

主要是表鎖,但是不會真的鎖

  • 在存在行級鎖和表級鎖的情況下,事務 T 想要對表 A 加 X 鎖,就需要先檢測是否有其它事務對表 A 或者表 A 中的任意一行加了鎖,那么就需要對表 A 的每一行都檢測一次,這是非常耗時的,
    意向鎖在原來的 X/S 鎖之上引入了 IX/IS,IX/IS 都是表鎖,用來表示一個事務想要在表中的某個資料行上加 X 鎖或 S 鎖,
    有以下兩個規定:
    一個事務在獲得某個資料行物件的 S 鎖之前,必須先獲得表的 IS 鎖或者更強的鎖;
    一個事務在獲得某個資料行物件的 X 鎖之前,必須先獲得表的 IX 鎖,

    通過引入意向鎖,事務 T 想要對表 A 加 X 鎖,只需要先檢測是否有其它事務對表 A 加了 X/IX/S/IS 鎖,如果加了就表示有其它事務正在使用這個表或者表中某一行的鎖,因此事務 T 加 X 鎖失敗,
  • 任意 IS/IX 鎖之間都是兼容的,因為它們只表示想要對表加鎖,而不是真正加鎖
    這里兼容關系針對的是表級鎖,而表級的 IX 鎖和行級的 X 鎖兼容,兩個事務可以對兩個資料行加 X 鎖,
    (事務 T1 想要對資料行 R1 加 X 鎖,事務 T2 想要對同一個表的資料行 R2 加 X 鎖,兩個事務都需要對該表加 IX 鎖,但是 IX 鎖是兼容的,并且 IX 鎖與行級的 X 鎖也是兼容的,因此兩個事務都能加鎖成功,對同一個表中的兩個資料行做修改,)

MySQL隱式與顯示鎖定

  • 隱式鎖定:MySQL 的 InnoDB 存盤引擎采用兩段鎖協議,會根據隔離級別在需要的時候自動加鎖,并且所有的鎖都是在同一時刻被釋放,這被稱為隱式鎖定

兩段鎖協議:加鎖和解鎖分為兩個階段進行,可串行化調度是指通過并發控制,使得并發執行的事務結果與某個串行執行的事務結果相同,串行執行的事務互不干擾,不會出現并發一致性問題

  • 或者使用特定陳述句進行顯示鎖定SELECT ... LOCK In SHARE MODE;(共享鎖)SELECT ... FOR UPDATE;(排他鎖)事務完成提交自動釋放鎖

MySQL三級封鎖協議

  • 一級封鎖協議:事務T要修改資料A時必須加X鎖,知道事務T結束才釋放鎖可以解決丟失修改問題,這時候不能同時有兩個事務對同一個資料進行修改,那么事務的修改就不會被覆寫
  • 二級封鎖協議:在一級基礎上,要求讀取資料A時必須加S鎖,讀取完馬上釋放S鎖可以解決讀臟資料問題,因為如果有一個事務在對資料A進行修改,根據1級封鎖協議,會加X鎖,那么就不能再加S鎖了,也就是不會讀入臟資料
  • 三級封鎖協議:在二級基礎上,要求讀取資料時必須加S鎖,直到事務結束了才能釋放S鎖可以解決不可重復讀的問題,因為讀A時,其他事務不能對A加X鎖,從而避免了在讀期間資料發生改變

InnoDB引擎的鎖實作

MVCC

  • 多版本并發控制是MySQL的innoDB存盤引擎實作隔離級別的一種具體方式,可用于實作提交讀和可重復讀這兩種隔離級別,而未提交讀隔離級別總是讀取最新的資料行,要求很低,無需使用MVCC
  • 在封鎖一節中提到,加鎖能解決多個事務同時執行時出現的并發一致性問題,在實際場景中讀操作往往多于寫操作,因此又引入了讀寫鎖來避免不必要的加鎖操作,例如讀和讀沒有互斥關系,讀寫鎖中讀和寫操作仍然是互斥的,而 MVCC 利用了多版本的思想,寫操作更新最新的版本快照,而讀操作去讀舊版本快照,沒有互斥關系,這一點和 CopyOnWrite 類似
  • 在 MVCC 中事務的修改操作(DELETE、INSERT、UPDATE)會為資料行新增一個版本快照,臟讀和不可重復讀最根本的原因是事務讀取到其它事務未提交的修改,在事務進行讀取操作時,為了解決臟讀和不可重復讀問題,MVCC 規定只能讀取已經提交的快照,當然一個事務可以讀取自身未提交的快照,這不算是臟讀
  • 系統版本號 SYS_ID:是一個遞增的數字,每開始一個新的事務,系統版本號就會自動遞增,
  • 事務版本號 TRX_ID :事務開始時的系統版本號,
  • MVCC的多版本指的是多個版本的快照,快照存盤在Undo日志中,該日志通過回滾指標ROLL_PTR把一個資料行的所有快照連接起來
  • INSERT、UPDATE、DELETE 操作會創建一個日志,并將事務版本號 TRX_ID 寫入,DELETE 可以看成是一個特殊的 UPDATE,還會額外將 DEL 欄位設定為 1

ReadView

  • MVCC維護了一個ReadView結構,主要包含了當前系統未提交的事務串列,還有該串列的最小值和最大值
  • 在進行 SELECT 操作時,根據資料行快照的 TRX_ID 與 TRX_ID_MIN 和 TRX_ID_MAX 之間的關系,從而判斷資料行快照是否可以使用,
    • TRX_ID < TRX_ID_MIN,表示該資料行快照時在當前所有未提交事務之前進行更改的,因此可以使用,
    • TRX_ID > TRX_ID_MAX,表示該資料行快照是在事務啟動之后被更改的,因此不可使用,
    • TRX_ID_MIN <= TRX_ID <= TRX_ID_MAX,需要根據隔離級別再進行判斷
      • 提交讀:如果 TRX_ID 在 TRX_IDs 串列中,表示該資料行快照對應的事務還未提交,則該快照不可使用,否則表示已經提交,可以使用,
      • 可重復讀:都不可以使用,因為如果可以使用的話,那么其它事務也可以讀到這個資料行快照并進行修改,那么當前事務再去讀這個資料行得到的值就會發生改變,也就是出現了不可重復讀問題,在資料行快照不可使用的情況下,需要沿著 Undo Log 的回滾指標 ROLL_PTR 找到下一個快照,再進行上面的判斷,

快照讀和安全讀

  • 快照讀:MVCC的select操作是快照中的資料,不需要進行加鎖操作
  • 當前讀:MVCC其它會對資料庫進行修改的操作就需要進行加鎖操作,從而讀取最新的資料,可以看到MVCC并不是完全不用加鎖,而只是避免了select的加鎖操作

如果需要select進行加鎖,就可以強制指定加鎖操作,如之前提到的共享鎖和排他鎖

Next-Key Locks

  • 概念:Next-Key Locks 是 MySQL 的 InnoDB 存盤引擎的一種鎖實作,MVCC 不能解決幻影讀問題,Next-Key Locks 就是為了解決這個問題而存在的,在可重復讀(REPEATABLE READ)隔離級別下,使用 MVCC + Next-Key Locks 可以解決幻讀問題,
  • Record Locks:鎖定一個記錄上的索引,而不是記錄本身,如果表沒有設定索引,InnoDB會自動在主鍵上創建隱藏的聚簇索引
  • Gap Locks:鎖定索引之間的間隙,但是不包含索引本身,例如當一個事務執行以下陳述句SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE;
  • Next-Key Locks:它是 Record Locks 和 Gap Locks 的結合,不僅鎖定一個記錄上的索引,也鎖定索引之間的間隙,它鎖定一個前開后閉區間,例如一個索引包含以下值:10, 11, 13, and 20,那么就需要鎖定以下區間:(-∞, 10](10, 11](11, 13](13, 20](20, +∞)

總結

上述理論較多,但是也是這些理論支撐整個研發程序,遇到多種業務場景時,需要根據資料庫的隔離級別判斷事務會不會出現死鎖,資料不一致等等理論性問題,MySQL最厲害的地方也是在RR【可重復讀】級別下避免了幻讀,即兼顧性能又保證資料安全,
在開發微服務或者分布使專案時,盡量將事務寫的簡單些,讓事務不會長時間鎖住對應的行,這樣也是保證了資料的一致性

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/233773.html

標籤:Java

上一篇:Java基礎 String

下一篇:maven資源匯出問題

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more