MySQL是如何實現事務的隔離級別

来源:https://www.cnblogs.com/yousheng/archive/2020/05/23/12944218.html
-Advertisement-
Play Games

摘要 本文旨在瞭解MySQL InnoDB引擎如何支持事務的隔離級別。 文章主要內容分兩個部分。 第一部分闡述資料庫的併發問題以及為之產生的ANSI SQL 標準隔離級別。 第二部分根據 MySQL 官方文檔解釋 InnoDB 是如何支持這些隔離級別的。 資料庫事務的併發問題 ANSI SQL 隔離 ...


摘要

本文旨在瞭解MySQL InnoDB引擎如何支持事務的隔離級別。
文章主要內容分兩個部分。
第一部分闡述資料庫的併發問題以及為之產生的ANSI SQL 標準隔離級別。
第二部分根據 MySQL 官方文檔解釋 InnoDB 是如何支持這些隔離級別的。

資料庫事務的併發問題

ANSI SQL 隔離級別的定義是來源於三個異象問題,ANSI SQL在權衡系統的可靠性和性能之間定義了不同的級別。所以這裡先介紹主流的三個併發問題是什麼。

讀異象 (read phenomena)

  • 臟讀 (dirty read)
    • 一個事務讀取到了另一個事務更新但未提交的數據。當另一個事務回滾或者再次修改,就會讀取到臟數據
  • 不可重覆讀 (non-repeatable read / Fuzzy Read)
    • 現象為一個事務多次同樣讀取數據的操作其結果不一致。例如讀取時有其他事務提交了修改
  • 幻讀 (phantom)
    • 在多次同樣的查詢操作下,後面的查詢出現了新行的數據。
    • 例如在執行多次查詢的時候,其他事務插入了一個新行或者修改了某行數據使得能匹配上Where條件,那麼後一次查詢必然將查詢到這個新數據(也就感覺出現了幻覺,莫名其妙多出了一行)
    • 不可重覆讀和幻讀其實類似,但是幻讀偏向於查詢新增數據(所以專門弄了間隙鎖來防止幻讀),不可重覆讀則是修改數據。

    Def: The so-called phantom problem occurs within a transaction when the same query produces different sets of rows at different times. For example, if a SELECT is executed twice, but returns a row the second time that was not returned the first time, the row is a “phantom” row.

    • 註:幻象 (Phantom) ,幻讀 (Phantom Read) ,幻象問題 (Phantom Problem) 都是一個表述。
  • 總結:上面介紹的三個讀併發問題,其本質都是一個事務在讀其他事務修改過的數據。

標準事務隔離級別

  • 因為事務在併發執行的過程中會存在相互干擾,需要有隔離性的保障,故引入事務隔離級別規範,以平衡操作的性能、可靠和一致

ANSI SQL下規定的隔離級別(1992 - 很老的標準了)

  1. 未提交讀 - Read Uncommited
    • 風險挺高,但是如果只是存粹的讀操作可以推薦使用(MyISAM也挺香呀)
  2. 已提交讀 - Read Commited(互聯網主流預設使用的隔離級別)
    • 事務無法看見其他未提交事務的修改
  3. 可重覆讀 - Repeatable Read (MySQL 預設)
    • 只讀事務開始時的快照數據
  4. 可序列化 - Serializable
  • ANSI SQL規定的隔離級別其實還是根據主流存在的資料庫併發問題來定義的,每個隔離級別來解決不同的問題。
    如果細分繼續細分不同的併發問題,隔離級別還能更加細化。
  • ANSI SQL Isolation Levels Defined in terms of the Three Original Phenomena

MySQL是如何支持不同隔離級別的

知識準備(術語解釋)

  • 一致性讀取 (consistent read)
    • 事務在進行讀操作時,使用的是事務開始時的行快照數據,這樣就不用擔心讀到其他其他事務修改的數據。
    • 在[可重覆讀]下,事務快照是基於第一次讀操作的快照(通過undo log 回溯)
    • 在[可提交讀]下,每一次一致性讀操作都會重置快照
    • 優點:不上鎖,允許其他事務進行修改
  • 半一致性讀取 (semi-consistent read)
    • UPDATE語句中的讀/匹配操作,當UPDATE語句執行的時候,InnoDB會取最後一次提交到MySQL的數據來進行 Where 子句中的匹配。
      • 如果匹配上了(也就是要更新),那就重讀該行並加鎖(或等待加鎖)
        • todo 為什麼要重讀該行不是很理解,直接用那條數據不就好了
    • 僅用於[可提交讀]隔離級別
    • 個人理解其實就是一致性讀取在[可提交讀]隔離級別下 UPDATE的表現
  • 鎖定讀 (locking read)
    • 即加鎖的查詢語句
    • E.g SELECT ... FOR UPDATE | SELECT ... FOR SHARE
  • 行鎖 (record lock) : 即鎖定索引記錄的鎖,即使沒有索引也會找到對應行記錄鎖主鍵哦
  • 間隙鎖 (gap lock) : 鎖在了兩條索引記錄之間的鎖,或者(無窮小,某索引)/(某索引,無窮大),他們鎖住的是一個範圍,且不同的間隙鎖不互斥,他們排斥的只是在鎖範圍內的插入操作
    • mark: R.C 隔離級別下是被禁用的
  • next-key lock : 行鎖和間隙鎖的組合實現

未提交讀 - Read Uncommited

  • SELECT 語句採用的無鎖策略,也就更容易產生臟讀

已提交讀 - Read Commited

  • 該隔離級別下的一致性讀取,讀的都是最新版的快照,每次讀快照都會被重置成最新

    Each consistent read, even within the same transaction, sets and reads its own fresh snapshot.

  • 對於Locking Reads、UPDATE、DELETE 語句,InnoDB的鎖策略是只鎖索引記錄,並沒有用間隙鎖來鎖範圍,所以容易產生幻讀問題

特別的對於 R.C 隔離級別有以下變動

  • 對於UPDATE語句,如果行已經被加鎖了,InnoDB會使用 “semi-consistent”read,來讀取快照中最新的值(而不是最原始的快照)來進行 WHERE 匹配更新。(也就是說 UPDATE 也採用 R.C 特供版一致性讀取)
  • 對於UPDATE或 DELETE語句,InnoDB僅對其更新或刪除的行持有鎖。MySQL評估WHERE條件後,將釋放不匹配行的記錄鎖。這大大降低了死鎖的可能性。
    • 比如掃表過程中,掃到這行不是要修改的,那麼就釋放鎖,要改的就一直加鎖直到事務結束
    • R.R 級別下則不會釋放

關於這個“semi-consistent” read

  • 他讀取的是最後一次提交到MySQL的版本,而不是快照中最早讀的那個版本
  • 優點:MySQL可以判斷當前的更新操作是否符合Where條件,如果不匹配就不用更新了,也就節省了一次寫操作,如果匹配就重新讀取嘗試加鎖
  • 缺點:如果都採用一致性讀consistent read,讀取都是最早事務的快照,就不會產生幻象問題了(Phantom Problem);但是採用了半一致性,兩次查詢的結果可能會不一致。

可重覆讀 - Repeatable Read

  • 採用一致性讀取,同事務中的每次讀取都取第一次讀的快照。(R.R 版一致性讀取)

    Consistent reads within the same transaction read the snapshot established by the first read.

  • 對於 locking reads,UPDATE,DELETE ,其加鎖策略取決於是否是唯一索引唯一條件查詢
    • 唯一索引配合唯一查詢條件,引擎只鎖定那條索引記錄,不鎖間隙
    • 其他場景,引擎使用Gap Lock 或 Next-Record Lock來鎖定掃描的索引範圍,以阻止其他事務插入新行到該間隙

串列化 - Serializable

  • InnoDB 默默的把所有純 SELECT 語句都轉成了 SELECT ... FOR SHARE ,也就預設都加讀鎖

小結

  • InnoDB 是依賴於不同的鎖策略實現了不同隔離級別的要求
  • R.C 隔離級別使用了當前讀 (R.C 下的一致性讀取) 且 禁用了間隙鎖
  • 理論上 R.R 隔離級別下因為使用了一致性讀和間隙鎖是不會產生的幻讀問題的,所以標準可能有點老了
    • todo MySQL在介紹幻讀的時候說 R.R 下麵會有幻讀,但又沒有實際的例子,很難讓人信服。(除非這個也算:兩次查詢前一次是普通讀,後一次是鎖定讀)

擴展

  • 在介紹 ANSI 隔離級別標準的時候,提到了ANSI標準是根據主流的三個併發問題來對症下藥的,但其實仍然有一些併發問題被 ANSI 標準給預設忽略或者說就不關心了,比如典型的臟讀和丟失修改。更詳細的可以參考下圖(1995)
    • Isolation Types Characterized by Possible Anomalies Allowed.

參考文獻


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 存儲過程概述 存儲過程是完成特定功能的一組SQL語句,它是資料庫的一種可編程對象,類似於函數 其快速、靈活、安全 系統存儲過程 SQL sever 內置的存儲過程,存儲在master庫中,主要用於執行SQL sever的某些功能、顯示有關資料庫和用戶的信息。系統存儲過程名以sp_開頭,可以在任何數據 ...
  • List<Recipe> randomRecipe(@Param("str")String[] strs,@Param("num")int num); 功能:隨機查詢num個不含strs[]的的Recipe 註:str:["蘋果","甘藍"] num:5 <select id="randomReci ...
  • T-SQL 基本語句 註釋:單行註釋: -- 多行註釋:/* ..... */ 定義批處理結束語句:go 輸出語句:print(表達式) 數據類型 變數 運算符 函數 4.轉換函數 cast(表達式 as 數據類型):將某種數據類型的表達式顯示轉換為另一種數據類型 convert(數據類型[(長度) ...
  • sysdatabases表 sysdatabases是在master資料庫中的表,是一個只讀的表,裡面包含所有資料庫的信息 在創建資料庫是進行存在性檢測 use master go if exists(select * from sysdatabases where name='資料庫名') dro ...
  • 視圖:只有結構沒有數據 視圖是在數據表基礎上定義的一個虛擬表,在打開視圖是從數據表提取查詢結果 一個視圖是從特定的角度查看資料庫的方式、 限定不同用戶能看到的資料庫的範圍 維護數據安全的一種方式 視圖可以修改資料庫里的數據 視圖中只負責存儲select命令,不保存數據,不占據物理物理空間 創建視圖 ...
  • 將查詢結果生成新表 在同一資料庫中 select <表達式列表> into <新表名> form 原來的表 select * into 新表名 form 原來的表 基於多表查詢的DML update 表名 set 欄位=‘’ where 子查詢update 表名 set 欄位=‘’ from 表1 ...
  • 基本數據檢索:單表 複雜數據檢索:多表:連接查詢、子查詢(嵌套查詢)、集合運算 基本select語句: select <檢索欄位> from <表> where <檢索條件> group by<分類> having<檢索條件> order by <排序欄位> 操縱列: 1.輸出所有列:select ...
  • 事務的使用方式 事務的錯誤處理 WATCH命令 生存時間 緩存策略 Redis中的事務(transaction)是一組命令的集合。事務同命令一樣都是Redis的最小執行單位,一個事務中的命令要麼都執行,要麼都不執行。 事務的原理是先將屬於一個事務的命令發送給Redis,然後再讓Redis依次執行這些 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...