自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

阿里面試官:MySQL是如何實(shí)現(xiàn)ACID的?

數(shù)據(jù)庫 MySQL
今天,筆者簡單談?wù)勛约簩CID特性實(shí)現(xiàn)原理的理解。本文主要探討MYSQL InnoDB引擎下的ACID實(shí)現(xiàn)原理,對什么是事務(wù),隔離級別簡單回顧一下。

[[425940]]

 作為二本上岸大廠的后端應(yīng)屆生,深知沒人帶一路摸索的艱辛,想把自己的心路歷程與經(jīng)驗(yàn)心得收獲分享給大家。后期大廠面試系列持續(xù)更新中.....

1前文

之前有同學(xué)在面阿里二面被問到:MYSQL是如何實(shí)現(xiàn)ACID的?其實(shí),如果叫簡單介紹什么是ACID,大家肯定都能回答,但是,想要答好底層如何實(shí)現(xiàn)ACID特性的,還得考考功力啦!

今天,筆者簡單談?wù)勛约簩CID特性實(shí)現(xiàn)原理的理解。本文主要探討MYSQL InnoDB引擎下的ACID實(shí)現(xiàn)原理,對什么是事務(wù),隔離級別簡單回顧一下。

2事務(wù)與ACID

何為事務(wù)呢?書上給予的概念多而難于理解,筆者對事務(wù)的理解:一系列操作組成,要么全部成功,要么全部失敗。它具備ACID四大特性,在并發(fā)下,可能存在臟讀、幻讀、不可重復(fù)讀的并發(fā)問題,于是又引出了四大隔離級別。

01事務(wù)ACID特性

MYSQL作為一個(gè)關(guān)系型數(shù)據(jù)庫,以最常見的InnoDB引擎來說,是如何保證ACID的。

(Atomicity)原子性:一些列操作要么全部成功,要么全部失敗

(Isolation)隔離性:事務(wù)的結(jié)果只有提交了其他事務(wù)才可見

(Consistency)一致性:數(shù)據(jù)庫總時(shí)從一個(gè)一致狀態(tài)變到另一個(gè)一致狀態(tài)(事務(wù)修改前后的數(shù)據(jù)總體保證一致 轉(zhuǎn)賬)

(Durability)持久性:事務(wù)提交后,對數(shù)據(jù)修改永久的

02原子性

在聊原子性之前,我得先給大家普及一個(gè)東西——undo log,這是啥玩意兒呢?如果想要詳細(xì)了解或則想知道它具體內(nèi)部咋實(shí)現(xiàn)的可以仔細(xì)去看書,這里我就簡單分享我的理解,知道這些,面試基本夠用啦。

undo log,它是一種回滾日志,既可以用來實(shí)現(xiàn)隔離性MVCC,也可以保證原子性。MVCC待會(huì)談?wù)摗?shí)現(xiàn)原子性的關(guān)鍵,是事務(wù)回滾時(shí)能夠撤銷所有已經(jīng)成功執(zhí)行的sql語句。

當(dāng)事務(wù)對數(shù)據(jù)庫進(jìn)行修改時(shí),InnoDB會(huì)生成對應(yīng)的undo log,undo log會(huì)保存事務(wù)開始前老版本的數(shù)據(jù),當(dāng)事務(wù)發(fā)生異常,便會(huì)rollback回滾到老版本狀態(tài)。當(dāng)發(fā)生回滾時(shí),InnoDB會(huì)根據(jù)undo log的內(nèi)容做相反邏輯操作。

  • insert語句,回滾時(shí)會(huì)執(zhí)行 delete;
  • delete語句,回滾時(shí)會(huì)執(zhí)行insert;
  • update語句,回滾時(shí)便執(zhí)行相反的update,把數(shù)據(jù)改回來。

總之,MYSQL的原子性便是由undo log來保證,undo log的作用我做了一下歸納總結(jié):

作用:undolog記錄事務(wù)開始前老版本數(shù)據(jù),用于實(shí)現(xiàn)回滾,保證原子性,實(shí)現(xiàn)MVCC,會(huì)將數(shù)據(jù)修改前的舊版本保存在undolog,然后行記錄有個(gè)隱藏字段回滾指針指向老版本。

03持久性

在聊持久性之前,我們得先知道redo log。老規(guī)矩,想深入學(xué)習(xí)理解看書噢,這里只做筆者面試回答分享。

我們以一個(gè)生活小案例來理解一下下:

redo log,是一種物理日志。它類似于一個(gè)卸貨的小推車,我們卸貨若是每下一件物品就拿著去入庫,那豈不是特浪費(fèi)時(shí)間(效率低、還要找到合適存庫位置)。此時(shí),若有一個(gè)小推車,我們將貨物首先存放在小推車,當(dāng)推車滿了再往庫里存,豈不大大增加了效率。

MYSQL中也用了類似思想,我們再更新數(shù)據(jù)庫時(shí),先將更新操作記錄在redo log日志,等redo log滿了或則MYSQL空閑了再刷盤。

其實(shí)就是MySQL里經(jīng)常說到的WAL技術(shù),WAL的全稱是Write-Ahead Logging,它的關(guān)鍵點(diǎn)就是先寫日志,再寫磁盤,也就是先裝小推車,等不忙的時(shí)候再裝庫。

總之,MYSQL的持久性便是由redo log來保證,redo log的作用我做了一下歸納總結(jié):

redo log

物理日志

作用:會(huì)記錄事務(wù)開啟后對數(shù)據(jù)做的修改,crash-safe

特性:空間一定,寫完后會(huì)循環(huán)寫,有兩個(gè)指針write pos指向當(dāng)前記錄位置,checkpoint指向?qū)⒉脸奈恢?,redolog相當(dāng)于是個(gè)取貨小車,貨物太多時(shí)來不及一件一件入庫太慢了這樣,就先將貨物放入小車,等到貨物不多或則小車滿了或則店里空閑時(shí)再將小車貨物送到庫房。用于crash-safe,數(shù)據(jù)庫異常斷電等情況可用redo log恢復(fù)。

以下只作了解:

寫入流程:先寫redo log buffer,然后wite到文件系統(tǒng)的page cache,此時(shí)并沒有持久化,然后fsync持久化到磁盤

寫入策略:根據(jù)innodb_flush_log_at_trx_commit參數(shù)控制(我的記憶:innodb以事務(wù)的什么提交方式刷新日志)

0——>事務(wù)提交時(shí)只把redo log留在redo log buffer

1——>將redo log直接持久化到磁盤(所以有個(gè)雙“1”配置,后面會(huì)講)

2——>只是把redo log寫到page cache

04隔離性

說到隔離性,我們都知道MYSQL有四種隔離級別,用來解決存在的并發(fā)問題。臟讀、幻讀、不可重復(fù)讀。

那么不同隔離級別,隔離性是怎樣實(shí)現(xiàn)的呢?具體實(shí)現(xiàn)原理是怎樣的呢?接下來我們就談?wù)?,看不懂沒關(guān)系,老規(guī)矩,結(jié)尾會(huì)進(jìn)行總結(jié)滴!

一句話:鎖+MVCC。

1、表鎖

  • lock table table_name read/write
  • myisam執(zhí)行select自動(dòng)加讀鎖,執(zhí)行update/delete/insert自動(dòng)加寫鎖
  • 表加了讀鎖,不會(huì)阻塞其他線程的讀操作,阻塞寫操作
  • 表加了寫鎖,讀寫操作都阻塞

2、行鎖

鎖的類型

  • 間隙鎖-gap lock:鎖定區(qū)間范圍,防止幻讀,左開右開,只在可重復(fù)讀隔離級別下生效—|—為了阻止多個(gè)事務(wù)將記錄插入到同一范圍內(nèi),而這會(huì)導(dǎo)致幻讀問題的產(chǎn)生
  • 記錄鎖-record Lock:鎖定行記錄,索的索引,索引失效,為表鎖
  • 臨鍵鎖-next-key Lock:record lock+gap lock 左開右閉(解決幻讀)

鎖的模式

  • select .... for update
  • 持有寫鎖,別的不可加讀鎖,也不可加寫鎖
  • select .... lock in share mode
  • 持有讀鎖,別的可以再加讀鎖,不可加寫鎖
  • 共享鎖-讀鎖-S鎖
  • 排他鎖-寫鎖-X鎖
  • 意向鎖:讀意向鎖+寫意向鎖
  • 自增鎖

需要的時(shí)候加上,并不是馬上釋放,等事務(wù)提交才釋放,兩階段鎖協(xié)議

3、全局鎖——全庫邏輯備份

4、死鎖

  • 兩個(gè)或多個(gè)事務(wù)在同一資源上相互占用,并請求加鎖時(shí),造成相互等待,無限阻塞
  • innodb回滾擁有最少排他行級鎖的事務(wù)
  • 設(shè)置鎖等待超時(shí)時(shí)間

樂觀鎖與悲觀鎖

  • 悲觀鎖用數(shù)據(jù)庫自帶鎖機(jī)制——寫多
  • 樂觀鎖用version版本機(jī)制或CAS算法——讀多寫少,很少發(fā)生沖突情況

MVCC

是什么:多版本并發(fā)控制。

原理提煉總結(jié):使用版本鏈+Read View

詳解:

版本鏈:同一行數(shù)據(jù)可能有多個(gè)版本

innodb數(shù)據(jù)表每行數(shù)據(jù)記錄會(huì)有幾個(gè)隱藏字段,row_id,事務(wù)ID,回滾指針。

1、Innodb采用主鍵索引(聚簇索引),會(huì)利用主鍵維護(hù)索引,若表沒有主鍵,就用第一個(gè)非空唯一索引,若沒有唯一索引,則用row_id這個(gè)隱藏字段作為主鍵索引。

2、事務(wù)開啟會(huì)向系統(tǒng)申請一個(gè)事務(wù)ID,嚴(yán)格遞增,會(huì)向行記錄插入最近操作它的那個(gè)事務(wù)的ID

3、undolog會(huì)記錄事務(wù)前老版本數(shù)據(jù),然后行記錄中回滾指針會(huì)指向老版本位置,如此形成一條版本鏈。因此可以利用undo log實(shí)現(xiàn)回滾,保證原子性,同時(shí)用于實(shí)現(xiàn)MVCC版本鏈。

圖3 版本鏈形成

Read View讀已提交隔離級別下,會(huì)在每次查詢都生成一個(gè)Read View,可重讀讀只在事務(wù)開始時(shí)生成一個(gè)Read View,以后每次查詢都用這個(gè)Read View,以此實(shí)現(xiàn)不同隔離界別。

Read View里面包含些什么?(一致性視圖)

一個(gè)數(shù)組+up_limit_id(低水位)+low_limit_id(高水位)(這里的up,low沒寫錯(cuò),就是這么定義的)

1、數(shù)組里包含事務(wù)啟動(dòng)時(shí)當(dāng)前活躍事務(wù)ID(未提交事務(wù)),低水位就是活躍事務(wù)最小ID,高水位就是下一次將分配的事務(wù)ID,也就是目前最大事務(wù)ID+1。

數(shù)據(jù)可見性規(guī)則是怎樣實(shí)現(xiàn)的?

數(shù)據(jù)版本的可見性規(guī)則,就是基于數(shù)據(jù)的row trx_id和這個(gè)一致性視圖(Read View)的對比結(jié)果得到的。

視圖數(shù)組把所有的trx_id 分成了幾種不同的情況

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

讀取原理:

某事務(wù)T要訪問數(shù)據(jù)A,先獲取該數(shù)據(jù)A中的事務(wù)id(獲取最近操作它的事務(wù)的事務(wù)ID),對比該事務(wù)T啟動(dòng)時(shí)刻生成的readview:

1、如果在readview的左邊(比readview都小),表示這個(gè)事務(wù)可以訪問這數(shù)據(jù)(在左邊意味著該事務(wù)已經(jīng)提交)

2、如果在readview的右邊(比readview都大),表示這個(gè)版本是由將來啟動(dòng)的事務(wù)生成的,是肯定不可見的;

3、如果當(dāng)前事務(wù)在未提交事務(wù)集合中:

a、若 row trx_id在數(shù)組中,表示這個(gè)版本是由還沒提交的事務(wù)生成的,不可見;

b. 若 row trx_id不在數(shù)組中,表示這個(gè)版本是已經(jīng)提交了的事務(wù)生成的,可見。

不可以訪問,獲取roll_pointer,通過版本鏈取上一版本。

根據(jù)數(shù)據(jù)歷史版本事務(wù)ID再重新與視圖數(shù)組對比。

這樣執(zhí)行下來,雖然期間這一行數(shù)據(jù)被修改過,但是事務(wù)A不論在什么時(shí)候查詢,看到這行數(shù)據(jù)的結(jié)果都是一致的,所以我們稱之為一致性讀。

總之,MYSQL的隔離性便是由MVCC+鎖來保證,各個(gè)隔離級別實(shí)現(xiàn)原理我做了一下歸納總結(jié):

隔離級別原理及解決問題分析:

  • 讀未提交:原理:直接讀取數(shù)據(jù),不能解決任何并發(fā)問題
  • 讀已提交:讀操作不加鎖,寫操作加排他鎖,解決了臟讀。原理:利用MVCC實(shí)現(xiàn),每一句語句執(zhí)行前都會(huì)生成Read View(一致性視圖)
  • 可重復(fù)讀:MVCC實(shí)現(xiàn),只有事務(wù)開始時(shí)會(huì)創(chuàng)建Read View,之后事務(wù)里的其他查詢都用這個(gè)Read View。解決了臟讀、不可重復(fù)讀,快照讀(普通查詢,讀取歷史數(shù)據(jù))使用MVCC解決了幻讀,當(dāng)前讀(讀取最新提交數(shù)據(jù))通過間隙鎖解決幻讀(lock in share mode、for update、update、detete、insert),間隙鎖在可重復(fù)讀下才生效。(默認(rèn)隔離級別)
  • 可串行化:原理:使用鎖,讀加共享鎖,寫加排他鎖,串行執(zhí)行

總結(jié):讀已提交和可重復(fù)讀實(shí)現(xiàn)原理就是MVCC Read View不同的生成時(shí)機(jī)??芍貜?fù)讀只在事務(wù)開始時(shí)生成一個(gè)Read View,之后都用的這個(gè);讀已提交每次執(zhí)行前都會(huì)生成Read View

05一致性

一致性是事務(wù)追求的最終目標(biāo),前問所訴的原子性、持久性和隔離性,其實(shí)都是為了保證數(shù)據(jù)庫狀態(tài)的一致性。

當(dāng)然,上文都是數(shù)據(jù)庫層面的保障,一致性的實(shí)現(xiàn)也需要應(yīng)用層面進(jìn)行保障。也就是你的業(yè)務(wù),比如購買操作只扣除用戶的余額,不減庫存,肯定無法保證狀態(tài)的一致。

你把周圍的人看作魔鬼,你就生活在地獄;你把周圍的人看作天使,你就生活在天堂。

本文轉(zhuǎn)載自微信公眾號「小龍coding」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系小龍coding公眾號。

 

 

責(zé)任編輯:武曉燕 來源: 小龍coding
相關(guān)推薦

2024-12-30 13:58:14

2023-11-27 08:32:02

元素HashMap

2024-12-25 15:44:15

2024-02-04 10:08:34

2019-11-21 08:40:44

面試官優(yōu)化性能

2021-09-17 12:50:10

MySQL數(shù)據(jù)庫ACID

2024-10-15 10:00:06

2021-11-05 10:07:13

Redis哈希表存儲(chǔ)

2021-01-20 07:16:07

冪等性接口token

2025-02-26 12:19:52

2023-11-29 08:00:53

JavaTreeMap底層

2024-09-11 22:51:19

線程通訊Object

2023-11-20 10:09:59

2024-02-20 14:10:55

系統(tǒng)緩存冗余

2015-08-13 10:29:12

面試面試官

2024-05-11 15:11:44

系統(tǒng)軟件部署

2025-04-14 11:41:12

RocketMQ長輪詢配置

2024-10-22 16:39:07

2023-02-08 07:04:20

死鎖面試官單元

2023-11-06 17:39:35

JavaArrayList線程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號