MySQL基礎(chǔ)原理

1碎节、MySQL基礎(chǔ)架構(gòu):查詢語句執(zhí)行流程

一條查詢語句的執(zhí)行過程一般是經(jīng)過連接器、分析器粉怕、優(yōu)化器健民、執(zhí)行器等功能模塊,最后到達(dá)存儲(chǔ)引擎贫贝。


MySQL 的邏輯架構(gòu)圖

大體來說秉犹,MySQL 可以分為 Server 層和存儲(chǔ)引擎層兩部分。

Server 層包括連接器稚晚、查詢緩存崇堵、分析器、優(yōu)化器客燕、執(zhí)行器等鸳劳,涵蓋 MySQL 的大多數(shù)核心服務(wù)功能,以及所有的內(nèi)置函數(shù)(如日期也搓、時(shí)間赏廓、數(shù)學(xué)和加密函數(shù)等)涵紊,所有跨存儲(chǔ)引擎的功能都在這一層實(shí)現(xiàn),比如存儲(chǔ)過程幔摸、觸發(fā)器摸柄、視圖等。而存儲(chǔ)引擎層負(fù)責(zé)數(shù)據(jù)的存儲(chǔ)和提取抚太。其架構(gòu)模式是插件式的塘幅,支持 InnoDB、MyISAM尿贫、Memory 等多個(gè)存儲(chǔ)引擎〉缦保現(xiàn)在最常用的存儲(chǔ)引擎是 InnoDB,它從 MySQL 5.5.5 版本開始成為了默認(rèn)存儲(chǔ)引擎庆亡。

1.1 連接器

第一步匾乓,你會(huì)先連接到這個(gè)數(shù)據(jù)庫上,這時(shí)候接待你的就是連接器又谋。連接器負(fù)責(zé)跟客戶端建立連接拼缝、獲取權(quán)限、維持和管理連接彰亥。

1.2 查詢緩存

第二步:查詢緩存咧七。MySQL 拿到一個(gè)查詢請(qǐng)求后,會(huì)先到查詢緩存看看任斋,之前是不是執(zhí)行過這條語句继阻。如果你的查詢能夠直接在這個(gè)緩存中找到 key,那么這個(gè) value 就會(huì)被直接返回給客戶端废酷。如果語句不在查詢緩存中瘟檩,就會(huì)繼續(xù)后面的執(zhí)行階段。執(zhí)行完成后澈蟆,執(zhí)行結(jié)果會(huì)被存入查詢緩存中墨辛。只要有對(duì)一個(gè)表的更新,這個(gè)表上所有的查詢緩存都會(huì)被清空趴俘。

1.3 分析器

分析器先會(huì)做“詞法分析”睹簇。MySQL 需要識(shí)別出里面的字符串分別是什么,代表什么哮幢。

做完了這些識(shí)別以后带膀,就要做“語法分析”。根據(jù)詞法分析的結(jié)果橙垢,語法分析器會(huì)根據(jù)語法規(guī)則垛叨,判斷你輸入的這個(gè) SQL 語句是否滿足 MySQL 語法。

1.4 優(yōu)化器

在開始執(zhí)行之前,還要先經(jīng)過優(yōu)化器的處理嗽元。

優(yōu)化器是在表里面有多個(gè)索引的時(shí)候敛纲,決定使用哪個(gè)索引;或者在一個(gè)語句有多表關(guān)聯(lián)(join)的時(shí)候剂癌,決定各個(gè)表的連接順序淤翔。

優(yōu)化器階段完成后,這個(gè)語句的執(zhí)行方案就確定下來了佩谷,然后進(jìn)入執(zhí)行器階段旁壮。

1.5 執(zhí)行器

MySQL 通過分析器知道了你要做什么,通過優(yōu)化器知道了該怎么做谐檀,于是就進(jìn)入了執(zhí)行器階段抡谐,開始執(zhí)行語句。

開始執(zhí)行的時(shí)候桐猬,要先判斷一下你對(duì)這個(gè)表 T 有沒有執(zhí)行查詢的權(quán)限麦撵。

如果有權(quán)限,就打開表繼續(xù)執(zhí)行溃肪。打開表的時(shí)候免胃,執(zhí)行器就會(huì)根據(jù)表的引擎定義,去使用這個(gè)引擎提供的接口惫撰。

  • 調(diào)用 InnoDB 引擎接口取這個(gè)表的第一行
  • 調(diào)用引擎接口取“下一行”羔沙,重復(fù)相同的判斷邏輯,直到取到這個(gè)表的最后一行厨钻。
  • 執(zhí)行器將上述遍歷過程中所有滿足條件的行組成的記錄集作為結(jié)果集返回給客戶端

2撬碟、日志系統(tǒng):更新語句執(zhí)行流程

SQL 語句基本的執(zhí)行鏈路,查詢語句的那一套流程莉撇,更新語句也是同樣會(huì)走一遍。語句前要先連接數(shù)據(jù)庫惶傻,表上有更新的時(shí)候棍郎,跟這個(gè)表有關(guān)的查詢緩存會(huì)失效,所以這條語句就會(huì)把表 T 上所有緩存結(jié)果都清空银室。

分析器會(huì)通過詞法和語法解析知道這是一條更新語句涂佃。優(yōu)化器決定要使用 ID 這個(gè)索引。然后蜈敢,執(zhí)行器負(fù)責(zé)具體執(zhí)行辜荠,找到這一行,然后更新抓狭。

更新流程還涉及兩個(gè)重要的日志模塊:redo log(重做日志)和 binlog(歸檔日志)伯病。

重要的日志模塊:redo log

WAL 技術(shù):Write-Ahead Logging,它的關(guān)鍵點(diǎn)就是先寫日志否过,再寫磁盤午笛。

當(dāng)有一條記錄需要更新的時(shí)候惭蟋,InnoDB 引擎就會(huì)先把記錄寫到 redo log里面,并更新內(nèi)存药磺,這個(gè)時(shí)候更新就算完成了告组。同時(shí),InnoDB 引擎會(huì)在適當(dāng)?shù)臅r(shí)候癌佩,將這個(gè)操作記錄更新到磁盤里面木缝,而這個(gè)更新往往是在系統(tǒng)比較空閑的時(shí)候做。

InnoDB 的 redo log 是固定大小的围辙,比如可以配置為一組 4 個(gè)文件我碟,每個(gè)文件的大小是 1GB,從頭開始寫酌畜,寫到末尾就又回到開頭循環(huán)寫怎囚。

image.png

crash-safe:InnoDB 就可以保證即使數(shù)據(jù)庫發(fā)生異常重啟,之前提交的記錄都不會(huì)丟失桥胞。

重要的日志模塊:binlog

MySQL 整體來看恳守,其實(shí)就有兩塊:一塊是 Server 層,它主要做的是 MySQL 功能層面的事情贩虾;還有一塊是引擎層催烘,負(fù)責(zé)存儲(chǔ)相關(guān)的具體事宜。redo log 是 InnoDB 引擎特有的日志缎罢,而 Server 層也有自己的日志伊群,稱為 binlog(歸檔日志)。

兩種日志有以下三點(diǎn)不同:

  • redo log 是 InnoDB 引擎特有的策精;binlog 是 MySQL 的 Server 層實(shí)現(xiàn)的舰始,所有引擎都可以使用。
  • redo log 是物理日志咽袜,記錄的是“在某個(gè)數(shù)據(jù)頁上做了什么修改”丸卷;binlog 是邏輯日志,記錄的是這個(gè)語句的原始邏輯询刹,比如“給 ID=2 這一行的 c 字段加 1 ”谜嫉。
  • redo log 是循環(huán)寫的,空間固定會(huì)用完凹联;binlog 是可以追加寫入的沐兰。“追加寫”是指 binlog 文件寫到一定大小后會(huì)切換到下一個(gè)蔽挠,并不會(huì)覆蓋以前的日志住闯。

update T set c=c+1 where ID=2; 執(zhí)行流程:

update 語句執(zhí)行流程

兩階段提交

redo log 和 binlog 是兩個(gè)獨(dú)立的邏輯,如果不用兩階段提交,要么就是先寫完 redo log 再寫 binlog寞秃,或者采用反過來的順序斟叼,數(shù)據(jù)庫的狀態(tài)就有可能和用它的日志恢復(fù)出來的庫的狀態(tài)不一致。

redo log 和 binlog 都可以用于表示事務(wù)的提交狀態(tài)春寿,而兩階段提交就是讓這兩個(gè)狀態(tài)保持邏輯上的一致朗涩。

3、事務(wù)隔離

事務(wù)就是要保證一組數(shù)據(jù)庫操作绑改,要么全部成功谢床,要么全部失敗。在 MySQL 中厘线,事務(wù)支持是在引擎層實(shí)現(xiàn)的俱病。MySQL 原生的 MyISAM 引擎就不支持事務(wù)立叛。

事物特性:ACID(Atomicity谓着、Consistency绪商、Isolation、Durability耳璧,即原子性成箫、一致性、隔離性旨枯、持久性)蹬昌。

3.1 SQL 標(biāo)準(zhǔn)的事務(wù)隔離級(jí)別包括:

  • 讀未提交(read uncommitted):一個(gè)事務(wù)還沒提交時(shí),它做的變更就能被別的事務(wù)看到
  • 讀提交(read committed):一個(gè)事務(wù)提交之后攀隔,它做的變更才會(huì)被其他事務(wù)看到
  • 可重復(fù)讀(repeatable read):一個(gè)事務(wù)執(zhí)行過程中看到的數(shù)據(jù)皂贩,總是跟這個(gè)事務(wù)在啟動(dòng)時(shí)看到的數(shù)據(jù)是一致的。當(dāng)然在可重復(fù)讀隔離級(jí)別下昆汹,未提交變更對(duì)其他事務(wù)也是不可見的明刷。
  • 串行化(serializable ):顧名思義是對(duì)于同一行記錄,“寫”會(huì)加“寫鎖”满粗,“讀”會(huì)加“讀鎖”遮精。當(dāng)出現(xiàn)讀寫鎖沖突的時(shí)候,后訪問的事務(wù)必須等前一個(gè)事務(wù)執(zhí)行完成败潦,才能繼續(xù)執(zhí)行。

3.2 事務(wù)隔離的實(shí)現(xiàn)

在 MySQL 中准脂,實(shí)際上每條記錄在更新的時(shí)候都會(huì)同時(shí)記錄一條回滾操作劫扒。記錄上的最新值,通過回滾操作狸膏,都可以得到前一個(gè)狀態(tài)的值沟饥。

不同時(shí)刻啟動(dòng)的事務(wù)會(huì)有不同的 read-view。同一條記錄在系統(tǒng)中可以存在多個(gè)版本,就是數(shù)據(jù)庫的多版本并發(fā)控制(MVCC)贤旷。

一個(gè)值從 1 被按順序改成了 2广料、3、4的回滾記錄

如圖中看到的幼驶,在視圖 A艾杏、B、C 里面盅藻,這一個(gè)記錄的值分別是 1购桑、2、4氏淑,對(duì)于 read-view A勃蜘,要得到 1,就必須將當(dāng)前值依次執(zhí)行圖中所有的回滾操作得到假残。同時(shí)你會(huì)發(fā)現(xiàn)缭贡,即使現(xiàn)在有另外一個(gè)事務(wù)正在將 4 改成 5,這個(gè)事務(wù)跟 read-view A辉懒、B阳惹、C 對(duì)應(yīng)的事務(wù)是不會(huì)沖突的。

回滾日志在不需要的時(shí)候才刪除耗帕。也就是說穆端,系統(tǒng)會(huì)判斷,當(dāng)沒有事務(wù)再需要用到這些回滾日志時(shí)仿便,就是當(dāng)系統(tǒng)里沒有比這個(gè)回滾日志更早的 read-view 的時(shí)候体啰,回滾日志會(huì)被刪除。所以盡量不要使用長事務(wù)嗽仪,除了對(duì)回滾段的影響荒勇,長事務(wù)還占用鎖資源,也可能拖垮整個(gè)庫闻坚。

4沽翔、索引

索引的出現(xiàn)其實(shí)就是為了提高數(shù)據(jù)查詢的效率,就像書的目錄一樣窿凤。

4.1 實(shí)現(xiàn)索引的方式:

  • 哈希表:增加新的數(shù)據(jù)速度會(huì)很快仅偎,只需要往后追加。但缺點(diǎn)是哈希索引做區(qū)間查詢的速度很慢雳殊。所以橘沥,哈希表這種結(jié)構(gòu)適用于只有等值查詢的場景,比如 Memcached 及其他一些 NoSQL 引擎夯秃。
  • 有序數(shù)組:有序數(shù)組在等值查詢和范圍查詢場景中的性能就都非常優(yōu)秀座咆。但是痢艺,更新數(shù)據(jù)的時(shí)候往中間插入一個(gè)記錄就必須得挪動(dòng)后面所有的記錄,成本太高介陶。所以堤舒,有序數(shù)組索引只適用于靜態(tài)存儲(chǔ)引擎,保存不會(huì)再修改的數(shù)據(jù)哺呜。
  • 搜索樹:使用N 叉樹讀寫性能好舌缤,符合磁盤的訪問模式,被廣泛應(yīng)用在數(shù)據(jù)庫引擎中弦牡。

數(shù)據(jù)庫底層存儲(chǔ)的核心就是基于這些數(shù)據(jù)模型的友驮。每碰到一個(gè)新數(shù)據(jù)庫,我們需要先關(guān)注它的數(shù)據(jù)模型驾锰,這樣才能從理論上分析出這個(gè)數(shù)據(jù)庫的適用場景卸留。

4.2 InnoDB 的索引模型

在 InnoDB 中,表都是根據(jù)主鍵順序以索引的形式存放的椭豫,這種存儲(chǔ)方式的表稱為索引組織表耻瑟。InnoDB 使用了 B+ 樹索引模型,所以數(shù)據(jù)都是存儲(chǔ)在 B+ 樹中的赏酥,每一個(gè)索引在 InnoDB 里面對(duì)應(yīng)一棵 B+ 樹喳整。

4.2.1 B+樹的結(jié)構(gòu)

create table T(id int primary key, k int not null, name varchar(16),index (k))engine=InnoDB;

InnoDB 的索引組織結(jié)構(gòu)
  • 主鍵索引的葉子節(jié)點(diǎn)存的是整行數(shù)據(jù)。在 InnoDB 里裸扶,主鍵索引也被稱為聚簇索引(clustered index)框都。
  • 非主鍵索引的葉子節(jié)點(diǎn)內(nèi)容是主鍵的值。在 InnoDB 里呵晨,非主鍵索引也被稱為二級(jí)索引(secondary index)魏保。

4.2.2 基于主鍵索引和普通索引的查詢有什么區(qū)別

  • 如果語句是 select * from T where ID=500,即主鍵查詢方式摸屠,則只需要搜索 ID 這棵 B+ 樹谓罗;
  • 如果語句是 select * from T where k=5,即普通索引查詢方式季二,則需要先搜索 k 索引樹檩咱,得到 ID 的值為 500,再到 ID 索引樹搜索一次胯舷。這個(gè)過程稱為回表刻蚯。

基于非主鍵索引的查詢需要多掃描一棵索引樹。因此桑嘶,我們?cè)趹?yīng)用中應(yīng)該盡量使用主鍵查詢炊汹。

4.2.3 索引維護(hù)

B+ 樹為了維護(hù)索引有序性,在插入新值的時(shí)候需要做必要的維護(hù):需要做數(shù)據(jù)移動(dòng)和頁分裂不翩、頁合并兵扬。

主鍵長度越小,普通索引的葉子節(jié)點(diǎn)就越小口蝠,普通索引占用的空間也就越小器钟。

4.2.4 覆蓋索引

如果執(zhí)行的語句需要查的值已經(jīng)在索引樹上,可以直接提供查詢結(jié)果妙蔗,不需要回表傲霸,也就是說,在這個(gè)查詢里面眉反,索引已經(jīng)“覆蓋了”我們的查詢需求昙啄,我們稱為覆蓋索引

由于覆蓋索引可以減少樹的搜索次數(shù)寸五,顯著提升查詢性能梳凛,所以使用覆蓋索引是一個(gè)常用的性能優(yōu)化手段。

索引字段的維護(hù)是有代價(jià)的梳杏,因此韧拒,建立冗余索引來支持覆蓋索引時(shí)需要權(quán)衡考慮。

4.2.5 最左前綴原則

B+ 樹這種索引結(jié)構(gòu)十性,可以利用索引的“最左前綴”叛溢,來定位記錄。

不只是索引的全部定義劲适,只要滿足最左前綴楷掉,就可以利用索引來加速檢索。這個(gè)最左前綴可以是聯(lián)合索引的最左 N 個(gè)字段霞势,也可以是字符串索引的最左 M 個(gè)字符烹植。

在建立聯(lián)合索引的時(shí)候,如何安排索引內(nèi)的字段順序

  • 第一原則是支示,如果通過調(diào)整順序刊橘,可以少維護(hù)一個(gè)索引,那么這個(gè)順序往往就是需要優(yōu)先考慮采用的颂鸿。
  • 考慮的第二原則就是空間

4.2.6 索引下推

MySQL 5.6 引入的索引下推優(yōu)化(index condition pushdown)促绵, 可以在索引遍歷過程中,對(duì)索引中包含的字段先做判斷嘴纺,直接過濾掉不滿足條件的記錄败晴,減少回表次數(shù)。

5栽渴、全局鎖尖坤、表鎖和行鎖

5.1 全局鎖

全局鎖就是對(duì)整個(gè)數(shù)據(jù)庫實(shí)例加鎖。MySQL 提供了一個(gè)加全局讀鎖的方法闲擦,命令是 Flush tables with read lock (FTWRL)慢味。當(dāng)你需要讓整個(gè)庫處于只讀狀態(tài)的時(shí)候场梆,可以使用這個(gè)命令,之后其他線程的以下語句會(huì)被阻塞:數(shù)據(jù)更新語句(數(shù)據(jù)的增刪改)纯路、數(shù)據(jù)定義語句(包括建表或油、修改表結(jié)構(gòu)等)和更新類事務(wù)的提交語句。

全局鎖的典型使用場景是驰唬,做全庫邏輯備份顶岸。也就是把整庫每個(gè)表都 select 出來存成文本。不加鎖的話叫编,備份系統(tǒng)備份的得到的庫不是一個(gè)邏輯時(shí)間點(diǎn)辖佣,這個(gè)視圖是邏輯不一致的。

官方自帶的邏輯備份工具是 mysqldump搓逾。當(dāng) mysqldump 使用參數(shù)–single-transaction 的時(shí)候卷谈,導(dǎo)數(shù)據(jù)之前就會(huì)啟動(dòng)一個(gè)事務(wù),來確保拿到一致性視圖恃逻。而由于 MVCC 的支持雏搂,這個(gè)過程中數(shù)據(jù)是可以正常更新的。single-transaction 方法只適用于所有的表使用事務(wù)引擎的庫寇损。

有了這個(gè)功能凸郑,為什么還需要 FTWRL 呢?一致性讀是好矛市,但前提是引擎要支持這個(gè)隔離級(jí)別芙沥。比如,對(duì)于 MyISAM 這種不支持事務(wù)的引擎浊吏,如果備份過程中有更新而昨,總是只能取到最新的數(shù)據(jù),那么就破壞了備份的一致性找田。這時(shí)歌憨,我們就需要使用 FTWRL 命令了。

不使用 set global readonly=true 的原因:

  • readonly 的值會(huì)被用來做其他邏輯
  • 在異常處理機(jī)制上有差異

5.2 表級(jí)鎖

MySQL 里面表級(jí)別的鎖有兩種:一種是表鎖墩衙,一種是元數(shù)據(jù)鎖(meta data lock务嫡,MDL)。

表鎖的語法是 lock tables … read/write漆改。與 FTWRL 類似心铃,可以用 unlock tables 主動(dòng)釋放鎖,也可以在客戶端斷開的時(shí)候自動(dòng)釋放挫剑。

對(duì)于 InnoDB 這種支持行鎖的引擎去扣,一般不使用 lock tables 命令來控制并發(fā)。

另一類表級(jí)的鎖是 MDL(metadata lock)樊破。MDL 不需要顯式使用愉棱,在訪問一個(gè)表的時(shí)候會(huì)被自動(dòng)加上唆铐。MDL 的作用是,保證讀寫的正確性奔滑。你可以想象一下或链,如果一個(gè)查詢正在遍歷一個(gè)表中的數(shù)據(jù),而執(zhí)行期間另一個(gè)線程對(duì)這個(gè)表結(jié)構(gòu)做變更档押,刪了一列,那么查詢線程拿到的結(jié)果跟表結(jié)構(gòu)對(duì)不上祈纯,肯定是不行的令宿。

在 MySQL 5.5 版本中引入了 MDL,當(dāng)對(duì)一個(gè)表做增刪改查操作的時(shí)候腕窥,加 MDL 讀鎖粒没;當(dāng)要對(duì)表做結(jié)構(gòu)變更操作的時(shí)候,加 MDL 寫鎖簇爆。

  • 讀鎖之間不互斥癞松,因此你可以有多個(gè)線程同時(shí)對(duì)一張表增刪改查。
  • 讀寫鎖之間入蛆、寫鎖之間是互斥的响蓉,用來保證變更表結(jié)構(gòu)操作的安全性。因此哨毁,如果有兩個(gè)線程要同時(shí)給一個(gè)表加字段枫甲,其中一個(gè)要等另一個(gè)執(zhí)行完才能開始執(zhí)行。

事務(wù)中的 MDL 鎖扼褪,在語句執(zhí)行開始時(shí)申請(qǐng)想幻,但是語句結(jié)束后并不會(huì)馬上釋放,而會(huì)等到整個(gè)事務(wù)提交后再釋放话浇。

如何安全地給小表加字段:

  • 首先我們要解決長事務(wù)脏毯,事務(wù)不提交,就會(huì)一直占著 MDL 鎖幔崖。
  • alter table 語句里面設(shè)定等待時(shí)間食店,如果在這個(gè)指定的等待時(shí)間里面能夠拿到 MDL 寫鎖最好,拿不到也不要阻塞后面的業(yè)務(wù)語句岖瑰,先放棄叛买。之后開發(fā)人員或者 DBA 再通過重試命令重復(fù)這個(gè)過程。使用DDL NOWAIT/WAIT n 這個(gè)語法

6蹋订、事務(wù)實(shí)現(xiàn)

begin/start transaction 命令并不是一個(gè)事務(wù)的起點(diǎn)率挣,在執(zhí)行到它們之后的第一個(gè)操作 InnoDB 表的語句,事務(wù)才真正啟動(dòng)露戒。如果你想要馬上啟動(dòng)一個(gè)事務(wù)椒功,可以使用 start transaction with consistent snapshot 這個(gè)命令捶箱。

在 MySQL 里,有兩個(gè)“視圖”的概念动漾,它沒有物理結(jié)構(gòu)丁屎,作用是事務(wù)執(zhí)行期間用來定義“我能看到什么數(shù)據(jù)”:

  • 一個(gè)是 view。它是一個(gè)用查詢語句定義的虛擬表旱眯,在調(diào)用的時(shí)候執(zhí)行查詢語句并生成結(jié)果晨川。創(chuàng)建視圖的語法是 create view … ,而它的查詢方法與表一樣删豺。
  • 另一個(gè)是 InnoDB 在實(shí)現(xiàn) MVCC 時(shí)用到的一致性讀視圖共虑,即 consistent read view,用于支持 RC(Read Committed呀页,讀提交)和 RR(Repeatable Read妈拌,可重復(fù)讀)隔離級(jí)別的實(shí)現(xiàn)。

在可重復(fù)讀隔離級(jí)別下蓬蝶,事務(wù)在啟動(dòng)的時(shí)候就“拍了個(gè)快照”尘分。注意,這個(gè)快照是基于整庫的丸氛。

InnoDB 里面每個(gè)事務(wù)有一個(gè)唯一的事務(wù) ID培愁,叫作 transaction id。它是在事務(wù)開始的時(shí)候向 InnoDB 的事務(wù)系統(tǒng)申請(qǐng)的缓窜,是按申請(qǐng)順序嚴(yán)格遞增的竭钝。

每行數(shù)據(jù)也都是有多個(gè)版本的。每次事務(wù)更新數(shù)據(jù)的時(shí)候雹洗,都會(huì)生成一個(gè)新的數(shù)據(jù)版本香罐,并且把 transaction id 賦值給這個(gè)數(shù)據(jù)版本的事務(wù) ID,記為 row trx_id时肿。同時(shí)庇茫,舊的數(shù)據(jù)版本要保留,并且在新的數(shù)據(jù)版本中螃成,能夠有信息可以直接拿到它旦签。

也就是說,數(shù)據(jù)表中的一行記錄寸宏,其實(shí)可能有多個(gè)版本 (row)宁炫,每個(gè)版本有自己的 row trx_id。


行狀態(tài)變更圖

按照可重復(fù)讀的定義氮凝,一個(gè)事務(wù)啟動(dòng)的時(shí)候羔巢,能夠看到所有已經(jīng)提交的事務(wù)結(jié)果。但是之后,這個(gè)事務(wù)執(zhí)行期間竿秆,其他事務(wù)的更新對(duì)它不可見启摄。因此,一個(gè)事務(wù)只需要在啟動(dòng)的時(shí)候聲明說幽钢,“以我啟動(dòng)的時(shí)刻為準(zhǔn)歉备,如果一個(gè)數(shù)據(jù)版本是在我啟動(dòng)之前生成的,就認(rèn)匪燕;如果是我啟動(dòng)以后才生成的蕾羊,我就不認(rèn),我必須要找到它的上一個(gè)版本”帽驯。如果“上一個(gè)版本”也不可見肚豺,那就得繼續(xù)往前找。還有界拦,如果是這個(gè)事務(wù)自己更新的數(shù)據(jù),它自己還是要認(rèn)的梗劫。

啟動(dòng)的時(shí)候享甸,InnoDB 為每個(gè)事務(wù)構(gòu)造了一個(gè)數(shù)組,用來保存這個(gè)事務(wù)啟動(dòng)瞬間梳侨,當(dāng)前正在“活躍”的所有事務(wù) ID蛉威。“活躍”指的就是走哺,啟動(dòng)了但還沒提交蚯嫌。數(shù)組里面事務(wù) ID 的最小值記為低水位,當(dāng)前系統(tǒng)里面已經(jīng)創(chuàng)建過的事務(wù) ID 的最大值加 1 記為高水位丙躏。這個(gè)視圖數(shù)組和高水位择示,就組成了當(dāng)前事務(wù)的一致性視圖(read-view)。

數(shù)據(jù)版本可見性規(guī)則

對(duì)于當(dāng)前事務(wù)的啟動(dòng)瞬間來說晒旅,一個(gè)數(shù)據(jù)版本的 row trx_id栅盲,有以下幾種可能:

  • 如果落在綠色部分,表示這個(gè)版本是已提交的事務(wù)或者是當(dāng)前事務(wù)自己生成的废恋,這個(gè)數(shù)據(jù)是可見的谈秫;
  • 如果落在紅色部分,表示這個(gè)版本是由將來啟動(dòng)的事務(wù)生成的鱼鼓,是肯定不可見的拟烫;
  • 如果落在黃色部分,那就包括兩種情況
    • a. 若 row trx_id 在數(shù)組中迄本,表示這個(gè)版本是由還沒提交的事務(wù)生成的硕淑,不可見;
    • b. 若 row trx_id 不在數(shù)組中,表示這個(gè)版本是已經(jīng)提交了的事務(wù)生成的喜颁,可見稠氮。

所以,InnoDB 利用了“所有數(shù)據(jù)都有多個(gè)版本”的這個(gè)特性半开,實(shí)現(xiàn)了“秒級(jí)創(chuàng)建快照”的能力隔披。

一個(gè)數(shù)據(jù)版本,對(duì)于一個(gè)事務(wù)視圖來說寂拆,除了自己的更新總是可見以外奢米,有三種情況:

  • 版本未提交,不可見纠永;
  • 版本已提交鬓长,但是是在視圖創(chuàng)建后提交的,不可見尝江;
  • 版本已提交涉波,而且是在視圖創(chuàng)建前提交的,可見炭序。

更新數(shù)據(jù)

更新數(shù)據(jù)都是先讀后寫的啤覆,而這個(gè)讀,只能讀當(dāng)前的值惭聂,稱為“當(dāng)前讀”(current read)窗声。

除了 update 語句外,select 語句如果加鎖辜纲,也是當(dāng)前讀笨觅。

比如select * from t where id=1 修改一下,加上 lock in share mode讀鎖(S 鎖耕腾,共享鎖) 或 for update寫鎖(X 鎖见剩,排他鎖),也都可以讀到最新的數(shù)據(jù)版本扫俺。

事務(wù) A炮温、B、C'的執(zhí)行流程

事務(wù) C’的不同是牵舵,更新后并沒有馬上提交柒啤,在它提交前,事務(wù) B 的更新語句先發(fā)起了畸颅。前面說過了担巩,雖然事務(wù) C’還沒提交,但是 (1,2) 這個(gè)版本也已經(jīng)生成了没炒,并且是當(dāng)前的最新版本涛癌。那么,事務(wù) B 的更新語句會(huì)怎么處理呢?這時(shí)候拳话,我們?cè)谏弦黄恼轮刑岬降摹皟呻A段鎖協(xié)議”就要上場了先匪。事務(wù) C’沒提交,也就是說 (1,2) 這個(gè)版本上的寫鎖還沒釋放弃衍。而事務(wù) B 是當(dāng)前讀呀非,必須要讀最新版本,而且必須加鎖镜盯,因此就被鎖住了岸裙,必須等到事務(wù) C’釋放這個(gè)鎖,才能繼續(xù)它的當(dāng)前讀速缆。

我們把一致性讀降允、當(dāng)前讀和行鎖就串起來了。

  • 可重復(fù)讀的核心就是一致性讀(consistent read)艺糜;而事務(wù)更新數(shù)據(jù)的時(shí)候剧董,只能用當(dāng)前讀。如果當(dāng)前的記錄的行鎖被其他事務(wù)占用的話破停,就需要進(jìn)入鎖等待翅楼。
  • 而讀提交的邏輯和可重復(fù)讀的邏輯類似,它們最主要的區(qū)別是:在可重復(fù)讀隔離級(jí)別下辱挥,只需要在事務(wù)開始的時(shí)候創(chuàng)建一致性視圖,之后事務(wù)里的其他查詢都共用這個(gè)一致性視圖边涕;在讀提交隔離級(jí)別下晤碘,每一個(gè)語句執(zhí)行前都會(huì)重新算出一個(gè)新的視圖。

InnoDB 的行數(shù)據(jù)有多個(gè)版本功蜓,每個(gè)數(shù)據(jù)版本有自己的 row trx_id园爷,每個(gè)事務(wù)或者語句有自己的一致性視圖。普通查詢語句是一致性讀式撼,一致性讀會(huì)根據(jù) row trx_id 和一致性視圖確定數(shù)據(jù)版本的可見性童社。對(duì)于可重復(fù)讀,查詢只承認(rèn)在事務(wù)啟動(dòng)前就已經(jīng)提交完成的數(shù)據(jù)著隆;對(duì)于讀提交扰楼,查詢只承認(rèn)在語句啟動(dòng)前就已經(jīng)提交完成的數(shù)據(jù);而當(dāng)前讀美浦,總是讀取已經(jīng)提交完成的最新版本弦赖。

來源:極客時(shí)間《MySQL實(shí)戰(zhàn)45講》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市浦辨,隨后出現(xiàn)的幾起案子蹬竖,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,888評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件币厕,死亡現(xiàn)場離奇詭異列另,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)旦装,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門页衙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人同辣,你說我怎么就攤上這事拷姿。” “怎么了旱函?”我有些...
    開封第一講書人閱讀 168,386評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵响巢,是天一觀的道長。 經(jīng)常有香客問我棒妨,道長踪古,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,726評(píng)論 1 297
  • 正文 為了忘掉前任券腔,我火速辦了婚禮伏穆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘纷纫。我一直安慰自己枕扫,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評(píng)論 6 397
  • 文/花漫 我一把揭開白布辱魁。 她就那樣靜靜地躺著烟瞧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪染簇。 梳的紋絲不亂的頭發(fā)上参滴,一...
    開封第一講書人閱讀 52,337評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音锻弓,去河邊找鬼砾赔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛青灼,可吹牛的內(nèi)容都是我干的暴心。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼杂拨,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼酷勺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起扳躬,我...
    開封第一講書人閱讀 39,807評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤脆诉,失蹤者是張志新(化名)和其女友劉穎甚亭,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體击胜,經(jīng)...
    沈念sama閱讀 46,349評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡亏狰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了偶摔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片暇唾。...
    茶點(diǎn)故事閱讀 40,567評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖辰斋,靈堂內(nèi)的尸體忽然破棺而出策州,到底是詐尸還是另有隱情,我是刑警寧澤宫仗,帶...
    沈念sama閱讀 36,242評(píng)論 5 350
  • 正文 年R本政府宣布够挂,位于F島的核電站,受9級(jí)特大地震影響藕夫,放射性物質(zhì)發(fā)生泄漏孽糖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評(píng)論 3 334
  • 文/蒙蒙 一毅贮、第九天 我趴在偏房一處隱蔽的房頂上張望办悟。 院中可真熱鬧,春花似錦滩褥、人聲如沸病蛉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铺然。三九已至,卻和暖如春丢间,著一層夾襖步出監(jiān)牢的瞬間探熔,已是汗流浹背驹针。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評(píng)論 1 272
  • 我被黑心中介騙來泰國打工烘挫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人柬甥。 一個(gè)月前我還...
    沈念sama閱讀 48,995評(píng)論 3 377
  • 正文 我出身青樓饮六,卻偏偏與公主長得像,于是被迫代替她去往敵國和親苛蒲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子卤橄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評(píng)論 2 359