最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入講解MySQL事務(wù)的ACID特性、隔離級(jí)別、實(shí)現(xiàn)原理及鎖機(jī)制

 更新時(shí)間:2026年01月02日 10:12:00   作者:張彥峰ZYF  
本文深入講解MySQL事務(wù)的ACID特性、隔離級(jí)別、實(shí)現(xiàn)原理及鎖機(jī)制,探討事務(wù)的原子性、一致性、隔離性和持久性,分析不同隔離級(jí)別的優(yōu)劣,詳解redolog、undolog、MVCC及行鎖算法

在現(xiàn)代數(shù)據(jù)庫(kù)管理系統(tǒng)中,事務(wù)是保證數(shù)據(jù)一致性和可靠性的核心機(jī)制之一。尤其在多用戶并發(fā)操作的環(huán)境下,如何確保每個(gè)操作都能按預(yù)期執(zhí)行,避免數(shù)據(jù)損壞或錯(cuò)誤,成為了數(shù)據(jù)庫(kù)設(shè)計(jì)和應(yīng)用中的重要挑戰(zhàn)。MySQL作為廣泛使用的關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng),其事務(wù)機(jī)制的實(shí)現(xiàn)深刻影響著數(shù)據(jù)處理的效率和安全性。

本篇文章將深入探討MySQL事務(wù)的基本概念、實(shí)現(xiàn)原理以及其在高并發(fā)場(chǎng)景中的應(yīng)用。我們將詳細(xì)解析MySQL事務(wù)如何遵循ACID(原子性、一致性、隔離性、持久性)原則,如何通過(guò)不同的隔離級(jí)別來(lái)平衡性能和數(shù)據(jù)一致性需求。此外,我們還將重點(diǎn)介紹事務(wù)的底層實(shí)現(xiàn)原理,幫助讀者從源碼層面理解MySQL是如何管理事務(wù)以及如何保證事務(wù)的正確性和高效性。

通過(guò)這篇文章,你將不僅了解MySQL事務(wù)的基本操作和使用方法,還能深入理解其背后的實(shí)現(xiàn)原理,為你的數(shù)據(jù)庫(kù)開發(fā)與優(yōu)化提供有力的理論支持。

一、MySQL事務(wù)簡(jiǎn)單介紹

MySQL事務(wù)是指一組操作,它們被看作一個(gè)單獨(dú)的工作單元,要么全部成功,要么全部失敗回滾。在MySQL中,事務(wù)可以確保數(shù)據(jù)的一致性和完整性。

事務(wù)通常由四個(gè)關(guān)鍵詞來(lái)描述:

  1. BEGIN 或 START TRANSACTION:標(biāo)志著事務(wù)的開始。
  2. COMMIT:表示事務(wù)完成,并把所有的修改持久化到數(shù)據(jù)庫(kù)。
  3. ROLLBACK:表示事務(wù)的失敗,并且撤銷所有對(duì)數(shù)據(jù)庫(kù)的修改。
  4. SAVEPOINT:可以設(shè)置事務(wù)的一個(gè)保存點(diǎn),可以回滾到此處。

在MySQL中,只有使用了InnoDB存儲(chǔ)引擎的表才支持事務(wù)。

當(dāng)執(zhí)行一系列的SQL語(yǔ)句時(shí),如果其中有一個(gè)SQL語(yǔ)句執(zhí)行失敗,則所有SQL語(yǔ)句都會(huì)回滾,也就是說(shuō)之前的所有SQL操作都被撤銷,數(shù)據(jù)庫(kù)回到之前的狀態(tài)。而只有當(dāng)所有SQL語(yǔ)句都執(zhí)行成功后,它們才會(huì)被提交到數(shù)據(jù)庫(kù)中,這就保證了數(shù)據(jù)的一致性。

事務(wù)在開發(fā)中常用于保證數(shù)據(jù)的完整性和一致性,例如在進(jìn)行銀行轉(zhuǎn)賬時(shí),需要保證從一個(gè)賬戶扣除的金額一定會(huì)被轉(zhuǎn)入到另一個(gè)賬戶中,如果出現(xiàn)了其中一個(gè)賬戶扣除了金額而另一個(gè)賬戶沒有收到對(duì)應(yīng)的金額的情況,那么這就是一種數(shù)據(jù)的不一致性。在這種情況下,使用事務(wù)可以保證這個(gè)問(wèn)題不會(huì)發(fā)生,因?yàn)橐此械牟僮鞫汲晒?,要么都失敗?/p>

二、事務(wù)特性ACID介紹

(一)原子性(Atomicity)

事務(wù)中的所有操作要么全部成功,要么全部失敗回滾。如果有一個(gè)操作失敗,則整個(gè)事務(wù)都應(yīng)該回滾到最初狀態(tài)。

例如,假設(shè)我們有一個(gè)銀行轉(zhuǎn)賬系統(tǒng)。當(dāng)我們從一個(gè)賬戶轉(zhuǎn)賬到另一個(gè)賬戶時(shí),需要確保資金的安全和正確性。如果轉(zhuǎn)賬過(guò)程中任何一個(gè)步驟失敗,例如金額不足或接收方賬戶不存在,則必須回滾到最初狀態(tài),確保事務(wù)的原子性。

(二)一致性(Consistency)

在事務(wù)執(zhí)行過(guò)程中,數(shù)據(jù)庫(kù)必須始終保持一致狀態(tài)。在事務(wù)執(zhí)行的任何時(shí)刻,數(shù)據(jù)庫(kù)必須滿足一組事務(wù)的約束條件。

例如,假設(shè)我們有一個(gè)訂單系統(tǒng)。當(dāng)用戶下訂單時(shí),訂單總金額必須小于用戶賬戶的余額。如果訂單總金額大于用戶賬戶的余額,則必須回滾事務(wù),以保持一致性。

(三)隔離性(Isolation)

事務(wù)應(yīng)該在相互隔離的環(huán)境中執(zhí)行,以避免并發(fā)執(zhí)行時(shí)可能出現(xiàn)的問(wèn)題。每個(gè)事務(wù)都應(yīng)該以一種完全獨(dú)立的方式執(zhí)行,不受其他事務(wù)的影響。

例如,如果兩個(gè)用戶在同一時(shí)間購(gòu)買同一件商品,系統(tǒng)必須確保兩個(gè)用戶看到的是正確的庫(kù)存量,并且不會(huì)出現(xiàn)兩個(gè)用戶都買到同一件商品的情況。

(四)持久性(Durability)

一旦事務(wù)成功提交,其結(jié)果就應(yīng)該持久保存在數(shù)據(jù)庫(kù)中,即使系統(tǒng)崩潰或重新啟動(dòng),數(shù)據(jù)也應(yīng)該仍然存在。

例如,假設(shè)我們有一個(gè)電子郵件系統(tǒng)。當(dāng)用戶發(fā)送電子郵件時(shí),該郵件必須被保存在數(shù)據(jù)庫(kù)中,即使系統(tǒng)在發(fā)送電子郵件后崩潰,也必須確保該郵件在系統(tǒng)恢復(fù)后仍然存在。

需要注意的是,不同的數(shù)據(jù)庫(kù)管理系統(tǒng)對(duì)事務(wù)的實(shí)現(xiàn)方式可能會(huì)有所不同,因此在使用事務(wù)時(shí),需要根據(jù)具體的數(shù)據(jù)庫(kù)管理系統(tǒng)和應(yīng)用場(chǎng)景來(lái)選擇適合的實(shí)現(xiàn)方式。

三、事務(wù)隔離級(jí)別

在沒有隔離級(jí)別的情況下,可能會(huì)發(fā)生以下情況:

  • 臟讀(Dirty Read):一個(gè)事務(wù)讀取了另一個(gè)事務(wù)還未提交的數(shù)據(jù),如果這個(gè)事務(wù)回滾,那么讀到的數(shù)據(jù)就是無(wú)效的,這種情況稱為臟讀。
  • 不可重復(fù)讀(Non-repeatable Read):一個(gè)事務(wù)在執(zhí)行過(guò)程中多次讀取同一數(shù)據(jù),由于其他事務(wù)對(duì)該數(shù)據(jù)進(jìn)行了修改,因此這些讀取操作得到的結(jié)果可能不同,這種情況稱為不可重復(fù)讀。
  • 幻讀(Phantom Read):一個(gè)事務(wù)按照相同的查詢條件兩次查詢,但是得到的結(jié)果集卻不同。這是因?yàn)槠渌聞?wù)對(duì)該表進(jìn)行了新增或刪除操作,導(dǎo)致當(dāng)前事務(wù)查詢到的結(jié)果集不一致,這種情況稱為幻讀。

這些情況都是由于多個(gè)事務(wù)之間的數(shù)據(jù)相互干擾導(dǎo)致的,而隔離級(jí)別就是用來(lái)解決這些問(wèn)題的。

事務(wù)的隔離級(jí)別規(guī)定了在一個(gè)事務(wù)內(nèi)的修改哪些在事務(wù)內(nèi)和事務(wù)間可見,哪些不可見。SQL標(biāo)準(zhǔn)定義了四個(gè)隔離級(jí)別,一般而言,隔離級(jí)別越高,安全性越高,但系統(tǒng)開銷更大,并發(fā)性能也越差。

隔離級(jí)別含義臟讀不可重復(fù)讀幻讀
讀未提交(Read Uncommitted)一個(gè)事務(wù)執(zhí)行的操作,即使還未提交,也能被其他事務(wù)看到存在存在存在
讀已提交(Read Committed)一個(gè)事務(wù)提交之后,其他事務(wù)才能看到該事務(wù)的修改不存在存在存在
可重復(fù)讀(Repeatable Read)同一個(gè)事務(wù)內(nèi)多次讀取的結(jié)果一致不存在不存在存在
可串行化(Serializable)強(qiáng)制事務(wù)串行按順序執(zhí)行不存在不存在不存在

通過(guò)如下SQL命令可以查看和修改MySQL的事務(wù)隔離級(jí)別

-- 查看全局事務(wù)隔離級(jí)別
select @@global.tx_isolation
-- 查看當(dāng)前會(huì)話事務(wù)隔離級(jí)別
select @@tx_isolation
-- 修改全局事務(wù)隔離級(jí)別
set global transaction isolation level repeatable read
-- 修改當(dāng)前會(huì)話事務(wù)隔離級(jí)別
set session transaction isolation level repeatable read

在實(shí)際應(yīng)用中,讀未提交級(jí)別在并發(fā)時(shí)會(huì)導(dǎo)致很多問(wèn)題,性能相對(duì)于其他隔離級(jí)別提高也有限,可串行化級(jí)別強(qiáng)制事務(wù)串行,并發(fā)效率很低,只適合于對(duì)數(shù)據(jù)一致性要求極高的場(chǎng)景,這兩個(gè)隔離級(jí)別都很少使用。因此在大多數(shù)數(shù)據(jù)庫(kù)系統(tǒng)中,默認(rèn)的隔離級(jí)別是RC(讀已提交)或RR(可重復(fù)讀)。

MySQL的InnoDB默認(rèn)隔離級(jí)別是RR(可重復(fù)讀),但與標(biāo)準(zhǔn)SQL不同的是,InnoDB在RR(可重復(fù)讀)隔離級(jí)別下,使用Next-Key鎖避免了幻讀問(wèn)題。也就是說(shuō),InnoDB在RR隔離級(jí)別下已經(jīng)能完全保證事務(wù)隔離性要求,即達(dá)到了SQL標(biāo)準(zhǔn)的Serializable隔離級(jí)別。

四、MySQL事務(wù)實(shí)現(xiàn)原理

(一)事務(wù)原理總述

MySQL 事務(wù)是基于 InnoDB 存儲(chǔ)引擎實(shí)現(xiàn)的。MySQL 的事務(wù)原理主要包括以下幾個(gè)方面:

  1. redo logInnoDB 在執(zhí)行事務(wù)時(shí),會(huì)將事務(wù)的修改操作記錄在 redo log 中,以保證事務(wù)的持久性。redo log 記錄了每個(gè)事務(wù)對(duì)數(shù)據(jù)所做的修改,包括修改的行、列和修改前后的值等信息。當(dāng)事務(wù)提交時(shí),會(huì)將 redo log 寫入到磁盤中,以保證數(shù)據(jù)的持久性。
  2. undo logInnoDB 在執(zhí)行事務(wù)時(shí),會(huì)將事務(wù)的修改操作記錄在 undo log 中,以支持事務(wù)的回滾和 MVCC 功能。undo log 記錄了每個(gè)事務(wù)對(duì)數(shù)據(jù)所做的修改,包括修改的行、列和修改前的值等信息。當(dāng)事務(wù)需要回滾時(shí),會(huì)使用 undo log 中的信息將數(shù)據(jù)恢復(fù)到事務(wù)執(zhí)行前的狀態(tài)。
  3. MVCCInnoDB 實(shí)現(xiàn)了多版本并發(fā)控制(MVCC)來(lái)支持事務(wù)的隔離性。MVCC 是通過(guò)保存多個(gè)版本的同一行來(lái)實(shí)現(xiàn)的,每個(gè)版本都有一個(gè)唯一的時(shí)間戳,表示該版本的生命周期。在事務(wù)執(zhí)行過(guò)程中,會(huì)根據(jù)當(dāng)前事務(wù)的隔離級(jí)別確定可見的數(shù)據(jù)版本,以保證事務(wù)之間的隔離。同時(shí)也可以保證并發(fā)性。
  4. 鎖機(jī)制InnoDB 通過(guò)實(shí)現(xiàn)共享鎖和排它鎖來(lái)保證數(shù)據(jù)的一致性和隔離性。共享鎖用于讀操作,可以多個(gè)事務(wù)同時(shí)持有;排它鎖用于寫操作,同一時(shí)間只能有一個(gè)事務(wù)持有。在事務(wù)執(zhí)行過(guò)程中,會(huì)根據(jù)需要自動(dòng)加鎖和解鎖,以保證數(shù)據(jù)的一致性、隔離性和并發(fā)性。
  5. 事務(wù)提交與回滾InnoDB 支持事務(wù)的原子性,一旦事務(wù)提交,就會(huì)將修改操作寫入磁盤中,并釋放所有鎖。如果事務(wù)發(fā)生異?;虮换貪L,會(huì)將修改操作回滾,并釋放所有鎖。

MySQL 事務(wù)的原理涉及多個(gè)方面,包括 redo log、undo log、MVCC、鎖機(jī)制以及事務(wù)提交和回滾等。這些機(jī)制共同保證了事務(wù)的 ACID 特性,同時(shí)也保證了數(shù)據(jù)的一致性、并發(fā)性和持久性。

(二)undo log 原子性分析

undo log 是 InnoDB 存儲(chǔ)引擎中用于實(shí)現(xiàn)事務(wù)回滾和 MVCC 的機(jī)制之一,可以保證事務(wù)的原子性。其原理如下:

當(dāng)一個(gè)事務(wù)需要修改一行數(shù)據(jù)時(shí),InnoDB 首先將該行數(shù)據(jù)的原始值拷貝到 undo log 中,然后執(zhí)行修改操作。如果事務(wù)需要回滾,可以使用 undo log 中的原始值將數(shù)據(jù)恢復(fù)到修改前的狀態(tài)。如果事務(wù)提交,則可以將 undo log 中的信息刪除。

在事務(wù)執(zhí)行期間,每次對(duì)數(shù)據(jù)進(jìn)行修改時(shí),InnoDB 將修改前的值保存到 undo log 中,以便在事務(wù)回滾時(shí)使用。如果事務(wù)提交,則將 undo log 中的信息刪除,以保證數(shù)據(jù)的一致性。如果事務(wù)發(fā)生異?;蚧貪L,可以使用 undo log 中的信息將數(shù)據(jù)恢復(fù)到事務(wù)開始前的狀態(tài),以保證事務(wù)的原子性。

undo log 通過(guò)保存數(shù)據(jù)的原始值來(lái)保證事務(wù)的原子性,可以使得數(shù)據(jù)修改操作能夠撤銷和回滾,并確保數(shù)據(jù)的一致性。

(三)redo log 持久性分析

redo log 是 InnoDB 存儲(chǔ)引擎實(shí)現(xiàn)事務(wù)持久性的重要機(jī)制之一。在事務(wù)提交時(shí),InnoDB 會(huì)將事務(wù)所做的修改操作記錄在 redo log 中,并確保其持久化到磁盤上,從而保證數(shù)據(jù)的持久性。

具體來(lái)說(shuō),InnoDB 使用 WAL 技術(shù)(Write-Ahead Logging)來(lái)實(shí)現(xiàn) redo log 的持久化。WAL 技術(shù)的基本思想是先將修改操作記錄到 redo log 中,再將數(shù)據(jù)寫入磁盤中。這樣可以確保在出現(xiàn)宕機(jī)等異常情況時(shí),可以通過(guò) redo log 中的信息將數(shù)據(jù)恢復(fù)到事務(wù)執(zhí)行前的狀態(tài),從而保證數(shù)據(jù)的一致性和持久性。

在 InnoDB 中,redo log 是以固定大小的文件形式存在的。當(dāng) redo log 文件被寫滿后,InnoDB 會(huì)自動(dòng)創(chuàng)建新的 redo log 文件,并將新的修改操作記錄在新的文件中。舊的 redo log 文件可以在不影響數(shù)據(jù)一致性的情況下被刪除,從而實(shí)現(xiàn) redo log 的循環(huán)利用。

為了確保 redo log 的持久化,InnoDB 在寫入 redo log 時(shí)會(huì)采用一些優(yōu)化技術(shù),例如 write-ahead logging 和 group commit。write-ahead logging 是指在修改數(shù)據(jù)之前,先將修改操作記錄在 redo log 中,再將數(shù)據(jù)寫入磁盤中。這樣可以確保即使出現(xiàn)宕機(jī)等異常情況,也可以通過(guò) redo log 中的信息將數(shù)據(jù)恢復(fù)到事務(wù)執(zhí)行前的狀態(tài)。而 group commit 是指將多個(gè)事務(wù)的提交操作合并到一起,一起寫入 redo log 中,從而減少寫入磁盤的次數(shù),提高寫入性能。

InnoDB 通過(guò) WAL 技術(shù)實(shí)現(xiàn) redo log 的持久化,并采用 write-ahead logging 和 group commit 等優(yōu)化技術(shù)來(lái)提高寫入性能。這些機(jī)制共同保證了 MySQL 數(shù)據(jù)庫(kù)的事務(wù)持久性,從而保證了數(shù)據(jù)的一致性和可靠性。

(四)多版本并發(fā)控制(MVCC)隔離性分析

MVCC(Multi-Version Concurrency Control)是 InnoDB 存儲(chǔ)引擎用來(lái)實(shí)現(xiàn)事務(wù)隔離的一種技術(shù)。MVCC 技術(shù)通過(guò)為每個(gè)事務(wù)保存一個(gè)可見的數(shù)據(jù)版本,來(lái)實(shí)現(xiàn)在并發(fā)訪問(wèn)的情況下保證事務(wù)的隔離性。MVCC 主要涉及以下兩個(gè)方面:

版本號(hào)

在 MVCC 中,每一行數(shù)據(jù)都會(huì)有多個(gè)版本號(hào),每個(gè)版本號(hào)對(duì)應(yīng)著一個(gè)事務(wù),表示該版本是由該事務(wù)所修改的。事務(wù)在進(jìn)行修改時(shí),會(huì)為該行數(shù)據(jù)生成一個(gè)新的版本,該版本號(hào)比當(dāng)前最大的版本號(hào)大1。而查詢操作只能讀取版本號(hào)小于等于當(dāng)前事務(wù)的版本號(hào)的數(shù)據(jù)。

事務(wù)版本鏈

每個(gè)事務(wù)都有一個(gè)版本鏈,版本鏈?zhǔn)怯稍撌聞?wù)創(chuàng)建的所有版本所組成的鏈表。在該鏈表上,每個(gè)版本都指向前一個(gè)版本,最后一個(gè)版本指向 NULL。版本鏈的作用是,當(dāng)事務(wù)需要回滾時(shí),可以沿著版本鏈將數(shù)據(jù)恢復(fù)到事務(wù)開始的狀態(tài)。

通過(guò)使用版本號(hào)和事務(wù)版本鏈,MVCC 實(shí)現(xiàn)了 InnoDB 存儲(chǔ)引擎的多版本并發(fā)控制,同時(shí)也保證了事務(wù)的隔離性。在執(zhí)行查詢操作時(shí),根據(jù)當(dāng)前事務(wù)的隔離級(jí)別,InnoDB 存儲(chǔ)引擎會(huì)選擇可見的數(shù)據(jù)版本。在可重復(fù)讀的隔離級(jí)別下,InnoDB 存儲(chǔ)引擎會(huì)將當(dāng)前事務(wù)的版本號(hào)作為可見的最大版本號(hào),因此當(dāng)前事務(wù)只能讀取該版本號(hào)之前的數(shù)據(jù)版本,避免了臟讀和不可重復(fù)讀等問(wèn)題。

MVCC只在RR和RC隔離級(jí)別下生效,不同的是RR級(jí)別下在事務(wù)第一個(gè)select語(yǔ)句開始的時(shí)候生成快照讀視圖,RC級(jí)別下每次select都會(huì)生成新的讀視圖。

需要注意的是,MVCC 技術(shù)雖然可以有效地提高并發(fā)性,但同時(shí)也會(huì)帶來(lái)一些問(wèn)題,如版本鏈過(guò)長(zhǎng)可能導(dǎo)致性能問(wèn)題,同時(shí)需要占用更多的存儲(chǔ)空間來(lái)保存多個(gè)版本。因此,在使用 MVCC 技術(shù)時(shí),需要權(quán)衡其帶來(lái)的利弊,合理地設(shè)置事務(wù)隔離級(jí)別和存儲(chǔ)空間等參數(shù)。

(五)MySQL的鎖機(jī)制一致性與隔離性性分析

鎖機(jī)制主要是為了保證并發(fā)事務(wù)的一致性和隔離性。在并發(fā)事務(wù)中,多個(gè)事務(wù)可能同時(shí)操作相同的數(shù)據(jù),如果不進(jìn)行鎖定,就會(huì)產(chǎn)生數(shù)據(jù)不一致的問(wèn)題。例如,兩個(gè)事務(wù)同時(shí)對(duì)同一行數(shù)據(jù)進(jìn)行修改,如果沒有鎖機(jī)制,可能會(huì)導(dǎo)致數(shù)據(jù)被覆蓋,從而造成數(shù)據(jù)的不一致。

通過(guò)使用鎖機(jī)制,可以保證每個(gè)事務(wù)在修改數(shù)據(jù)時(shí),都能夠獨(dú)占相應(yīng)的資源,防止其他事務(wù)對(duì)數(shù)據(jù)的并發(fā)操作,從而保證了事務(wù)的一致性。同時(shí),鎖機(jī)制也可以通過(guò)設(shè)置不同的隔離級(jí)別來(lái)保證事務(wù)之間的隔離性,避免不同事務(wù)之間的互相干擾和影響。

因此,鎖機(jī)制既保證了并發(fā)事務(wù)的一致性,也保證了事務(wù)之間的隔離性。具體原理可以概括為:在事務(wù)修改數(shù)據(jù)之前,需要先獲得相應(yīng)的鎖,獲得鎖之后,事務(wù)才可以修改數(shù)據(jù),并且在整個(gè)事務(wù)期間,這部分?jǐn)?shù)據(jù)都是鎖定的,其他事務(wù)如果要修改數(shù)據(jù),必須等待當(dāng)前事務(wù)提交或回滾后釋放鎖

行鎖與表鎖

鎖按照粒度可以分為行鎖和表鎖。表鎖會(huì)鎖定整張表,而行鎖則只鎖定需要操作的數(shù)據(jù),顯然行鎖具有更好的并發(fā)性能。但是由于加鎖本身需要消耗資源(獲得鎖、檢查鎖、釋放鎖等都需要消耗資源),因此在鎖定數(shù)據(jù)較多情況下使用表鎖可以節(jié)省大量資源。

InnoDB同時(shí)支持表鎖和行鎖,出于性能考慮,絕大多數(shù)情況下使用的都是行鎖。InnoDB實(shí)現(xiàn)了兩種標(biāo)準(zhǔn)行級(jí)鎖

  • 共享鎖(S Lock):允許事務(wù)讀一行數(shù)據(jù)。在select語(yǔ)句后面加上lock in share mode可以顯式獲取共享鎖

  • 排他鎖(X Lock):允許事務(wù)刪除或更新一行數(shù)據(jù)。update、delete和insert語(yǔ)句會(huì)自動(dòng)給涉及數(shù)據(jù)集加排他鎖,select語(yǔ)句需要在語(yǔ)句后加上for update顯式加排他鎖

#排他鎖
SELECT * FROM table_name WHERE ... FOR UPDATE;
#共享鎖
SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE;

意向鎖

意向鎖是一種特殊的表級(jí)鎖,它是為了協(xié)調(diào)行級(jí)鎖和表級(jí)鎖而引入的。在進(jìn)行行級(jí)鎖定之前,InnoDB 存儲(chǔ)引擎會(huì)先使用意向鎖來(lái)協(xié)調(diào)并通知其它事務(wù)該行的鎖定情況,從而提高并發(fā)性能。

意向鎖分為兩種類型:

  • 意向共享鎖(Intention Shared Lock,IS鎖):表示一個(gè)事務(wù)想要在某個(gè)數(shù)據(jù)行上加共享鎖,此時(shí)會(huì)先設(shè)置該表的 IS 鎖。當(dāng)一個(gè)事務(wù)想要在某個(gè)數(shù)據(jù)行上加行級(jí)共享鎖時(shí),需要檢查該表的 IS 鎖是否存在,如果存在,則說(shuō)明有其它事務(wù)想要在該表上加行級(jí)共享鎖,此時(shí)需要等待其它事務(wù)釋放 IS 鎖后再進(jìn)行加鎖操作。
  • 意向排他鎖(Intention Exclusive Lock,IX鎖):表示一個(gè)事務(wù)想要在某個(gè)數(shù)據(jù)行上加排他鎖,此時(shí)會(huì)先設(shè)置該表的 IX 鎖。當(dāng)一個(gè)事務(wù)想要在某個(gè)數(shù)據(jù)行上加行級(jí)排他鎖時(shí),需要檢查該表的 IX 鎖是否存在,如果存在,則說(shuō)明有其它事務(wù)想要在該表上加行級(jí)共享鎖或行級(jí)排他鎖,此時(shí)需要等待其它事務(wù)釋放 IX 鎖后再進(jìn)行加鎖操作。

使用意向鎖的主要目的是減少鎖沖突,提高并發(fā)性能,同時(shí)保證數(shù)據(jù)的一致性。如果沒有意向鎖的協(xié)調(diào)機(jī)制,可能會(huì)導(dǎo)致不同事務(wù)之間的鎖定產(chǎn)生沖突,從而降低并發(fā)性能。

擴(kuò)展:意向鎖、共享鎖和排他鎖的兼容性

ISIXSX
IS兼容兼容兼容不兼容
IX兼容兼容不兼容不兼容
S兼容不兼容兼容不兼容
X不兼容不兼容不兼容不兼容

行鎖算法(記錄鎖+間隙鎖+下一鍵鎖)

在行鎖中,有三種行鎖算法:Record Lock、Gap Lock和Next-Key Lock。下面對(duì)這三種鎖進(jìn)行詳細(xì)分析:

  • Record Lock(記錄鎖)Record Lock是在行上設(shè)置的鎖,用于保證在事務(wù)中不會(huì)有其他事務(wù)對(duì)同一行進(jìn)行修改。在事務(wù)中對(duì)某一行進(jìn)行修改時(shí),會(huì)對(duì)該行加上記錄鎖,其他事務(wù)需要對(duì)該行進(jìn)行修改時(shí),必須等待該記錄鎖被釋放。
  • Gap Lock(間隙鎖)Gap Lock是在索引記錄之間設(shè)置的鎖,用于防止其他事務(wù)在這些索引記錄之間插入新的索引記錄。在事務(wù)中對(duì)索引進(jìn)行修改時(shí),會(huì)對(duì)索引記錄之間的間隙加上間隙鎖,其他事務(wù)需要在這些間隙之間插入新的索引記錄時(shí),必須等待間隙鎖被釋放。
  • Next-Key Lock(下一鍵鎖)Next-Key Lock是Record Lock和Gap Lock的結(jié)合體,同時(shí)鎖住了索引記錄和索引記錄之間的間隙。在事務(wù)中對(duì)索引進(jìn)行修改時(shí),會(huì)對(duì)索引記錄及其間隙加上下一鍵鎖,其他事務(wù)需要對(duì)這些索引記錄及其間隙進(jìn)行修改時(shí),必須等待下一鍵鎖被釋放。

在上述三種鎖中,Record Lock用于保證行的并發(fā)訪問(wèn),Gap Lock用于保證索引記錄之間的并發(fā)訪問(wèn),Next-Key Lock則是前兩種鎖的結(jié)合體,用于同時(shí)保證行和索引記錄之間的并發(fā)訪問(wèn)。

需要注意的是,Next-Key Lock并不僅僅是Record Lock和Gap Lock的簡(jiǎn)單疊加,而是在兩種鎖的基礎(chǔ)上增加了額外的約束條件。例如,Next-Key Lock會(huì)鎖定當(dāng)前索引記錄及其間隙,并要求下一個(gè)索引記錄不能被鎖定。這種鎖的機(jī)制可以有效地避免死鎖的發(fā)生,同時(shí)保證數(shù)據(jù)的一致性和完整性。

案例分析

CREATE TABLE user (
? id INT PRIMARY KEY,
? name VARCHAR(50),
? age INT
);

Record Lock行鎖算法舉例

當(dāng)執(zhí)行一個(gè)更新操作時(shí),InnoDB 會(huì)將要更新的行加上行鎖,以保證其它事務(wù)不能修改這一行,直到當(dāng)前事務(wù)提交或回滾。如果其它事務(wù)要修改同一行,必須等待該行的行鎖被釋放。

現(xiàn)在執(zhí)行以下兩個(gè)事務(wù):

事務(wù) A:

BEGIN;
SELECT * FROM user WHERE id=1 FOR UPDATE;
-- do some updates
COMMIT;

事務(wù) B:
BEGIN;
SELECT * FROM user WHERE id=1 FOR UPDATE;
-- do some updates
COMMIT;

當(dāng)事務(wù) A 執(zhí)行 SELECT * FROM user WHERE id=1 FOR UPDATE; 語(yǔ)句時(shí),會(huì)將 id=1 的行加上行鎖。因此,當(dāng)事務(wù) B 執(zhí)行 SELECT * FROM user WHERE id=1 FOR UPDATE; 時(shí),會(huì)被阻塞,直到事務(wù) A 釋放行鎖。

Gap Lock行鎖算法舉例

Gap Lock 的作用是鎖定一個(gè)范圍,但不包括記錄本身。當(dāng)一個(gè)事務(wù)要向一個(gè)不存在的記錄插入數(shù)據(jù)時(shí),InnoDB 會(huì)加上 Gap Lock,以確保沒有其它事務(wù)插入相同的記錄。同樣的,如果其它事務(wù)要更新或刪除這個(gè)范圍內(nèi)的記錄,也會(huì)被阻塞。

現(xiàn)在執(zhí)行以下兩個(gè)事務(wù):

事務(wù) A:

BEGIN;
INSERT INTO user (id, name, age) VALUES (5, 'Tom', 25);
COMMIT;

事務(wù) B:

BEGIN;
SELECT * FROM user WHERE id > 4 AND id < 6 FOR UPDATE;
-- do some updates
COMMIT;

當(dāng)事務(wù) A 執(zhí)行 INSERT INTO user (id, name, age) VALUES (5, 'Tom', 25); 語(yǔ)句時(shí),會(huì)加上 Gap Lock,鎖定 id > 4 AND id < 6 這個(gè)范圍。因此,當(dāng)事務(wù) B 執(zhí)行 SELECT * FROM user WHERE id > 4 AND id < 6 FOR UPDATE; 時(shí),會(huì)被阻塞,直到事務(wù) A 釋放 Gap Lock。

Next-Key Lock行鎖算法舉例

Next-Key Lock 不僅鎖定范圍,還鎖定范圍內(nèi)的記錄,以保證記錄在范圍內(nèi)的行被鎖定。Next-Key Lock 由兩部分組成,一部分是 Gap Lock,一部分是 Record Lock。下面給出 Next-Key Lock 的三種典型情況:

  • 當(dāng)事務(wù) A 執(zhí)行 INSERT INTO user (id, name, age) VALUES (5, 'Tom', 25); 語(yǔ)句時(shí),會(huì)加上 Next-Key Lock,鎖定 id = 5 這一行。因此,當(dāng)事務(wù) B 執(zhí)行 SELECT * FROM user WHERE id = 5 FOR UPDATE; 時(shí),會(huì)被阻塞,直到事務(wù) A 釋放 Next-Key Lock。
  • 當(dāng)事務(wù) A 執(zhí)行 DELETE FROM user WHERE id = 5; 語(yǔ)句時(shí),會(huì)加上 Next-Key Lock,鎖定 id = 5 這一行。因此,當(dāng)事務(wù) B 執(zhí)行 SELECT * FROM user WHERE id = 5 FOR UPDATE; 時(shí),會(huì)被阻塞,直到事務(wù) A 釋放 Next-Key Lock。
  • 當(dāng)事務(wù) A 執(zhí)行 UPDATE user SET age = 30 WHERE id = 5; 語(yǔ)句時(shí),會(huì)加上 Next-Key Lock,鎖定 id = 5 這一行。因此,當(dāng)事務(wù) B 執(zhí)行 SELECT * FROM user WHERE id = 5 FOR UPDATE; 時(shí),會(huì)被阻塞,直到事務(wù) A 釋放 Next-Key Lock。

Next-Key Lock 可以保證不會(huì)出現(xiàn)幻讀的情況,因?yàn)?Next-Key Lock 不僅鎖定了范圍,還鎖定了范圍內(nèi)的記錄。而幻讀是由于范圍內(nèi)有新插入的行導(dǎo)致的,Next-Key Lock 可以鎖定這些新插入的行,從而避免了幻讀的發(fā)生。

(六)死鎖問(wèn)題分析

既然InnoDB對(duì)記錄操作時(shí)會(huì)加鎖,不可避免會(huì)出現(xiàn)死鎖的問(wèn)題。如果兩個(gè)事務(wù)在執(zhí)行過(guò)程中,都持有對(duì)方需要的鎖,并且在等待對(duì)方釋放鎖,此時(shí)就發(fā)生了死鎖。

簡(jiǎn)單鎖舉例場(chǎng)景

假設(shè)我們有一張表user(id, name, age),id是主鍵,name是普通索引。索引數(shù)據(jù)如下

場(chǎng)景一:delete from user where id=3   走id主鍵索引,會(huì)直接鎖主鍵索引上id為3的記錄

場(chǎng)景二:delete from user where name='盧自清'   走name二級(jí)索引,會(huì)先鎖住二級(jí)索引,然后再去鎖聚簇索引上對(duì)應(yīng)主鍵的記錄

場(chǎng)景三:delete from user where age=20  不走索引,全表掃描,會(huì)對(duì)所有記錄加鎖

可以看到,查詢條件的不同,加鎖的結(jié)果也不一樣。而死鎖一般是事務(wù)相互等待對(duì)方的鎖,最后形成環(huán)路造成的。

典型形成環(huán)路的死鎖例子

操作不同表的相同記錄

事務(wù)A事務(wù)B

begin;

delete from table1 where id=2;

begin;

update table2 set msg='aaa' where id=1;

update table2 set msg='aaa' where id=1;
delete from table1 where id=2;

這個(gè)比較好理解,事務(wù)A持有表table1的id=2記錄行鎖,等待表table2的id=1的記錄行鎖,事務(wù)B持有表table2的id=1記錄行鎖,等待表table1的id=2記錄行鎖,兩者互相等待,出現(xiàn)死鎖

操作同一張表的相同記錄

事務(wù)A事務(wù)B

begin;

delete from table1 where id=2;

begin;

delete from table1 where id=1;

delete from table1 where id=1;
delete from table1 where id=2;

這個(gè)比較常見,事務(wù)在批量更新的時(shí)候,如果一個(gè)事務(wù)更新的順序是[1,2],另一個(gè)事務(wù)更新的順序是[2,1],就可能出現(xiàn)死鎖

不同索引造成鎖沖突

事務(wù)A事務(wù)B

begin;

update user set name='張清風(fēng)' where name='盧自清';

begin;

delete from user where  id=2;

這個(gè)就很隱晦了,事務(wù)A在執(zhí)行時(shí),除了在二級(jí)索引加鎖外,還會(huì)在主鍵索引上加鎖,在主鍵索引上加鎖的順序是[2,5],事務(wù)B執(zhí)行時(shí),只在主鍵索引上加鎖,加鎖順序是[2]。[2]存在環(huán)路,有發(fā)生死鎖的可能。

gap鎖沖突

事務(wù)A事務(wù)B

begin;

update user set name='張清風(fēng)' where name='盧自清';

begin;

update user set name='張清風(fēng)' where name='王澄泓';

insert into user values(null,'林可佳',19);
insert into user values(null,'閆瀾飜',28);

事務(wù)A和事務(wù)B都持有g(shù)ap鎖,插入數(shù)據(jù)時(shí)都要等待對(duì)方的gap鎖釋放,發(fā)生死鎖。

擴(kuò)展:避免死鎖技巧

由于死鎖是個(gè)偶發(fā)性的問(wèn)題,對(duì)線上造成的影響也難以預(yù)料,要求在業(yè)務(wù)層面采取措施避免死鎖的發(fā)生,下面給出了幾個(gè)可以避免死鎖的技巧:

  • 以固定的順序訪問(wèn)表和行,避免循環(huán)等待
  • 大事務(wù)更容易發(fā)生死鎖,如果業(yè)務(wù)允許,將大事務(wù)拆小。
  • 在同一個(gè)事務(wù)中,盡可能做到一次鎖定所需要的所有資源,減少死鎖概率。
  • 降低隔離級(jí)別。如果業(yè)務(wù)允許,將隔離級(jí)別從RR調(diào)整為RC,可以避免掉很多因?yàn)間ap鎖造成的死鎖。
  • 為表添加合理的索引。如果不走索引將會(huì)為表的所有行都加鎖,增大了死鎖的概率。

五、總結(jié)

在本文中,我們深入探討了MySQL事務(wù)的基本概念及其實(shí)現(xiàn)原理。事務(wù)作為數(shù)據(jù)庫(kù)管理系統(tǒng)中的一個(gè)關(guān)鍵組成部分,能夠確保在并發(fā)操作中維護(hù)數(shù)據(jù)的一致性與完整性。我們?cè)敿?xì)介紹了事務(wù)的四大基本特性——ACID原則,以及MySQL如何通過(guò)不同的隔離級(jí)別來(lái)平衡性能與一致性,滿足不同場(chǎng)景下的需求。

通過(guò)分析MySQL事務(wù)的實(shí)現(xiàn)原理,我們揭示了InnoDB存儲(chǔ)引擎如何通過(guò)MVCC(多版本并發(fā)控制)、鎖機(jī)制、日志文件等技術(shù),保證事務(wù)的原子性和隔離性,處理并發(fā)訪問(wèn)時(shí)的讀寫沖突。此外,事務(wù)的日志記錄機(jī)制(包括redo日志和undo日志)也為我們提供了在出現(xiàn)故障時(shí)恢復(fù)數(shù)據(jù)的一種有效手段。

在實(shí)際開發(fā)中,合理的事務(wù)管理不僅能保證數(shù)據(jù)的可靠性,還能在一定程度上提高系統(tǒng)的性能。理解事務(wù)的底層實(shí)現(xiàn)原理,將幫助開發(fā)者在進(jìn)行數(shù)據(jù)庫(kù)設(shè)計(jì)和性能調(diào)優(yōu)時(shí)做出更明智的決策。

總之,MySQL事務(wù)機(jī)制不僅是數(shù)據(jù)庫(kù)操作的核心之一,也是保障數(shù)據(jù)安全、提高系統(tǒng)并發(fā)性能的重要技術(shù)。希望本文的分析和講解,能夠?yàn)樽x者在數(shù)據(jù)庫(kù)開發(fā)和優(yōu)化過(guò)程中提供幫助,并為深入理解數(shù)據(jù)庫(kù)底層技術(shù)奠定基礎(chǔ)。

參考文章鏈接

  1. 阮一峰的《MySQL 教程》中的事務(wù)部分:https://www.ruanyifeng.com/blog/2013/12/mysql_tutorial.html
  2. MySQL 官方文檔中的事務(wù)部分:MySQL :: MySQL 8.0 Reference Manual :: 13.3.1 START TRANSACTION, COMMIT, and ROLLBACK Statements
  3. 阿里云數(shù)據(jù)庫(kù) RDS 的事務(wù)管理介紹:404錯(cuò)誤頁(yè)-阿里云幫助中心
  4. 《高性能 MySQL》一書中的事務(wù)部分:高性能MySQL(第3版) (豆瓣)
  5. MySQL 事務(wù)詳解:https://www.runoob.com/mysql/mysql-transactions.html
  6. MySQL 事務(wù)隔離級(jí)別和鎖機(jī)制詳解:https://www.cnblogs.com/lzrabbit/p/3734850.html
  7. 深入淺出 MySQL 事務(wù)隔離級(jí)別:https://www.cnblogs.com/-wenli/p/11046971.html
  8. InnoDB 存儲(chǔ)引擎下的事務(wù)管理機(jī)制:https://mp.weixin.qq.com/s/kZzeCLylw5zJbgRv6oOUzA
  9. MySQL 存儲(chǔ)引擎 InnoDB 事務(wù)原理詳解:利用Rsync同步備份服務(wù)器數(shù)據(jù)-騰訊云開發(fā)者社區(qū)-騰訊云
  10. MySQL 事務(wù)深入分析:https://www.cnblogs.com/kerrycode/p/4743812.html

相關(guān)文章

  • 如何使用mysql完成excel中的數(shù)據(jù)生成

    如何使用mysql完成excel中的數(shù)據(jù)生成

    這篇文章主要介紹了如何使用mysql完成excel中的數(shù)據(jù)生成的相關(guān)資料,需要的朋友可以參考下
    2017-11-11
  • mysql隨機(jī)查詢10條數(shù)據(jù)的三種方法

    mysql隨機(jī)查詢10條數(shù)據(jù)的三種方法

    本文主要介紹了mysql隨機(jī)查詢10條數(shù)據(jù)的三種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-09-09
  • 解決MySQL innoDB間隙鎖產(chǎn)生的死鎖問(wèn)題

    解決MySQL innoDB間隙鎖產(chǎn)生的死鎖問(wèn)題

    線上經(jīng)常偶發(fā)死鎖問(wèn)題,當(dāng)時(shí)處理一張表,也沒有聯(lián)表處理,但是有兩個(gè)mq入口,并且消息體存在一樣的情況,但是是偶發(fā)的,又模擬不出來(lái)什么場(chǎng)景會(huì)導(dǎo)致死鎖,只能進(jìn)行代碼分析,問(wèn)題還原的方式去排查問(wèn)題,本文給大家介紹了如何解決MySQL innoDB間隙鎖產(chǎn)生的死鎖問(wèn)題
    2023-10-10
  • mysql ONLY_FULL_GROUP_BY設(shè)置sql_mode無(wú)效排查問(wèn)題(windows)

    mysql ONLY_FULL_GROUP_BY設(shè)置sql_mode無(wú)效排查問(wèn)題(windows)

    這篇文章主要介紹了mysql ONLY_FULL_GROUP_BY設(shè)置sql_mode無(wú)效排查問(wèn)題(windows),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-09-09
  • MySql中子查詢內(nèi)查詢示例詳解

    MySql中子查詢內(nèi)查詢示例詳解

    這篇文章主要介紹了MySql中子查詢內(nèi)查詢示例詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • mysql自增長(zhǎng)ID字段丟失問(wèn)題及解決

    mysql自增長(zhǎng)ID字段丟失問(wèn)題及解決

    MySQL 8.0前InnoDB自增ID重啟會(huì)丟失,重新插入從max+1開始;8.0后持久化到磁盤,保留原有值,MyISAM在8.0及以后版本均不會(huì)丟失自增ID,差異源于存儲(chǔ)引擎對(duì)自增值的處理方式
    2025-09-09
  • mysql中insert與select的嵌套使用解決組合字段插入問(wèn)題

    mysql中insert與select的嵌套使用解決組合字段插入問(wèn)題

    本節(jié)主要介紹了mysql中insert與select的嵌套使用解決組合字段插入問(wèn)題,需要的朋友可以參考下
    2014-07-07
  • Java的Struts框架中的主題模板和國(guó)際化設(shè)置

    Java的Struts框架中的主題模板和國(guó)際化設(shè)置

    這篇文章主要介紹了Java的Struts框架中的主題模板和國(guó)際化設(shè)置,Struts是Java的SSH三大web開放框架之一,需要的朋友可以參考下
    2015-12-12
  • 騰訊面試:一條SQL語(yǔ)句執(zhí)行得很慢的原因有哪些?---不看后悔系列(推薦)

    騰訊面試:一條SQL語(yǔ)句執(zhí)行得很慢的原因有哪些?---不看后悔系列(推薦)

    這篇文章主要介紹了SQL語(yǔ)句執(zhí)行慢的原因,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • mysql表的內(nèi)連和外連實(shí)戰(zhàn)記錄

    mysql表的內(nèi)連和外連實(shí)戰(zhàn)記錄

    在開發(fā)中我們的業(yè)務(wù)需求有時(shí)候是復(fù)雜的,多張表聯(lián)合查詢的時(shí)候是有多種方式的,面對(duì)不同的需求,靈活使用不同的表連接方式,這篇文章主要給大家介紹了關(guān)于mysql表內(nèi)連和外連的相關(guān)資料,需要的朋友可以參考下
    2024-01-01

最新評(píng)論

渭南市| 岐山县| 于田县| 镇原县| 娄底市| 渝北区| 永川市| 泸州市| 体育| 珲春市| 辛集市| 五峰| 泰顺县| 沾化县| 青神县| 新丰县| 井研县| 泗水县| 右玉县| 贵州省| 鄂伦春自治旗| 绥江县| 浏阳市| 方正县| 安新县| 信阳市| 玛纳斯县| 湖北省| 罗城| 平顶山市| 祁连县| 姜堰市| 江安县| 嘉义县| 亳州市| 镇雄县| 赤峰市| 阳江市| 肥西县| 鄂托克前旗| 红桥区|