跟面試官侃半小時MySQL事務隔離性,從基本概念深入到實現
提到MySQL的事務,我相信對MySQL有了解的同學都能聊上幾句,無論是面試求職,還是日常開發,MySQL的事務都跟我們息息相關。
而事務的ACID(即原子性Atomicity、一致性Consistency、隔離性Isolation、永續性Durability)可以說涵蓋了事務的全部知識點,所以,我們不僅要知道ACID是什麼,還要了解ACID背後的實現,只有這樣,無論在日常開發還是面試求職,都能無往而不利。
為了大家更好的閱讀體驗,對ACID的深入分析將分為上下兩篇。
本篇為上篇,主要圍繞ACID中的I,也就是“隔離性”展開,從基本概念,到隔離性的實現,最後以一個實戰案例進行融會貫通。
嗯,看完你都能理解,那跟面試官侃半小時隔離性就沒問題了。
1.事務隔離性的基本概念
1.1 什麼是ACID中的Isolation,隔離性
Isolation,隔離性,也有人稱之為併發控制(concurrency control)。事務的隔離性要求每個事務讀寫的物件對其他事務都是相互隔離的,也就是這個事務提交前,這個事務的修改內容對其他事務都是不可見的。事務的隔離性,主要是解決不同事物之間的相互讀寫影響。
所謂的讀寫影響注意分為三種:
- 髒讀:讀到了別的事務尚未提交(commit)的變更,別人沒提交,我讀到了。
- 不可重複讀:別的事務提交了變更,被當前事務讀到了。然後導致本事務多次select的結果不一樣,讀到了別的事務提交的內容。
- 幻讀:也是讀到了別的事務提交的內容,但是跟上面的不同之處在於,讀到了原本不存在的記錄。
注意,不可重複讀,主要是讀到了別的事務update的內容。而幻讀,是讀到了別的事務insert的內容。
1.2 隔離性的隔離級別
為了解決事務隔離性的問題,資料庫一般會有不同的隔離級別來解決相應的讀寫影響。
- 讀未提交:一個事務B還沒提交,它的修改就被別的事務A讀到了。
- 讀已提交:一個事務B提交後,它的修改被其他事務A看到了。
- 可重複讀:一個事物B提交前和提交後,事務A都無法讀到事務B的變更。
- 序列化:對同一行記錄,當出現不同事物的讀寫衝突時,是通過序列化的方式解決的,後一個事務必須等前一個事務完成才能執行。
不同隔離級別能夠解決不同的隔離性問題。
需要注意的是,這是標準事務隔離級別的定義。在MySQL的innodb引擎中,在可重複讀級別下,通過mvcc解決了幻讀的問題,具體實現我們後面再講。
同時,需要注意的是,到目前為止,我們說的讀,都是”快照讀”,普通的select。後面我們還會提到“當前讀”,是不一樣的哦。
2.事務隔離性的實現
要實現事務的隔離性,需要了解兩個方面的內容,一個是鎖,一個是多版本併發控制(MVCC)。
2.1 事務的行鎖
InnoDB中,實現了兩種標準的行級鎖:
- 共享鎖(S Lock),也叫讀鎖,允許事務讀取一行資料。
- 排它鎖(X Lock),也叫寫鎖,允許事務刪除或者更新一行資料(注意,這裡沒有提到插入哦,插入涉及到幻讀,可以看文章最後的說明)
普通select語句不會有任何鎖,那麼如何獲得共享鎖和排它鎖呢?
- Select … lock in share mode語句能夠獲得共享鎖
- Select … for update(特殊的select,用mysql簡單實現分散式鎖經常用它)、Update、delete語句能夠獲得排它鎖
當一個事務A已經獲得了行r的共享鎖,那麼另一個事務B可以立刻獲得行r的共享鎖,因為不會改變r的數值,這種叫做鎖相容。
如果這時候有事務C希望獲得行r的排它鎖,那麼就必須等待事務A和事務B釋放行r的共享鎖之後,才能獲得排它鎖,這種叫做鎖不相容。
普通的select不會對行上鎖,而select…lock in share mode會上共享鎖,select…for update會上排它鎖。
- 對於普通的select的讀取方式,稱為”快照讀“,也叫”一致性非鎖定讀“。
- 對於帶鎖的select讀取,或者update tb set a = a+1(讀取a的當前值),稱為“當前讀”,也叫“一致性鎖定讀”。
如果在update、insert的時候,不能進行select,那麼服務的併發訪問效能就太差了。因此,我們日常的查詢,都是“快照讀”,不會上鎖,只有在update\insert\“當前讀”的時候,才會上鎖。而為了解決“快照讀”的併發訪問問題,就引入了MVCC。
2.2 多版本併發控制MVCC
如果說上面的行鎖是一種悲觀鎖,那麼MVCC就是一種樂觀鎖的實現方式,而且是一種很常用的樂觀鎖實現方式。
所謂多版本,就是一行記錄在資料庫中儲存了多個版本,每個版本以事務ID作為版本號。InnoDB 裡面每個事務有一個唯一的事務 ID,是在事務開始的時候向InnoDB的事務系統申請的,並且按照申請順序嚴格遞增的。假如一行記錄被多個事務更新,那麼,就會產生多個版本的記錄。
以某一行資料作為例子:
經過兩次事務的操作,value從22變成了19,同時,保留了三個事務id,15、25、30。
在每個記錄多版本的基礎上,需要利用“一致性檢視”,來做版本的可見性判斷。
這裡,我們要區分MySQL裡面的兩個”檢視”概念:
- 一個是view,通過語法create view … 實現,主要建立一個虛擬表,用來執行查詢語句。
- 一個是InnoDB用來實現mvcc的一致性檢視(consistent read view),純邏輯概念,沒有物理結構,定義了在事務期間,你能看到哪些版本的資料。
我們全文提到的“檢視”都是第二種,主要是支援InnoDB在“讀已提交”和“可重複讀”級別的併發訪問問題。
- “讀未提及”級別下,沒有一致性檢視
- “讀已提交”級別下,會在 每個SQL開始執行的時候 建立一致性檢視
- “可重複讀”級別下,會在 每個事務開始的時候 建立一致性檢視
- “序列化”級別下,直接通過加鎖避免併發問題
下面,我們簡單介紹一下建立一致性檢視的邏輯。
以“可重複讀”級別為例。
- 當一個事務開啟的時候,會向系統申請一個新事務id
- 此時,可能還有多個正在進行的其他事務沒有提交,因此在瞬時時刻,是有多個活躍的未提交事務id
- 將這些未提交的事務id組成一個數組,數組裡面最小的事務id記錄為低水位,當前系統建立過的事務id的最大值+1記錄為高水位
- 這個陣列array 和 高水位,就組成了“一致性檢視”。
有了一致性檢視後,我們就可以判斷一行資料的多版本可見性了,無論是“讀已提交”還是“可重複讀”級別,可見性判斷規則是一樣的,區別在於建立快照(一致性檢視)的時間。
在當前事務中,讀取其他某一行的記錄,對其中的版本號的可見性判斷有五種情況(建議自己跟著捋一捋,挺重要的):
- 如果版本號小於“低水位”,說明事務已經提交,那肯定 可見;
- 如果版本號大於“高水位”,說明這行資料的這個事務id版本是在快照後產生的,那肯定 不可見;
- 如果版本號在事務陣列array中,說明這個事務還沒提交,所以 不可見;
- 如果版本號不在事務陣列array中,且低於高水位,說明這個事務已經提交,所以 可見;
- 當然,無論什麼時候,自己的事務id中的任何變化,都是可見的
可以看看下面這個例子,更容易理解。
系統建立過的事務id:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
事務A啟動,拍個快照
此時未提交的事務id有:7,8,9
一致性檢視:陣列array[7,8,9] + 高水位16(15+1)
對於任意一行資料的可見性判斷如下:
1)小於7的,可見
2)大於16的,說明是快照後產生的,不可見
3)10-15,不在陣列array中,說明已經提交了,可見
4)7,8,9在array中,說明未提交,不可見
兩個重要結論:
- InnoDB 利用了“所有資料都有多個版本”的這個特性,實現了“秒級建立快照”的能力。
- MVCC的實現,就是根據當前事務的事務id為依據建立“一致性檢視”,利用一致性檢視來判斷資料版本的可見性。
3.隔離性實戰
下面,我們來兩個實戰案例,將上面的基礎概念與實現融會貫通吧。
1)併發select&update 案例
id=1 的value初始為1。
我們看下,在不同隔離級別,Time5、Time7、Time9事務A查詢到的value 分佈為多少。
- “讀未提交”:2,2,2
- “讀以提交”:1,2,2
- “可重複讀”:1,1,2
- 序列化:1,1,2(注意,這裡在事務A提交前,事務B都會阻塞,直到事務A提交後才能執行)
2)併發update案例
id=1 的value初始為1,在可重複讀級別:
我們看一下,你猜猜事務A和事務B讀取的value是多少?
答案是:1 和 3
可能會產生困惑,事務A在啟動後快照,所以讀到了1是正常的,但是事務2在啟動的時候快照了,然後在自己的事務中+1,怎麼會讀到3而不是2呢?
原因很簡單,即使是在可重複讀的級別,事務 更新資料 的時候,只能用當前讀(想想也能理解,不然update就出現資料不一致了)。
如果當前的記錄的行鎖被其他事務佔用的話,就需要進入鎖等待。而讀提交的邏輯和可重複讀的邏輯類似,它們最主要的區別是:在可重複讀隔離級別下,只需要在事務開始的時候建立一致性檢視,之後事務裡的其他查詢都共用這個一致性檢視;在讀提交隔離級別下,每一個語句執行前都會重新算出一個新的檢視。
這裡,我們需要注意的是事務的啟動時機。
- begin/start transaction 命令並不是一個事務的起點,在執行到它們之後的第一個操作 InnoDB 表的語句,事務才真正啟動,一致性檢視是在執行第一個快照讀語句時建立的。
- 如果你想要馬上啟動一個事務,可以使用 start transaction with consistent snapshot 這個命令,一致性檢視是在執行 start transaction with consistent snapshot 時建立的。
4.關於幻讀
首先明確一下,什麼是幻讀?開篇介紹了什麼是幻讀,這裡再申明一下幻讀出現的場景
- 第一:事務的隔離級別為可重複讀,且是當前讀
- 第二:幻讀僅專指新插入的行,在範圍查詢中,後一次查詢出現了新的資料行。
前文已經提到了,對於普通資料庫,需要到可序列化的隔離級別才能解決幻讀問題。
而對於InnoDB儲存引擎來說,在可重複讀級別下就能解決幻讀問題。
InnoDB儲存引擎有三種行鎖演算法:
- 行鎖:當個行記錄上的鎖
- 間隙鎖:Gap Lock,鎖定一個範圍,但不包含記錄本身
- Next-Key Lock:就是行鎖+間隙鎖,同時鎖上一個範圍,並且鎖定記錄本身
InnoDB就是通過Next-Key Lock解決了幻讀的問題,具體內容可以看我之前的文章: 前阿里資料庫專家總結的MySQL裡的各種鎖(下篇)
看到這裡了,原創不易,點個關注、點個贊吧,你最好看了~
知識碎片重新梳理,構建Java知識圖譜:https://github.com/saigu/JavaKnowledgeGraph(歷史文章查閱非常方便)
掃碼關注我的公眾號“阿丸筆記”,第一時間獲取最新更新。同時可以免費獲取海量Java技術棧電子書、各個大廠面試題。
相關推薦
跟面試官侃半小時MySQL事務隔離性,從基本概念深入到實現
提到MySQL的事務,我相信對MySQL有了解的同學都能聊上幾句,無論是面試求職,還是日常開發,MySQL的事務都跟我們息息相關。 而事務的ACID(即原子性Atomicity、一致性Consistency、隔離性Isolation、永續性Durability)可以說涵蓋了事務的全部知識點,所以,我們不僅要知
跟面試官侃半小時MySQL事務,說完原子性、一致性、永續性的實現
提到MySQL的事務,我相信對MySQL有了解的同學都能聊上幾句,無論是面試求職,還是日常開發,MySQL的事務都跟我們息息相關。 而事務的ACID(即原子性Atomicity、一致性Consistency、隔離性Isolation、永續性Durability)可以說涵蓋了事務的全部知識點,所以,我們不僅要知
騰訊面試居然跟我扯了半小時的CountDownLatch
一個長頭髮、穿著清爽的小姐姐,拿著一個嶄新的Mac筆記本向我走來,看著來勢洶洶,我心想著肯定是技術大佬吧!但是我也是一個才華橫溢的人,穩住我們能贏。 ![面試官](https://img-blog.csdnimg.cn/20200509090547451.jpeg#pic_center) > **面試官**
頭條面試居然跟我扯了半小時的Semaphore
一個長頭髮、穿著清爽的小姐姐,拿著一個嶄新的Mac筆記本向我走來,看著來勢洶洶,我心想著肯定是技術大佬吧!但是我也是一個才華橫溢的人,穩住我們能贏。 ![](https://img-blog.csdnimg.cn/20200607091134202.png#pic_center) > **面試官**:看你
一個HashMap能跟面試官扯上半個小時
### 一個HashMap能跟面試官扯上半個小時 > 《安琪拉與面試官二三事》系列文章 > [一個HashMap能跟面試官扯上半個小時](https://blog.csdn.net/zhengwangzw/article/details/104889549) > [一個synchroniz
## 【分散式事務】面試官問我:MySQL中的XA事務崩潰瞭如何恢復??
## 寫在前面 > 前段時間搭建了一套MySQL分散式資料庫叢集,資料庫節點有12個,用來測試各種分散式事務方案的效能和優缺點。測試MySQL XA事務時,正當測試指令碼向資料庫中批量插入資料時,強制伺服器斷電!注意:是直接拔電源,使其瞬間斷電,再次重啟伺服器後,MySQL資料庫報錯了。特此記錄MyS
跟面試官講Binder(二)之關於AIDL的認識
面試官開口說:“聽你剛才所說,在Android系統中,都是利用Binder來進行程序間通訊的,那我怎麼聽說,還有利用AIDL來實現程序間通訊的呢?”。 其實,AIDL只是一種描述性語言,其全稱是Android Interface Definition Language,即介
從面試官問“為什麼選擇mysql資料庫”說開去
前幾天面試,面試官問我:“為什麼選擇mysql資料庫”。現在想想,有如下的問題需要解決 關係型資料庫有什麼特點及舉例 非關係型資料庫有什麼特點及舉例 關係型資料庫與非關係型資料庫有什麼區別 關係型資
看完這篇HTTP,跟面試官扯皮就沒問題了
我是一名程式設計師,我的主要程式語言是 Java,我更是一名 Web 開發人員,所以我必須要了解 HTTP,所以本篇文章就來帶你從 HTTP 入門到進階,看完讓你有一種恍然大悟、醍醐灌頂的感覺。 最初在有網路之前,我們的電腦都是單機的,單機系統是孤立的,我還記得 05 年前那會兒家裡有個電腦,想打電腦遊戲還得
【原創】面試官:談談你對mysql聯合索引的認識?
引言 本文預計分為兩個部分: (1)聯合索引部分的基礎知識 在這個部分,我們溫習一下聯合索引的基礎 (2)聯合索引部分的實戰題 在這個部分,列舉幾個我認為算是實戰中的代表題,挑出來說說。 正文 基礎 講聯合索引,一定要扯最左匹配!放心,我不扯有的沒的,幾句話懂個大概就行! 最左匹配 所謂最左原則指的就是如果你
這個案例寫出來,還怕跟面試官扯不明白 OAuth2 登入流程?
昨天和小夥伴們介紹了 OAuth2 的基本概念,在講解 Spring Cloud Security OAuth2 之前,我還是先來通過實際程式碼來和小夥伴們把 OAuth2 中的各個授權模式走一遍,今天我們來看最常用也最複雜的授權碼模式。 本文我將通過一個完整的 Demo ,注意,是一個完整的 Demo,帶領
面試官:你知道哪些事務失效的場景?
前言 宣告式事務是Spring功能中最爽之一,可是有些時候,我們在使用宣告式事務並未生效,這是為什麼呢? 文章首發於微信公眾號【碼猿技術專欄】 今天陳某帶大家來聊一聊宣告事務的幾種失效場景。本文將會從以下兩個方面來說一下事務為什麼會失效? @Transactional介紹 @Transactional失效場
面試官突然問我MySQL儲存過程,我竟然連基礎都不會!(詳細)
**所有知識體系文章,[GitHub](https://github.com/Ziphtracks/JavaLearningmanual)已收錄,歡迎Star!再次感謝,願你早日進入大廠!** **GitHub地址:** [https://github.com/Ziphtracks/JavaLearning
看完這篇 HashSet,跟面試官扯皮沒問題了
> 我是風箏,公眾號「古時的風箏」,一個兼具深度與廣度的程式設計師鼓勵師,一個本打算寫詩卻寫起了程式碼的田園碼農! 文章會收錄在 [JavaNewBee](https://github.com/huzhicheng/JavaNewBee) 中,更有 Java 後端知識圖譜,從小白到大牛要走的路都在裡面。 之
面試中的老大難-mysql事務和鎖,一次性講清楚!
眾所周知,`事務和鎖`是mysql中非常重要功能,同時也是面試的重點和難點。本文會詳細介紹`事務和鎖`的相關概念及其實現原理,相信大家看完之後,一定會對`事務和鎖`有更加深入的理解。 > 本文主要內容是根據掘金小冊《從根兒上理解 MySQL》整理而來。如想詳細瞭解,建議購買掘金小冊閱讀。 ##
【MySQL】面試官問我:MySQL如何實現無資料插入,有資料更新?我是這樣回答的!
## 寫在前面 > 馬上就是金九銀十的跳槽黃金期了,很多讀者都開始出去面試了。這不,又一名讀者出去面試被面試官問了一個MySQL的問題:向MySQL中插入資料,如何實現MySQL中沒有當前id標識的資料時插入資料,有當前id標識的資料時更新資料。其實,這題目一點也不難!! ## 先來個簡單題目 正
mysql事務隔離級別與鎖的關系
美團 enc 自己 ren 問題總結 關系 sql 事務 sql事務 其實操作了這麽久mysql一直也沒有把mysql中事務跟鎖的關系弄得特別清楚。然後搜到美團這篇文章,順便結合一下自己遇到的問題總結一下。 首先事務有四種隔離級別: Refere
mysql事務隔離級別
也有 超時 改變 提交 已提交 style pda 結果 多個實例 概念 隔離級別用於決定如果控制並發用戶如何讀寫數據的操作,同時對性能也有一定的影響作用。 臟讀:讀取未提交的數據; 不可重復讀(Non-repeatable read):在一個事務的兩次查詢之中
MySQL事務隔離級別詳解
默認 多少 bcf 結構 有一個 個數 ref tle eat 轉載自: MySQL事務隔離級別詳解 SQL標準定義了4類隔離級別,包括了一些具體規則,用來限定事務內外的哪些改變是可見的,哪些是不可見的。低級別的隔離級一般支持更高的並發處理,並擁有更低的系統開銷。Read
事務的隔離級別和mysql事務隔離級別修改
eat log control 容易 新的 pda mit 全局 nbsp A事務做了操作 沒有提交 對B事務來說 就等於沒做 獲取的都是之前的數據 但是 在A事務中查詢的話 查到的都是操作之後的數據 沒有提交的數據只有自己看得到,並沒有update到數據庫。 查看In