日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

mysql内部实现原理面试_理解完这些基本上能解决面试中MySql的事务问题

發(fā)布時間:2024/7/19 数据库 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql内部实现原理面试_理解完这些基本上能解决面试中MySql的事务问题 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

事務(wù)是指邏輯上的一組操作,要么都執(zhí)行,要么都不執(zhí)行,

事務(wù)的特性(ACID)原子性(Atomicity):事務(wù)是不可分割的工作單元,要么都成功,要么都失敗, 如果事務(wù)中一個sql語句執(zhí)行失敗,則已執(zhí)行的語句也必須回滾,數(shù)據(jù)庫退回到事務(wù)前的狀態(tài)。

一致性(Consistency):事務(wù)不能破壞數(shù)據(jù)的完整性和業(yè)務(wù)的一致性 。例如在銀行轉(zhuǎn)賬時,不管事務(wù)成功還是失敗,雙方錢的總額不變

隔離性(Isolation):一個事務(wù)所操作的數(shù)據(jù)在提交之前,對其他事務(wù)的可見性設(shè)定(一般是不可見)

持久性(Durability):事務(wù)提交之后,所做的修改就會永久保存,不會因?yàn)橄到y(tǒng)故障導(dǎo)致數(shù)據(jù)丟失

嚴(yán)格來說,只有同時滿足數(shù)據(jù)庫的事務(wù)ACID特性才能算一個完整的事務(wù),但現(xiàn)實(shí)中實(shí)現(xiàn)能夠真正滿足的完整的事務(wù)特性少之又少,但是在實(shí)現(xiàn)中也必須盡量達(dá)到事務(wù)要求的特性。

那么事務(wù)ACID特性具體怎么實(shí)現(xiàn)的呢?我們來分析看看,首先先看看事務(wù)的特性。

原子性(Atomicity)

首先我們來看看事務(wù)的原子性特性,看看其如何實(shí)現(xiàn)的?

原子性(Atomicity):事務(wù)是不可分割的工作單元,要么都成功,要么都失敗, 如果事務(wù)中一個sql語句執(zhí)行失敗,則已執(zhí)行的語句也必須回滾,數(shù)據(jù)庫退回到事務(wù)前的狀態(tài)

原子性(Atomicity)的實(shí)現(xiàn)離不開 MySQL的事務(wù)日志 undo log日志類型,當(dāng)事務(wù)需要回滾的時候需要將數(shù)據(jù)庫狀態(tài)回滾到事務(wù)開始前,即需要撤銷所有已經(jīng)成功執(zhí)行的sql語句。那么undo log起了關(guān)鍵性作用:

當(dāng)事務(wù)對數(shù)據(jù)庫進(jìn)行修改時,InnoDB會生成對應(yīng)的undo log;如果事務(wù)執(zhí)行失敗或調(diào)用了rollback,導(dǎo)致事務(wù)需要回滾,便可以利用undo log中的信息將數(shù)據(jù)回滾到修改之前的樣子。

那么undo log是什么呢?每個數(shù)據(jù)變更操作是怎么被記錄下來的呢?

undo log( 回滾日志 )

undo log (回滾日志):是采用段(segment)的方式來記錄的,每個undo操作在記錄的時候占用一個undo log segment。為什么會在數(shù)據(jù)更改操作的時候,記錄了相對應(yīng)的undo log呢?其目的在于:為了保證數(shù)據(jù)的原子性,記錄事務(wù)發(fā)生之前的一個版本,用于回滾,

通過mvcc+undo log實(shí)現(xiàn)innodb事務(wù)可重復(fù)讀和讀取已提交隔離級別。

其中,undo log分為:insert undo log : insert操作中產(chǎn)生的undo log,

update undo log: 對delete 和update操作產(chǎn)生的undo log

數(shù)據(jù)更改的undo log怎么記錄的呢?

因?yàn)閕nsert操作的記錄,只對事務(wù)本身可見,對其他事務(wù)不可見。故該undo log可以在事務(wù)提交后直接刪除,不需要進(jìn)行purge操作,

而Delete操作在事務(wù)中實(shí)際上并不是真正的刪除掉數(shù)據(jù)行,而是一種Delete Mark操作,在記錄上標(biāo)識Delete_Bit,而不刪除記錄。是一種"假刪除",只是做了個標(biāo)記,真正的刪除工作需要后臺purge線程去完成。

update分為兩種情況:update的列是否是主鍵列。如果不是主鍵列,在undo log中直接反向記錄是如何update的。即update是直接進(jìn)行的。

如果是主鍵列,update分兩部執(zhí)行:先刪除該行,再插入一行目標(biāo)行。

與insert undo log 不同的,update undo log日志,當(dāng)事務(wù)提交的時候,innodb不會立即刪除undo log, 會將該事務(wù)對應(yīng)的undo log放入到刪除列表中,未來通過purge線程來刪除。

因?yàn)楹罄m(xù)還可能會用到undo log,如隔離級別為repeatable read時,事務(wù)讀取的都是開啟事務(wù)時的最新提交行版本,只要該事務(wù)不結(jié)束,該行版本就不能刪除(即undo log不能刪除),且undo log分配的頁可重用減少存儲空間和提升性能。Note: purge線程兩個主要作用是:清理undo頁和清除page里面帶有Delete_Bit標(biāo)識的數(shù)據(jù)行。

接著我們來看看事務(wù)的隔離性,看看事務(wù)有哪些隔離級別,而且事務(wù)并發(fā)中會產(chǎn)生什么問題。

隔離性(Isolation)

隔離性(Isolation),是指事務(wù)內(nèi)部的操作與其他事務(wù)是隔離的,并發(fā)執(zhí)行的各個事務(wù)之間不能互相干擾 ,一個事務(wù)所操作的數(shù)據(jù)在提交之前,對其他事務(wù)的可見性設(shè)定(一般是不可見)。

事務(wù)隔離級別

而且數(shù)據(jù)庫為了在并發(fā)下有效保證讀取數(shù)據(jù)正確性,數(shù)據(jù)庫提供了四種事務(wù)隔離級別,分別為:讀未提交(臟讀):允許讀取尚未提交的數(shù)據(jù),允許臟讀

讀已提交( 不可重復(fù)讀 ):允許讀取事務(wù)已經(jīng)提交的數(shù)據(jù)

可重復(fù)讀( 幻讀 ):在同一個事務(wù)內(nèi)的查詢結(jié)果都是和事務(wù)開始時刻查詢一致的( InnoDB默認(rèn)級別 )

串行化:所有事務(wù)逐個依次執(zhí)行, 每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞

其中,不同的隔離級別可能會存在在不同并發(fā)問題,主要并發(fā)問題包括:數(shù)據(jù)丟失: 兩個或多個事務(wù)操作相同數(shù)據(jù),基于最初選定的值更新該行時,由于每個事務(wù)都不知道其他事務(wù)的存在,就會發(fā)生丟失更新問題——最后的更新覆蓋了其他事務(wù)所做的更新

**臟讀:**讀到了其他事務(wù)還未提交的數(shù)據(jù),事務(wù)A讀取了事務(wù)B更新的數(shù)據(jù),然后B回滾操作,那么A讀取到的數(shù)據(jù)是臟數(shù)據(jù)

**不可重復(fù)讀(重點(diǎn)是修改):**在一個事務(wù)中,先后進(jìn)行兩次相同的讀取,由于另一個事務(wù)修改了數(shù)據(jù),導(dǎo)致前后兩次結(jié)果的不一致,事務(wù)A多次讀取同一數(shù)據(jù),事務(wù)B在事務(wù)A多次讀取的過程中,對數(shù)據(jù)作了更新并提交,導(dǎo)致事務(wù)A多次讀取同一數(shù)據(jù)時,結(jié)果不一致。

幻讀(重點(diǎn)是新增、刪除): 在一個事務(wù)中,先后進(jìn)行兩次相同的讀取(一般是范圍查詢),由于另一個事務(wù)新增或刪除了數(shù)據(jù),導(dǎo)致前后兩次結(jié)果不一致

不可重復(fù)讀和幻讀的區(qū)別?

不可重復(fù)讀和幻讀最大的區(qū)別,就在于如何通過鎖機(jī)制來解決他們產(chǎn)生的問題,

使用鎖機(jī)制來實(shí)現(xiàn)這兩種隔離級別,在可重復(fù)讀中,相同sql第一次讀取到數(shù)據(jù)后就將這些數(shù)據(jù)加鎖,其它事務(wù)無法更新操作這些數(shù)據(jù)來實(shí)現(xiàn)可重復(fù)讀了隔離。

但這種處理方式卻無法鎖住insert的數(shù)據(jù),因此會出現(xiàn)當(dāng)事務(wù)A先前讀取了數(shù)據(jù),事務(wù)B再insert數(shù)據(jù)提交,結(jié)果發(fā)現(xiàn)事務(wù)A就會發(fā)現(xiàn)莫名其妙多了些數(shù)據(jù),這就是幻讀,不能通過行鎖來避免 。

了解了并發(fā)問題后,來看看不同的隔離級別可能會存在在不同并發(fā)問題:事務(wù)隔離級別臟讀不可重復(fù)讀幻讀

為了實(shí)現(xiàn)事務(wù)隔離,延伸出了數(shù)據(jù)庫鎖。其中,innodb事務(wù)的隔離級別是由鎖機(jī)制和MVCC(多版本并發(fā)控制)來實(shí)現(xiàn)的

那我們來先看看鎖的原理,怎么使用鎖來實(shí)現(xiàn)事務(wù)隔離的呢?

鎖機(jī)制

鎖機(jī)制的基本工作原理,事務(wù)在修改數(shù)據(jù)之前,需要先獲得相應(yīng)的鎖;獲得鎖之后,事務(wù)便可以修改數(shù)據(jù);該事務(wù)操作期間,這部分?jǐn)?shù)據(jù)是鎖定的,其他事務(wù)如果需要修改數(shù)據(jù),需要等待當(dāng)前事務(wù)提交或回滾后釋放鎖,

MySQL主要分成三種類型(級別)的鎖機(jī)制:表級鎖:最大顆粒度的鎖機(jī)制,鎖定資源爭用的概率也會最高 ,并發(fā)度最低 ,但開銷小,加鎖快,不會出現(xiàn)死鎖,

行級鎖:最大顆粒度的鎖機(jī)制很小, 發(fā)生鎖定資源爭用的概率也最小,能夠給予應(yīng)用程序盡可能大的并發(fā)處理能力而提高一些需要高并發(fā)應(yīng)用系統(tǒng)的整體性能 ,但 開銷大,加鎖慢;會出現(xiàn)死鎖 ,

頁級鎖: 開銷和加鎖時間界于表鎖和行鎖之間;會出現(xiàn)死鎖;鎖定粒度界于表鎖和行鎖之間,并發(fā)度一般

而且不同的存儲引擎支持不同的的鎖機(jī)制,主要分析一下InnoDB鎖。

InnoDB鎖

InnoDB實(shí)現(xiàn)了以下兩種類型的行鎖共享鎖(S鎖、行鎖):多個事務(wù)對同一數(shù)據(jù)行可以共享一把鎖,只能讀不能修改

排它鎖(X鎖、行鎖):一個事務(wù)獲取一個數(shù)據(jù)行的排它鎖,那么其他事務(wù)將不能再獲取該行的鎖(共享鎖、排它鎖), 允許獲取排他鎖的事務(wù)更新數(shù)據(jù)

對于UPDATE,DELETE,INSERT操作, InnoDB會自動給涉及及數(shù)據(jù)集加排他鎖(X);對于普通SELECT語句,InnoDB不會加任何鎖,

而且因?yàn)镮nnoDB引擎允許行鎖和表鎖共存,實(shí)現(xiàn)多粒度鎖機(jī)制,使用意向鎖實(shí)現(xiàn)表鎖機(jī)制,意向共享鎖(IS鎖、表鎖):當(dāng)事務(wù)準(zhǔn)備給數(shù)據(jù)行加共享鎖時,會先給表加上一個意向共享鎖。意向共享鎖之間是兼容的

意向排它鎖(IX鎖、表鎖):當(dāng)事務(wù)準(zhǔn)備給數(shù)據(jù)行加排它鎖時,會先給表加上一個意向排它鎖。意向排它鎖之間是兼容的

意向鎖(IS、IX)是InnoDB數(shù)據(jù)操作之前自動加的,不需要用戶干預(yù)。它的意義在于:當(dāng)事務(wù)想去進(jìn)行鎖表時,可以先判斷意向鎖是否存在,存在時則可快速返回該表不能啟用表鎖,否則就需要等待

如果一個事務(wù)請求的鎖模式與當(dāng)前的鎖兼容,InnoDB就請求的鎖授予該事務(wù);反之,如果兩者兩者不兼容,該事務(wù)就要等待鎖釋放。

InnoDB行鎖

InnoDB的行鎖是通過給索引上的索引項(xiàng)加鎖來實(shí)現(xiàn)的。只有通過索引檢索數(shù)據(jù),才能使用行鎖,否則將使用表鎖(鎖住索引的所有記錄)

臨鍵鎖(next-key),可以防止幻讀。根據(jù)索引,劃分為一個個左開右閉的區(qū)間。當(dāng)進(jìn)行范圍查詢的時候,若命中索引且能夠檢索到數(shù)據(jù),則鎖住記錄所在的區(qū)間和它的下一個區(qū)間,

其實(shí),臨鍵鎖(Next-Key)=記錄鎖(Record Locks)+間隙鎖(Gap Locks),當(dāng)我們用范圍條件檢索數(shù)據(jù)而不是相等條件檢索數(shù)據(jù),并請求共享或排他鎖時,InnoDB會給符合范圍條件的已有數(shù)據(jù)記錄的索引項(xiàng)加鎖;對于鍵值在條件范圍內(nèi)但并不存在的記錄,叫做間隙(GAP)。

當(dāng)使用唯一索引,且記錄存在的精準(zhǔn)查詢時,使用Record Locks記錄鎖

具體的使用體現(xiàn)在哪里呢?如下圖所示:范圍查詢,記錄存在

當(dāng)記錄不存在(不論是等值查詢,還是范圍查詢)時,next-key將退化成Gap Lock(間隙鎖)

當(dāng)條件是精準(zhǔn)匹配(即為等值查詢時)且記錄存在時,并且是唯一索引,臨鍵鎖(Next-Key)退化成Record Lock(記錄鎖)

當(dāng)條件是精準(zhǔn)匹配(即為等值查詢時)且記錄存在,但不是唯一索引時,臨鍵鎖(Next-Key)會有精準(zhǔn)值的數(shù)據(jù)會增加Record Lock(記錄鎖)和精準(zhǔn)值前后的區(qū)間的數(shù)據(jù)會增加Gap Lock(間隙鎖)。

如何使用鎖解決并發(fā)問題

利用鎖解決臟讀、不可重復(fù)讀、幻讀X鎖解決臟讀

S鎖解決不可重復(fù)讀

臨鍵鎖解決幻讀

Multiversion concurrency control (MVCC 多版本并發(fā)控制)

InnoDB的MVCC是通過在每行記錄后面保存兩個隱藏的列來實(shí)現(xiàn)的,一個保存了行的事務(wù)ID(事務(wù)ID就會遞增 ),一個保存了行的回滾段的指針 。

每開始一個新的事務(wù),都會自動遞增產(chǎn) 生一個新的事務(wù)id。事務(wù)開始時刻的會把事務(wù)id放到當(dāng)前事務(wù)影響的行事務(wù)id中,而DB_ROLL_PTR表示指向該行回滾段的指針,該行記錄上所有版本數(shù)據(jù),在undo中都通過鏈表形式組織,該值實(shí)際指向undo中該行的歷史記錄鏈表,

在并發(fā)訪問數(shù)據(jù)庫時,對正在事務(wù)中的數(shù)據(jù)做MVCC多版本的管理,以避免寫操作阻塞讀操作,并且會通過比較版本解決幻讀。

而且MVCC只在REPEATABLE READ和READ COMMITIED兩個隔離級別下才會工作,其中,MVCC實(shí)現(xiàn)實(shí)質(zhì)就是保存數(shù)據(jù)在某個時間點(diǎn)的快照來實(shí)現(xiàn)的。 那哪些操作是快照讀?

快照讀和當(dāng)前讀

快照讀,innodb快照讀,數(shù)據(jù)的讀取將由 cache(原本數(shù)據(jù)) + undo(事務(wù)修改前的數(shù)據(jù)) 兩部分組成普通的select,比如 select * from table where ?;

當(dāng)前讀,SQL讀取的數(shù)據(jù)是最新版本。通過鎖機(jī)制來保證讀取的數(shù)據(jù)無法通過其他事務(wù)進(jìn)行修改UPDATE

DELETE

INSERT

SELECT … LOCK IN SHARE MODE

SELECT … FOR UPDATE

其中當(dāng)前讀中,只有SELECT … LOCK IN SHARE MODE對讀取記錄加S鎖 (共享鎖)外,其他的操作,都加的是X鎖 (排它鎖)。

那么在RR隔離級別下,MVCC具體是如何操作的。

RR隔離級別下,MVCC具體操作

SELECT操作,InnoDB遵循以后兩個規(guī)則執(zhí)行:InnoDB只查找版本早于當(dāng)前事務(wù)版本的數(shù)據(jù)行(即行的事務(wù)編號小于或等于當(dāng)前事務(wù)的事務(wù)編號,這樣可以確保事務(wù)讀取的行,要么是在事務(wù)開始前已經(jīng)存在的,要么是事務(wù)自身插入或者修改過的記錄。

行的刪除版本要么未定義,讀取到事務(wù)開始之前狀態(tài)的版本,這可以確保事務(wù)讀取到的行,在事務(wù)開始之前未被刪除.只有同時滿足的兩者的記錄,才能返回作為查詢結(jié)果.

INSERT:InnoDB為新插入的每一行保存當(dāng)前事務(wù)編號作為行版本號。

DELETE:InnoDB為刪除的每一行保存當(dāng)前事務(wù)編號作為行刪除標(biāo)識。

UPDATE:InnoDB為插入一行新記錄,保存當(dāng)前事務(wù)編號作為行版本號,同時保存當(dāng)前事務(wù)編號到原來的行作為行刪除標(biāo)識。

保存這兩個額外系統(tǒng)版本號,使大多數(shù)讀操作都可以不用加鎖。這樣設(shè)計(jì)使得讀數(shù)據(jù)操作很簡單,性能很好,并且也能保證只會讀取到符合標(biāo)準(zhǔn)的行,不足之處是每行記錄都需要額外的存儲空間,需要做更多的行檢查工作,以及一些額外的維護(hù)工作。

分析完了原子性和隔離性,我們繼續(xù)看看事務(wù)的持久性。

持久性(Durability)

持久性(Durability):事務(wù)提交之后,所做的修改就會永久保存,不會因?yàn)橄到y(tǒng)故障導(dǎo)致數(shù)據(jù)丟失,

而且其實(shí)現(xiàn)的關(guān)鍵在于redo log, 在執(zhí)行SQL時會保存已執(zhí)行的SQL語句到一個指定的Log文件,當(dāng)執(zhí)行recovery時重新執(zhí)行redo log記錄的SQL操作。

那么redo log如何實(shí)現(xiàn)的呢?

redo log

當(dāng)向數(shù)據(jù)庫寫入數(shù)據(jù)時,執(zhí)行過程會首先寫入Buffer Pool,Buffer Pool中修改的數(shù)據(jù)會定期刷新到磁盤中(這一過程稱為刷臟),這整一過程稱為redo log。redo log 分為:Buffer Pool內(nèi)存中的日志緩沖(redo log buffer),該部分日志是易失性的;

磁盤上的重做日志文件(redo log file),該部分日志是持久的。

Buffer Pool的使用可以大大提高了讀寫數(shù)據(jù)的效率,但是也帶了新的問題:如果MySQL宕機(jī),而此時Buffer Pool中修改的數(shù)據(jù)在內(nèi)存還沒有刷新到磁盤,就會導(dǎo)致數(shù)據(jù)的丟失,事務(wù)的持久性無法保證。

為了確保事務(wù)的持久性,在當(dāng)事務(wù)提交時,會調(diào)用fsync接口對redo log進(jìn)行刷盤, (即redo log buffer寫日志到磁盤的redo log file中 ),刷新頻率由 innodb_flush_log_at_trx_commit變量來控制的:0 : 每秒刷新寫入到磁盤中的,當(dāng)系統(tǒng)崩潰,會丟失1秒鐘的數(shù)據(jù) ;

1: 事務(wù)每次提交都寫入磁盤;

2:每秒刷新寫入到磁盤中的,但跟0是有區(qū)別的。

redo log有更加詳細(xì)的解讀,后續(xù)有時間再補(bǔ)上,到現(xiàn)在為止,已經(jīng)將事務(wù)三個特性都理解了,那事務(wù)一致性呢?

一致性(Consistency)

一致性(Consistency):事務(wù)不能破壞數(shù)據(jù)的完整性和業(yè)務(wù)的一致性 :數(shù)據(jù)的完整性: 實(shí)體完整性、列完整性(如字段的類型、大小、長度要符合要求)、外鍵約束等

業(yè)務(wù)的一致性:例如在銀行轉(zhuǎn)賬時,不管事務(wù)成功還是失敗,雙方錢的總額不變。

那是如何保證數(shù)據(jù)一致性的?

其實(shí)數(shù)據(jù)一致性是通過事務(wù)的原子性、持久性和隔離性來保證的原子性:語句要么全執(zhí)行,要么全不執(zhí)行,是事務(wù)最核心的特性,事務(wù)本身就是以原子性來定義的;實(shí)現(xiàn)主要基于undo log

持久性:保證事務(wù)提交后不會因?yàn)殄礄C(jī)等原因?qū)е聰?shù)據(jù)丟失;實(shí)現(xiàn)主要基于redo log

隔離性:保證事務(wù)執(zhí)行盡可能不受其他事務(wù)影響;InnoDB默認(rèn)的隔離級別是RR,RR的實(shí)現(xiàn)主要基于鎖機(jī)制(包含next-key lock)、MVCC(包括數(shù)據(jù)的隱藏列、基于undo log的版本鏈、ReadView)

總結(jié)

其中要同時滿足ACID特性,這樣的事務(wù)少之又少。實(shí)際中很多例子都只是滿足一些特性,比如:MySQL的NDB Cluster事務(wù)不滿足持久性和隔離性;

InnoDB默認(rèn)事務(wù)隔離級別是可重復(fù)讀,不滿足隔離性;

Oracle默認(rèn)的事務(wù)隔離級別為READ COMMITTED,不滿足隔離性

所以我們只能使用這個四個維度的特性去衡量事務(wù)的操作。

很多PHPer在進(jìn)階的時候總會遇到一些問題和瓶頸,業(yè)務(wù)代碼寫多了沒有方向感,不知道該從那里入手去提升,對此我整理了一些資料,包括但不限于:分布式架構(gòu)、高可擴(kuò)展、高性能、高并發(fā)、服務(wù)器性能調(diào)優(yōu)、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql優(yōu)化、shell腳本、Docker、微服務(wù)、高并發(fā)、Nginx等多個知識點(diǎn)高級進(jìn)階干貨需要的可以免費(fèi)分享給大家。點(diǎn)擊領(lǐng)取資料

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎

總結(jié)

以上是生活随笔為你收集整理的mysql内部实现原理面试_理解完这些基本上能解决面试中MySql的事务问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。