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

Mysql事物的持久性及原子性詳解

 更新時(shí)間:2026年05月27日 09:10:58   作者:有夢想的魚  
這段文章詳細(xì)介紹了數(shù)據(jù)庫事務(wù)的ACID特性,重點(diǎn)闡述了原子性和持久性的實(shí)現(xiàn)機(jī)制,包括CommitLogging和WAL機(jī)制,通過具體案例和代碼演示,深入解析了數(shù)據(jù)庫如何確保事務(wù)的正確執(zhí)行,感興趣的朋友跟隨小編一起看看吧

前言

本篇文章是分享學(xué)習(xí)《鳳凰架構(gòu)》這本書的筆記。感興趣的朋友,京東商城有售,也有電子版,強(qiáng)烈推薦大家去了解一下。

記得剛學(xué)習(xí)數(shù)據(jù)庫時(shí),老師講到事務(wù)就提到了 ACID 這個字眼。那時(shí)候比較懵逼,考試時(shí)記不住這4個單詞所有的含義,反正寫個 ACID 的話,2分也能拿一分。一直到工作了好幾年之后,準(zhǔn)確來說到我學(xué)習(xí)這個之前,其實(shí)一直持續(xù)懵逼的狀態(tài)就是:你問事務(wù),我就說 ACID,主打一個不深究放過自己。

就像當(dāng)年面試人家問什么是 MVC,我就說就是那個3個圈圈,模型視圖控制器,具體不太好描述。不過目前為止真正的行家遇到比較少,幾乎都能糊弄過去。我猜大概率可能是人家不好揭穿我,也有可能是跟我一樣在不求甚解的階段。至于哪一種也不重要,目前來說有AI學(xué)東西還是很快的,雖然無法決定我的上限,但它能顯著提升我能力的下限。

下面復(fù)習(xí)一遍數(shù)據(jù)庫事務(wù)。數(shù)據(jù)庫管理系統(tǒng)在寫入或者更新數(shù)據(jù)的過程中,為了保證數(shù)據(jù)是正確可靠的,需要滿足四個特性:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持久性(Durability),簡稱 ACID。這次學(xué)習(xí)的步驟是先學(xué)習(xí)原子性持久性,至于隔離性后續(xù)再分享。

?? ACID 核心特性

特性英文核心定義關(guān)鍵說明
原子性Atomicity不可分割事務(wù)中的操作要么全部完成,要么全部不完成。若發(fā)生錯誤,系統(tǒng)會回滾(Rollback)到事務(wù)開始前的狀態(tài)。
一致性Consistency數(shù)據(jù)完整性事務(wù)執(zhí)行前后,數(shù)據(jù)庫的完整性約束沒有被破壞。寫入的數(shù)據(jù)必須完全符合所有的預(yù)設(shè)規(guī)則。
隔離性Isolation并發(fā)互不干擾多個并發(fā)事務(wù)之間相互隔離,防止交叉執(zhí)行導(dǎo)致數(shù)據(jù)不一致。(如讀未提交、可重復(fù)讀等)
持久性Durability永久生效事務(wù)一旦提交,對數(shù)據(jù)的修改就是永久的。即使后續(xù)發(fā)生系統(tǒng)故障或斷電,數(shù)據(jù)也不會丟失。

1.為什么說 ACID 的提法不太嚴(yán)謹(jǐn)?

在學(xué)習(xí)《鳳凰架構(gòu)》后,原文中總結(jié)的一點(diǎn)讓我豁然開朗:

“對這四種特性是不太嚴(yán)謹(jǐn)?shù)?,因?yàn)檫@四種特性并不正交。A、I、D 是手段,C 是目的,為了拼湊個單詞縮寫才弄到一塊去。其實(shí)誤導(dǎo)的弊端已經(jīng)超過了易于傳播的好處。”

當(dāng)一個服務(wù)只操作一個數(shù)據(jù)源的時(shí)候,通過 A、I、D 來獲得一致性是相對容易的。但當(dāng)一個服務(wù)涉及到多個不同的數(shù)據(jù)源,甚至多個不同服務(wù)同時(shí)涉及到多個不同的數(shù)據(jù)源時(shí),就變得非常困難。

舉個經(jīng)典的購物例子,成功售出后,需要確保以下三步被正確地處理:

1.減去用戶賬號中的錢,修改用戶余額。

UPDATE user_money SET balance = balance - 100 WHERE user_id = 123 AND balance >= 100;

2.修改庫存的數(shù)量,然后把商品狀態(tài)標(biāo)為待配送。

UPDATE goods_info SET stock = stock - 1, status = '待配送' WHERE id = 4;

3.商家的賬號增加錢。

UPDATE user_wallet SET balance = balance + 100 WHERE user_id = 2;

這里我們要區(qū)分兩種場景:

場景一:

本地事務(wù)。如果這三個表恰好都在同一個數(shù)據(jù)庫實(shí)例里,那事情就很簡單,使用本地事務(wù)包裹這三條 SQL 即可。

場景二:

分布式事務(wù)。如果是微服務(wù)架構(gòu),用戶庫、商品庫和商家錢包庫通常是物理隔離的。此時(shí)簡單的本地事務(wù)就無能為力了,需要引入 TCC、Saga 等方案。

這一次我們主要聚焦在第一種場景——本地事務(wù),看看數(shù)據(jù)庫是如何在底層實(shí)現(xiàn)它的。

2.數(shù)據(jù)庫是如何實(shí)現(xiàn)原子性和持久性的?

原子性和持久性在事務(wù)里是密切相關(guān)的兩個屬性:

  • 原子性保證了事務(wù)的多個操作要么都生效,要么都不生效,不會存在中間狀態(tài)。
  • 持久性保證了一旦事務(wù)生效,就不會再因?yàn)槿魏卧蚨鴮?dǎo)致其修改的內(nèi)容被撤銷或丟失。也就是說,數(shù)據(jù)必須要成功寫入磁盤后才能擁有持久性。

但是實(shí)現(xiàn)它們也有困難,因?yàn)?strong>寫入磁盤這個操作不是原子的,不僅有寫入與未寫入,還存在著“正在寫”的中間狀態(tài)。接下來我們結(jié)合代碼代入使用場景進(jìn)一步體會:

using (var scope = new TransactionScope())
{
    using (var conn = new SqlConnection(ConnectionString))
    {
        conn.Open();
        string sql1 = "UPDATE user_money SET balance = balance - 100 WHERE user_id = 123 AND balance >= 100";      // 1. 減去用戶賬號中的錢
        ExecuteNonQuery(conn, sql1);
        string sql2 = "UPDATE goods_info SET stock = stock - 1, status = '待配送' WHERE id = 4";  // 2. 修改庫存數(shù)量,并把商品狀態(tài)標(biāo)為待配送
        ExecuteNonQuery(conn, sql2);
        string sql3 = "UPDATE user_wallet SET balance = balance + 100 WHERE user_id = 2";  // 3. 商家的賬號增加錢
        ExecuteNonQuery(conn, sql3);
    }
    scope.Complete(); //關(guān)鍵分水嶺
}

?? 崩潰場景推演

  • 未提交事務(wù)崩潰:如果程序還沒走到 scope.Complete(),數(shù)據(jù)庫已經(jīng)將其中一個數(shù)據(jù)的變動寫入了磁盤,此時(shí)服務(wù)器崩潰。重啟之后,數(shù)據(jù)庫必須想辦法得知崩潰前發(fā)生過一次不完整的操作,將已經(jīng)修改過的數(shù)據(jù)恢復(fù)成原來的樣子,以保證原子性
  • 已提交事務(wù)崩潰:如果已經(jīng)執(zhí)行了 scope.Complete(),程序日志記錄了成功,但數(shù)據(jù)庫還未將全部三個數(shù)據(jù)的變動都寫入到磁盤,此時(shí)服務(wù)器崩潰。重啟之后,數(shù)據(jù)庫也必須要有辦法得知崩潰前發(fā)生過一次完整的操作,將還沒來得及寫入磁盤的那部分?jǐn)?shù)據(jù)重新寫入,以保證持久性。

為了解決這些問題,數(shù)據(jù)庫界探索出了不同的策略。

?? 策略一:Commit Logging(追加日志)

這種思路比較簡單:先把我要做的事情記錄到磁盤中(比如修改什么數(shù)據(jù)、從什么值改成什么值),寫完了日志用一個特殊狀態(tài)來標(biāo)記下,然后再修改數(shù)據(jù)。

流程大致如下:

修改xxxx放到第15頁 (Update Log)
修改xxx放到第39頁 (Update Log)
Commit Record 
開始在后臺真實(shí)改第15頁的那個數(shù)據(jù)
開始在后臺改第39頁的那個數(shù)據(jù)
改完了寫一條 
End record

當(dāng) Complete 命令執(zhí)行時(shí),數(shù)據(jù)庫首先將 Commit Record 寫入日志文件并強(qiáng)制刷盤。此時(shí)真實(shí)數(shù)據(jù)文件尚未被修改,僅日志中記錄了事務(wù)已準(zhǔn)備好提交的狀態(tài)。

  • 保證持久性:如果在修改真實(shí)數(shù)據(jù)時(shí)崩潰,重啟后根據(jù)已經(jīng)寫入磁盤的日志信息恢復(fù)現(xiàn)場、繼續(xù)修改數(shù)據(jù)即可。
  • 保證原子性:如果 End record 日志沒有寫入成功就發(fā)生崩潰,系統(tǒng)重啟后會看到一部分沒有 Commit Record 的日志,那將這部分日志標(biāo)記為回滾狀態(tài)即可。

?? 策略二:WAL 機(jī)制(Write-Ahead Logging)

Commit Logging 存在一個巨大的缺陷:性能瓶頸。所有對數(shù)據(jù)的真實(shí)修改都必須發(fā)生在事務(wù)提交、日志寫入了 Commit Record 之后。假設(shè)一個大型事務(wù)執(zhí)行了 10 秒鐘,這 10 秒內(nèi)產(chǎn)生的大量數(shù)據(jù)修改只能堆積在內(nèi)存里。直到第 10 秒末提交時(shí),數(shù)據(jù)庫才必須手忙腳亂地把積攢的數(shù)據(jù)一股腦地往磁盤上寫。這就導(dǎo)致了平時(shí)磁盤閑得發(fā)慌,提交時(shí)磁盤忙到卡死。有沒有辦法解決呢?我們先看兩個專業(yè)定義:

FORCE:當(dāng)事務(wù)提交后,要求變動數(shù)據(jù)必須同時(shí)完成寫入。

NO-FORCE:不強(qiáng)制變動數(shù)據(jù)必須同時(shí)完成寫入。

STEAL:在事務(wù)提交前,允許變動數(shù)據(jù)提前寫入。

NO-STEAL:不允許在事務(wù)提交前提前寫入。

大白話理解就是:立馬寫磁盤叫 FORCE,反之 NO-FORCE;偷偷寫點(diǎn)叫 STEAL,不準(zhǔn)偷偷寫叫 NO-STEAL。

Commit Logging 允許 NO-FORCE,但不允許 STEAL。因?yàn)榧偃缡聞?wù)提交前就有部分變動數(shù)據(jù)寫入磁盤,那一旦事務(wù)要回滾,這些提前寫入的變動數(shù)據(jù)就都成了錯誤。

現(xiàn)代數(shù)據(jù)庫(如 MySQL InnoDB)采用了 WAL 機(jī)制,它既允許 NO-FORCE,也允許 STEAL。為了實(shí)現(xiàn)這一點(diǎn),它引入了兩種關(guān)鍵日志:

  • 1.Undo Log(回滾日志):為了支持 STEAL。在數(shù)據(jù)提前寫入磁盤前,先記錄修改前的舊值。一旦事務(wù)回滾或崩潰,就利用它來擦除那些提前寫入的“臟數(shù)據(jù)”,保證原子性。
  • 2.Redo Log(重做日志):為了支持 NO-FORCE。在數(shù)據(jù)被修改前,先強(qiáng)制將物理修改記錄刷入磁盤。一旦提交后發(fā)生崩潰,就利用它來重放那些還沒來得及寫入磁盤的數(shù)據(jù),保證持久性。

組合角度分析:

  • STEAL + NO-FORCE:允許偷偷寫(需 Undo Log 當(dāng)后悔藥),允許提交后不實(shí)時(shí)寫(需 Redo Log 當(dāng)記事本)。這是現(xiàn)代數(shù)據(jù)庫的主流選擇。
  • NO-FORCE + NO-STEAL:不允許偷寫,允許提交后不實(shí)時(shí)寫,所以只需要 Redo Log。
  • FORCE + NO-STEAL:沒偷寫,而且強(qiáng)制寫磁盤,所以不需要日志(性能最差)。

3.總結(jié)

我們可以得出以下結(jié)論:

1. 為什么需要 NO-FORCE 與 Redo Log?

絕大多數(shù)數(shù)據(jù)庫都采用 NO-FORCE 策略來優(yōu)化 I/O 性能。為了實(shí)現(xiàn)它,就需要引入 Redo Log。即使修改數(shù)據(jù)時(shí)系統(tǒng)崩潰了,重啟后數(shù)據(jù)庫可以根據(jù) Redo Log 恢復(fù)現(xiàn)場。

通俗理解:我先把要改的東西記錄在日志里,再根據(jù)日志統(tǒng)一寫到磁盤中。萬一我在寫入磁盤的過程中“暈倒”(宕機(jī))了,等我“醒來”(重啟)的時(shí)候,照著日志重新做一遍,也能成功。

2. 為什么需要 STEAL 與 Undo Log?
為了進(jìn)一步提升性能,允許在事務(wù)提交前“偷偷地”先寫一點(diǎn)數(shù)據(jù)到磁盤(STEAL)。但這帶來了新問題:萬一事務(wù)回滾,這些提前寫入的數(shù)據(jù)就變成了“臟數(shù)據(jù)”。這就需要引入 Undo Log,在偷摸寫入數(shù)據(jù)之前記錄舊值,以便隨時(shí)恢復(fù)。

3. Binlog 與 SQL Server 的事務(wù)日志

最后額外說一下,在 MySQL 中還有一個 Binlog 的概念。它和 Redo Log 所處的層面不一樣:

  • Redo Log:處于 InnoDB 存儲引擎層,主要負(fù)責(zé)底層的崩潰恢復(fù)。
  • Binlog:處于 MySQL Server 層,主要負(fù)責(zé)主從復(fù)制和數(shù)據(jù)恢復(fù)。

為了保證 Binlog 與 InnoDB 引擎層的 Redo Log 之間的數(shù)據(jù)一致,MySQL 引入了 2PC(兩階段提交) 機(jī)制。而在 SQL Server 中,也有差不多的概念,叫做事務(wù)日志(Transaction Log),它的功能更廣,同時(shí)承擔(dān)了類似 Redo Log 和 Undo Log 的職責(zé)。

到此這篇關(guān)于Mysql事物的持久性及原子性的文章就介紹到這了,更多相關(guān)mysql持久性和原子性內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • mysql 索引合并的使用

    mysql 索引合并的使用

    索引合并是mysql底層為我們提供的智能算法。本文就介紹了mysql 索引合并的使用,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • MySQL無法存儲emoji表情解決方案分析

    MySQL無法存儲emoji表情解決方案分析

    這篇文章主要介紹了MySQL無法存儲emoji表情解決方案,結(jié)合實(shí)例形式分析了Python爬蟲爬取文章中emoji表情存入數(shù)據(jù)庫的實(shí)現(xiàn)方法,涉及mysql utf8mb4編碼的修改相關(guān)操作技巧,需要的朋友可以參考下
    2018-07-07
  • Myeclipse連接mysql數(shù)據(jù)庫心得體會

    Myeclipse連接mysql數(shù)據(jù)庫心得體會

    這篇文章主要為大家詳細(xì)介紹了MyEclipse連接MySQL數(shù)據(jù)庫圖文教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • mysql一對多關(guān)聯(lián)查詢分頁錯誤問題的解決方法

    mysql一對多關(guān)聯(lián)查詢分頁錯誤問題的解決方法

    這篇文章主要介紹了mysql一對多關(guān)聯(lián)查詢分頁錯誤問題的解決方法,非常不錯,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-09-09
  • MySQL如何選擇合適的索引

    MySQL如何選擇合適的索引

    這篇文章主要介紹了MySQL如何選擇合適的索引,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-09-09
  • MySQL?時(shí)區(qū)與?serverTimezone詳解

    MySQL?時(shí)區(qū)與?serverTimezone詳解

    存儲?TIMESTAMP?類型數(shù)據(jù)時(shí),MySQL 會根據(jù)當(dāng)前會話的時(shí)區(qū)將時(shí)間轉(zhuǎn)換為 UTC 時(shí)間,MySQL 實(shí)際存儲的是 UTC 時(shí)間,這篇文章主要介紹了MySQL?時(shí)區(qū)與?serverTimezone,需要的朋友可以參考下
    2024-12-12
  • MySQL數(shù)據(jù)庫高級查詢和多表查詢

    MySQL數(shù)據(jù)庫高級查詢和多表查詢

    這篇文章主要介紹了MySQL數(shù)據(jù)庫高級查詢和多表查詢,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • 深入mysql主從復(fù)制延遲問題的詳解

    深入mysql主從復(fù)制延遲問題的詳解

    本篇文章是對mysql中主從復(fù)制延遲的問題進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • MySQL中列轉(zhuǎn)行和行轉(zhuǎn)列總結(jié)解決思路

    MySQL中列轉(zhuǎn)行和行轉(zhuǎn)列總結(jié)解決思路

    最近工作中用到了好幾次列轉(zhuǎn)行,索性做個小總結(jié),下面這篇文章主要給大家介紹了關(guān)于MYSQL如何列轉(zhuǎn)行的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • MySQL批量處理圖片URL統(tǒng)一去掉域名前綴的方法

    MySQL批量處理圖片URL統(tǒng)一去掉域名前綴的方法

    文章介紹了如何使用MySQL 8.0的新函數(shù)REGEXP_REPLACE()批量處理圖片URL,去掉域名前綴,將所有圖片路徑統(tǒng)一存為相對路徑,從而避免路徑重復(fù)和加載錯誤,需要的朋友可以參考下
    2025-11-11

最新評論

靖远县| 灵璧县| 孝感市| 汉寿县| 留坝县| 旌德县| 中山市| 海原县| 青田县| 青川县| 陆良县| 疏附县| 安国市| 尼玛县| 闻喜县| 天气| 阿拉尔市| 苍溪县| 郯城县| 登封市| 连江县| 榕江县| 来宾市| 舞阳县| 鄄城县| 新干县| 渝北区| 宿松县| 慈溪市| 尼木县| 咸宁市| 启东市| 维西| 阆中市| 合江县| 富川| 无棣县| 石阡县| 四子王旗| 庐江县| 桐城市|