摘要:關(guān)系型數(shù)據(jù)庫中的事務(wù)管理詳解并發(fā)控制與事務(wù)日志數(shù)據(jù)庫系統(tǒng)的萌芽出現(xiàn)于年代。并發(fā)控制并發(fā)控制旨在針對數(shù)據(jù)庫中對事務(wù)并行的場景,保證中的一致性與隔離性。絕大部分數(shù)據(jù)庫會采用鎖或者數(shù)據(jù)版本控制的方式來處理并發(fā)控制問題。
本文節(jié)選自:關(guān)系型數(shù)據(jù)庫理論 https://url.wx-coder.cn/DJNQn ,涉及引用/整理的文章列舉在了 Database-List。關(guān)系型數(shù)據(jù)庫中的事務(wù)管理詳解:并發(fā)控制與事務(wù)日志
數(shù)據(jù)庫系統(tǒng)的萌芽出現(xiàn)于 60 年代。當時計算機開始廣泛地應(yīng)用于數(shù)據(jù)管理,對數(shù)據(jù)的共享提出了越來越高的要求。傳統(tǒng)的文件系統(tǒng)已經(jīng)不能滿足人們的需要。能夠統(tǒng)一管理和共享數(shù)據(jù)的數(shù)據(jù)庫管理系統(tǒng)(DBMS)應(yīng)運而生。1961 年通用電氣公司(General ElectricCo.)的 Charles Bachman 成功地開發(fā)出世界上第一個網(wǎng)狀 DBMS 也是第一個數(shù)據(jù)庫管理系統(tǒng)—— 集成數(shù)據(jù)存儲(Integrated DataStore IDS),奠定了網(wǎng)狀數(shù)據(jù)庫的基礎(chǔ)。
1970 年,IBM 的研究員 E.F.Codd 博士在刊物 Communication of the ACM 上發(fā)表了一篇名為“A Relational Modelof Data for Large Shared Data Banks”的論文,提出了關(guān)系模型的概念,奠定了關(guān)系模型的理論基礎(chǔ)。1974 年,IBM 的 Ray Boyce 和 DonChamberlin 將 Codd 關(guān)系數(shù)據(jù)庫的 12 條準則的數(shù)學(xué)定義以簡單的關(guān)鍵字語法表現(xiàn)出來,里程碑式地提出了 SQL(Structured Query Language)語言。在很長的時間內(nèi),關(guān)系數(shù)據(jù)庫(如 MySQL 和 Oracle)對于開發(fā)任何類型的應(yīng)用程序都是首選,巨石型架構(gòu)也是應(yīng)用程序開發(fā)的標準架構(gòu)。
本文即是對關(guān)系型數(shù)據(jù)庫中的事務(wù)管理相關(guān)內(nèi)容進行討論。
事務(wù)基礎(chǔ) ACID事務(wù)提供一種全做,或不做(All or Nothing)的機制,即將一個活動涉及的所有操作納入到一個不可分割的執(zhí)行單元,組成事務(wù)的所有操作只有在所有操作均能正常執(zhí)行的情況下方能提交,只要其中任一操作執(zhí)行失敗,都將導(dǎo)致整個事務(wù)的回滾。數(shù)據(jù)庫事務(wù)具有 ACID 屬性,即原子性(Atomic)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability),在分布式事務(wù) https://url.wx-coder.cn/7p8Xx 中我們也會討論分布式系統(tǒng)中應(yīng)該如何實現(xiàn)事務(wù)機制。
ACID 包含了描述事務(wù)操作的整體性的原子性,描述事務(wù)操作下數(shù)據(jù)的正確性的一致性,描述事務(wù)并發(fā)操作下數(shù)據(jù)的正確性的隔離性,描述事務(wù)對數(shù)據(jù)修改的可靠性的持久性。針對數(shù)據(jù)庫的一系列操作提供了一種從失敗狀態(tài)恢復(fù)到正常狀態(tài)的方法,使數(shù)據(jù)庫在異常狀態(tài)下也能夠保持數(shù)據(jù)的一致性,且面對并發(fā)訪問時,數(shù)據(jù)庫能夠提供一種隔離方法,避免彼此間的操作互相干擾。
原子性(Atomicity):整個事務(wù)中的所有操作,要么全部完成,要么全部不完成,不可能停滯在中間某個環(huán)節(jié)。事務(wù)在執(zhí)行過程中發(fā)生錯誤,會被回滾(Rollback)到事務(wù)開始前的狀態(tài),就像這個事務(wù)從來沒有執(zhí)行過一樣。例如:銀行轉(zhuǎn)賬,從 A 賬戶轉(zhuǎn) 100 元至 B 賬戶,分為兩個步驟:從 A 賬戶取 100 元;存入 100 元至 B 賬戶。這兩步要么一起完成,要么一起不完成。
一致性(Consistency):在事務(wù)開始之前和事務(wù)結(jié)束以后,數(shù)據(jù)庫數(shù)據(jù)的一致性約束沒有被破壞;即當事務(wù) A 與 B 同時運行,無論 A,B 兩個事務(wù)的結(jié)束順序如何,數(shù)據(jù)庫都會達到統(tǒng)一的狀態(tài)。
隔離性(Isolation):數(shù)據(jù)庫允許多個并發(fā)事務(wù)同時對數(shù)據(jù)進行讀寫和修改的能力,如果一個事務(wù)要訪問的數(shù)據(jù)正在被另外一個事務(wù)修改,只要另外一個事務(wù)未提交,它所訪問的數(shù)據(jù)就不受未提交事務(wù)的影響。隔離性可以防止多個事務(wù)并發(fā)執(zhí)行時由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致。 例如:現(xiàn)有有個交易是從 A 賬戶轉(zhuǎn) 100 元至 B 賬戶,在這個交易事務(wù)還未完成的情況下,如果此時 B 查詢自己的賬戶,是看不到新增加的 100 元的。
持久性(Durability):當某個事務(wù)一旦提交,無論數(shù)據(jù)庫崩潰還是其他未知情況,該事務(wù)的結(jié)果都能夠被持久化保存下來。
隔離級別SQL 標準定義了 4 類隔離級別,包括了一些具體規(guī)則,用來限定事務(wù)內(nèi)外的哪些改變是可見的,哪些是不可見的。低級別的隔離級一般支持更高的并發(fā)處理,并擁有更低的系統(tǒng)開銷。
隔離級別 | 臟讀(Dirty Read ) | 不可重復(fù)讀(NonRepeatable Read ) | 幻讀(Phantom Read ) |
---|---|---|---|
未提交讀(Read Uncommitted) | 可能 | 可能 | 可能 |
提交讀(Read Committed ) | 不可能 | 可能 | 可能 |
可重復(fù)讀(Repeatable Read ) | 不可能 | 不可能 | 可能 |
可串行化(Serializable ) | 不可能 | 不可能 | 不可能 |
在該隔離級別,所有事務(wù)都可以看到其他未提交事務(wù)的執(zhí)行結(jié)果。本隔離級別很少用于實際應(yīng)用,因為它的性能也不比其他級別好多少。讀取未提交的數(shù)據(jù),也被稱之為臟讀(Dirty Read)。
Read Committed 提交讀這是大多數(shù)數(shù)據(jù)庫系統(tǒng)的默認隔離級別比如 Sql Server, Oracle 等,但不是 MySQL 默認的。它滿足了隔離的簡單定義:一個事務(wù)只能看見已經(jīng)提交事務(wù)所做的改變。這種隔離級別也支持所謂的不可重復(fù)讀(Nonrepeatable Read),因為同一事務(wù)的其他實例在該實例處理其間可能會有新的 Commit,所以同一查詢可能返回不同結(jié)果。
Repeatable Read | 重復(fù)讀當隔離級別設(shè)置為 Repeatable Read 時,可以避免不可重復(fù)讀。不可重復(fù)讀是指事務(wù) T1 讀取數(shù)據(jù)后,事務(wù) T2 執(zhí)行更新操作,使 T1 無法再現(xiàn)前一次讀取結(jié)果。具體地講,不可重復(fù)讀包括三種情況:
事務(wù) T1 讀取某一數(shù)據(jù)后,事務(wù) T2 對其做了修改,當事務(wù) T1 再次讀該數(shù)據(jù)時,得到與前一次不同的值。例如,T1 讀取 B=100 進行運算,T2 讀取同一數(shù)據(jù) B,對其進行修改后將 B=200 寫回數(shù)據(jù)庫。T1 為了對讀取值校對重讀 B,B 已為 200,與第一次讀取值不一致。
事務(wù) T1 按一定條件從數(shù)據(jù)庫中讀取了某些數(shù)據(jù)記錄后,事務(wù) T2 刪除了其中部分記錄,當 T1 再次按相同條件讀取數(shù)據(jù)時,發(fā)現(xiàn)某些記錄神密地消失了。
事務(wù) T1 按一定條件從數(shù)據(jù)庫中讀取某些數(shù)據(jù)記錄后,事務(wù) T2 插入了一些記錄,當 T1 再次按相同條件讀取數(shù)據(jù)時,發(fā)現(xiàn)多了一些記錄,也就是幻讀。
這是 MySQL 的默認事務(wù)隔離級別,它確保在一個事務(wù)內(nèi)的相同查詢條件的多次查詢會看到同樣的數(shù)據(jù)行,都是事務(wù)開始時的數(shù)據(jù)快照。雖然 Repeatable Read 避免了不可重復(fù)讀,但還有可能出現(xiàn)幻讀。簡單說,就是當某個事務(wù)在讀取某個范圍內(nèi)的記錄時,另外的一個事務(wù)又在該范圍內(nèi)插入新的記錄。在之前的事務(wù)在讀取該范圍的記錄時,就會產(chǎn)生幻行,InnoDB 通過間隙鎖(next-key locking)策略防止幻讀的出現(xiàn)。
Serializable | 序列化Serializable 是最高的事務(wù)隔離級別,它通過強制事務(wù)排序,使之不可能相互沖突,從而解決幻讀問題。簡言之,它是在每個讀的數(shù)據(jù)行上加上共享鎖。在這個級別,可能導(dǎo)致大量的超時現(xiàn)象和鎖競爭。該隔離級別代價也花費最高,性能很低,一般很少使用,在該級別下,事務(wù)順序執(zhí)行,不僅可以避免臟讀、不可重復(fù)讀,還避免了幻讀。
并發(fā)控制并發(fā)控制旨在針對數(shù)據(jù)庫中對事務(wù)并行的場景,保證 ACID 中的一致性(Consistency)與隔離性(Isolation)。假如所有的事務(wù)都僅進行數(shù)據(jù)讀取,那么事務(wù)之間并不會有沖突;而一旦某個事務(wù)讀取了正在被其他事務(wù)修改的數(shù)據(jù)或者兩個事務(wù)修改了相同的數(shù)據(jù),那么數(shù)據(jù)庫就必須來保證事務(wù)之間的隔離,來避免某個事務(wù)因為未見最新的數(shù)據(jù)而造成的誤操作。解決并發(fā)控制問題最理想的方式就是能夠每當某個事務(wù)被創(chuàng)建或者停止的時候,監(jiān)控所有事務(wù)的所有操作,判斷是否存在沖突的事務(wù),然后對沖突事務(wù)中的操作進行重排序以盡可能少地減少沖突,而后以特定的順序運行這些操作。絕大部分數(shù)據(jù)庫會采用鎖(Locks)或者數(shù)據(jù)版本控制(Data Versioning)的方式來處理并發(fā)控制問題。
數(shù)據(jù)庫技術(shù)中主流的三種并發(fā)控制技術(shù)分別是: Multi-version Concurrency Control (MVCC), Strict Two-Phase Locking (S2PL), 以及 Optimistic Concurrency Control (OCC),每種技術(shù)也都有很多的變種。在 MVCC 中,每次寫操作都會在舊的版本之上創(chuàng)建新的版本,并且會保留舊的版本。當某個事務(wù)需要讀取數(shù)據(jù)時,數(shù)據(jù)庫系統(tǒng)會從所有的版本中選取出符合該事務(wù)隔離級別要求的版本。MVCC 的最大優(yōu)勢在于讀并不會阻塞寫,寫也不會阻塞讀;而像 S2PL 這樣的系統(tǒng),寫事務(wù)會事先獲取到排他鎖,從而會阻塞讀事務(wù)。PostgreSQL 以及 Oracle 等 RDBMS 實際使用了所謂的 Snapshot Isolation(SI)這個 MVCC 技術(shù)的變種。Oracle 引入了額外的 Rollback Segments,當寫入新的數(shù)據(jù)時,老版本的數(shù)據(jù)會被寫入到 Rollback Segment 中,隨后再被覆寫到實際的數(shù)據(jù)塊。PostgreSQL 則是使用了相對簡單的實現(xiàn)方式,新的數(shù)據(jù)對象會被直接插入到關(guān)聯(lián)的 Table Page 中;而在讀取表數(shù)據(jù)的時候,PostgreSQL 會通過可見性檢測規(guī)則(Visibility Check Rules)來選擇合適的版本。
鎖管理器(Lock Manager)基于鎖的方式基礎(chǔ)理念為:如果某個事務(wù)需要數(shù)據(jù),則對數(shù)據(jù)加鎖,操作完畢后釋放鎖;如果過程中其他事務(wù)需要鎖,則需要等到該事務(wù)釋放數(shù)據(jù)鎖,這種鎖也就是所謂的排他鎖(Exclusive Lock)。不過使用排他鎖會帶來極大的性能損耗,其會導(dǎo)致其他那些僅需要讀取數(shù)據(jù)的事務(wù)也陷入等待。另一種加鎖的方式稱為共享鎖(Shared Lock),當兩個事務(wù)都聲明讀取數(shù)據(jù) A 時,它們會分別給 A 添加共享鎖;對于此事需要修改數(shù)據(jù) A 的事務(wù)而言,它必須等待所有的共享鎖釋放完畢之后才能針對數(shù)據(jù) A 添加排他鎖。同樣地,對于已經(jīng)被設(shè)置了排他鎖的數(shù)據(jù),僅有讀取請求的事務(wù)同樣需要等到該排他鎖被釋放后才能添加共享鎖。
從鎖定的數(shù)據(jù)范圍鎖粒度(Lock Granularity)來看分為:
表鎖:管理鎖的開銷最小,同時允許的并發(fā)量也最小的鎖機制。MyIsam 存儲引擎使用的鎖機制。當要寫入數(shù)據(jù)時,把整個表都鎖上,此時其他讀、寫動作一律等待。在 MySql 中,除了 MyIsam 存儲引擎使用這種鎖策略外,MySql 本身也使用表鎖來執(zhí)行某些特定動作,比如 ALTER TABLE.
行鎖:可以支持最大并發(fā)的鎖策略。InnoDB 和 Falcon 兩種存儲引擎都采用這種策略。
鎖管理器(Lock Manager)即負責分配與釋放鎖,大部分數(shù)據(jù)庫是以哈希表的方式來存放持有鎖以及等待鎖的事務(wù)。在 MySQL 實戰(zhàn) https://url.wx-coder.cn/Tu5dq 中我們也討論了如何觸發(fā)鎖機制,譬如查詢加鎖,select * from testlock where id=1 for update;,即查詢時不允許更改,該語句在自動提交為 off 或事務(wù)中生效,相當于更改操作,模擬加鎖;而更像類操作 update testlock name=name; 則是會自動加鎖。
同樣的,參考并發(fā)編程導(dǎo)論 https://url.wx-coder.cn/Yagu8 中的討論,只要存在鎖的地方就會存在死鎖(Deadlock)的可能性:
在發(fā)生死鎖的時候,鎖管理器會根據(jù)一定的規(guī)則來選取應(yīng)該終止或者被回滾的事務(wù):
根據(jù)是否能最小化需要被回滾的數(shù)據(jù);
根據(jù)事務(wù)發(fā)生的先后順序;
根據(jù)事務(wù)執(zhí)行所需要的時間,以盡可能避免饑餓狀態(tài)的出現(xiàn);
根據(jù)需要回滾的關(guān)聯(lián)事務(wù)的數(shù)目;
避免死鎖,確保純隔離的最簡單方法是在事務(wù)開始時獲取鎖并在事務(wù)結(jié)束時釋放鎖。這意味著事務(wù)必須在啟動之前等待其所有鎖,并且在事務(wù)結(jié)束時釋放事務(wù)持有的鎖,這種方式會浪費很多時間來等待所有鎖。實際的數(shù)據(jù)庫,譬如 DB2 與 SQL Server 中往往采取兩階段鎖協(xié)議(Two-Phase Locking Protocol),即將事務(wù)過程切分為兩個階段:
Growing Phase: 該階段僅可以獲取鎖,而不可以釋放鎖。
Shrinking Phase: 該階段僅可以釋放鎖,而不可以獲取新的鎖。
該策略能夠減少其他事務(wù)等待鎖的時間,并且避免某個事務(wù)在中途修改了并不是它初次申請的數(shù)據(jù)。
MVCC在并發(fā)編程導(dǎo)論 https://url.wx-coder.cn/Yagu8 中我們討論了兩種不同類型的鎖:樂觀鎖(Optimistic Lock)與悲觀鎖(Pessimistic Lock),前文介紹的各種鎖即是悲觀鎖,而 MVCC(Multiple Version Concurrency Control) 這樣的基于數(shù)據(jù)版本的鎖則是樂觀鎖,它能夠保證讀寫操作之間不會相互阻塞:
每個事務(wù)都可以在同一時間修改相同的數(shù)據(jù);
每個事務(wù)會保有其需要的數(shù)據(jù)副本;
如果兩個事務(wù)修改了相同的數(shù)據(jù),那么僅有單個更改操作會被接收,另一個操作會被回滾或者重新執(zhí)行。
樂觀鎖,大多是基于數(shù)據(jù)版本(Version)記錄機制實現(xiàn)。數(shù)據(jù)版本即為數(shù)據(jù)增加一個版本標識,在基于數(shù)據(jù)庫表的版本解決方案中,一般是通過為數(shù)據(jù)庫表增加一個 version 字段來實現(xiàn)。讀取出數(shù)據(jù)時,將此版本號一同讀出,之后更新時,對此版本號加一。此時,將提交數(shù)據(jù)的版本數(shù)據(jù)與數(shù)據(jù)庫表對應(yīng)記錄的當前版本信息進行比對,如果提交的數(shù)據(jù)版本號大于數(shù)據(jù)庫表當前版本號,則予以更新,否則認為是過期數(shù)據(jù)。而 PostgreSQL 中則是依賴于 txid 以及 Commit Log 結(jié)合而成的可見性檢測機制來實現(xiàn) MVCC,詳情可以參考 PostgreSQL 架構(gòu)機制 https://url.wx-coder.cn/SgRDQ 中關(guān)于并發(fā)控制相關(guān)的介紹。
日志管理器(Log Manager)數(shù)據(jù)庫事務(wù)由具體的 DBMS 系統(tǒng)來保障操作的原子性,同一個事務(wù)當中,如果有某個操作執(zhí)行失敗,則事務(wù)當中的所有操作都需要進行回滾,回到事務(wù)執(zhí)行前的狀態(tài)。導(dǎo)致事務(wù)失敗的原因有很多,可能是因為修改不符合表的約束規(guī)則,也有可能是網(wǎng)絡(luò)異常,甚至是存儲介質(zhì)故障等,而一旦事務(wù)失敗,則需要對所有已作出的修改操作進行還原,使數(shù)據(jù)庫的狀態(tài)恢復(fù)到事務(wù)執(zhí)行前的狀態(tài),以保障數(shù)據(jù)的一致性,使修改操作要么全部成功、要么全部失敗,避免存在中間狀態(tài)。
訪問磁盤中的數(shù)據(jù)往往速度較慢,換言之,內(nèi)存中數(shù)據(jù)的訪問速度還是遠快于 SSD 中的數(shù)據(jù)訪問速度。基于這個考量,基本上所有數(shù)據(jù)庫引擎都盡可能地避免訪問磁盤數(shù)據(jù)。并且無論數(shù)據(jù)庫表還是數(shù)據(jù)庫索引都被劃分為了固定大小的數(shù)據(jù)頁(譬如 8 KB)。當我們需要讀取表或者索引中的數(shù)據(jù)時,關(guān)系型數(shù)據(jù)庫會將磁盤中的數(shù)據(jù)頁映射入存儲緩沖區(qū)。當我們需要修改數(shù)據(jù)時,關(guān)系型數(shù)據(jù)庫首先會修改內(nèi)存頁中的數(shù)據(jù),然后利用 fsync 這樣的同步工具將改變同步回磁盤中。
不過一旦數(shù)據(jù)庫突發(fā)崩潰,那么緩沖區(qū)中的數(shù)據(jù)也就丟失,最終打破了事務(wù)的持久性。另一個極端情況而言,我們也可以隨時將數(shù)據(jù)寫入到磁盤中,但是在崩潰的時候,很可能只寫入了一半的數(shù)據(jù),而打破了事務(wù)的原子性(Atomicity)。為了解決這個問題,我們可以采取以下兩種方案:
影子拷貝(Shadow Copies/Pages):每個事務(wù)會創(chuàng)建數(shù)據(jù)庫的部分拷貝,然后針對這些拷貝進行操作。在發(fā)生異常的時候,這些拷貝會被移除;正常的情況下,數(shù)據(jù)庫則會立刻將這個拷貝寫入到磁盤然后移除老的數(shù)據(jù)塊。
事務(wù)日志(Transaction Log):所謂的事務(wù)日志即是獨立的存儲空間,在將數(shù)據(jù)寫入真正的數(shù)據(jù)表之外,數(shù)據(jù)庫都會將事務(wù)操作順序?qū)懭氲侥硞€日志文件中。
在實際情況下,Shadow Copies/Pages 會受到極大的磁盤限制,因此絕大部分數(shù)據(jù)庫還是選擇了以事務(wù)日志的方式。
事務(wù)日志(Transaction Log)為了實現(xiàn)數(shù)據(jù)庫狀態(tài)的恢復(fù),DBMS 系統(tǒng)通常需要維護事務(wù)日志以追蹤事務(wù)中所有影響數(shù)據(jù)庫數(shù)據(jù)的操作,以便執(zhí)行失敗時進行事務(wù)的回滾。以 MySQL 的 InnoDB 存儲引擎為例,InnoDB 存儲引擎通過預(yù)寫事務(wù)日志的方式,來保障事務(wù)的原子性、一致性以及持久性。它包含 Redo 日志和 Undo 日志,Redo 日志在系統(tǒng)需要的時候,對事務(wù)操作進行重做,如當系統(tǒng)宕機重啟后,能夠?qū)?nèi)存中還沒有持久化到磁盤的數(shù)據(jù)進行恢復(fù),而 Undo 日志,則能夠在事務(wù)執(zhí)行失敗的時候,利用這些 Undo 信息,將數(shù)據(jù)還原到事務(wù)執(zhí)行前的狀態(tài)。
事務(wù)日志可以提高事務(wù)執(zhí)行的效率,存儲引擎只需要將修改行為持久到事務(wù)日志當中,便可以只對該數(shù)據(jù)在內(nèi)存中的拷貝進行修改,而不需要每次修改都將數(shù)據(jù)回寫到磁盤。這樣做的好處是,日志寫入是一小塊區(qū)域的順序 I/O,而數(shù)據(jù)庫數(shù)據(jù)的磁盤回寫則是隨機 I/O,磁頭需要不停地移動來尋找需要更新數(shù)據(jù)的位置,無疑效率更低,通過事務(wù)日志的持久化,既保障了數(shù)據(jù)存儲的可靠性,又提高了數(shù)據(jù)寫入的效率。
當某個事務(wù)需要去更改數(shù)據(jù)表中某一行時,未提交的改變會被寫入到內(nèi)存數(shù)據(jù)中,而之前的數(shù)據(jù)會被追加寫入到 Undo Log 文件中。Oracle 或者 MySQL 中使用了所謂 Undo Log 數(shù)據(jù)結(jié)構(gòu),而 SQL Server 中則是使用 Transaction Log 完成此項工作。PostgreSQL 并沒有 Undo Log,不過其內(nèi)建支持所謂多版本的表數(shù)據(jù),即同一行的數(shù)據(jù)可能同時存在多個版本。總而言之,任何關(guān)系型數(shù)據(jù)庫都采用的類似的數(shù)據(jù)結(jié)構(gòu)都是為了允許回滾以及數(shù)據(jù)的原子性。
某個事務(wù)提交之后,內(nèi)存中的改變就需要同步到磁盤中。不過并不是所有的事務(wù)提交都會立刻觸發(fā)同步,過高頻次的同步反而會對應(yīng)用性能造成損傷。這里關(guān)系型數(shù)據(jù)庫就是依靠 Redo Log 來達成這一點,它是一個僅允許追加寫入的基于磁盤的數(shù)據(jù)結(jié)構(gòu),它會記錄所有尚未執(zhí)行同步的事務(wù)操作。相較于一次性寫入固定數(shù)目的數(shù)據(jù)頁到磁盤中,順序地寫入到 Redo Log 會比隨機訪問快上很多。因此,關(guān)于事務(wù)的 ACID 特性的保證與應(yīng)用性能之間也就達成了較好的平衡。該數(shù)據(jù)結(jié)構(gòu)在 Oracle 與 MySQL 中就是叫 Redo Log,而 SQL Server 中則是由 Transaction Log 執(zhí)行,在 PostgreSQL 中則是使用 Write-Ahead Log(WAL)。下面我們繼續(xù)回到上面的那個問題,應(yīng)該在何時將內(nèi)存中的數(shù)據(jù)寫入到磁盤中。關(guān)系型數(shù)據(jù)庫系統(tǒng)往往使用檢查點來同步內(nèi)存的臟數(shù)據(jù)頁與磁盤中的對應(yīng)部分。為了避免 IO 阻塞,同步過程往往需要等待較長的時間才能完成。因此,關(guān)系型數(shù)據(jù)庫需要保證即使在所有內(nèi)存臟頁同步到磁盤之前引擎就崩潰的時候不會發(fā)生數(shù)據(jù)丟失。同樣地,在每次數(shù)據(jù)庫重啟的時候,數(shù)據(jù)庫引擎會基于 Redo Log 重構(gòu)那些最后一次成功的檢查點以來所有的內(nèi)存數(shù)據(jù)頁。
WAL(Write-Ahead Logging)WAL 協(xié)議主要包含了以下三條規(guī)則:
每個數(shù)據(jù)庫中的修改操作都會產(chǎn)生一條記錄,該記錄必須在數(shù)據(jù)被寫入到數(shù)據(jù)庫之前被寫入到日志文件中;
所有的操作日志都必須嚴格按序記錄,即如果 A 記錄發(fā)生在 B 之前,那么 A 也必須在 B 之前被寫入到日志中;
在事務(wù)被提交之后,必須在日志寫入成功之后才能回復(fù)事務(wù)處理成功。
同樣可以參考 PostgreSQL 架構(gòu)機制 https://url.wx-coder.cn/SgRDQ 中有關(guān)于 WAL 的實例討論。
延伸閱讀歡迎關(guān)注某熊的技術(shù)之路公眾號或某熊的技術(shù)之路指北,讓我們一起前行。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/17947.html
摘要:關(guān)系型數(shù)據(jù)庫中的事務(wù)管理詳解并發(fā)控制與事務(wù)日志數(shù)據(jù)庫系統(tǒng)的萌芽出現(xiàn)于年代。并發(fā)控制并發(fā)控制旨在針對數(shù)據(jù)庫中對事務(wù)并行的場景,保證中的一致性與隔離性。絕大部分數(shù)據(jù)庫會采用鎖或者數(shù)據(jù)版本控制的方式來處理并發(fā)控制問題。 本文節(jié)選自:關(guān)系型數(shù)據(jù)庫理論 https://url.wx-coder.cn/DJNQn ,涉及引用/整理的文章列舉在了 Database-List。 showImg(htt...
摘要:基礎(chǔ)問題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線抽象關(guān)鍵字修飾符知識點總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機制解讀抽象類與三大特征時間和時間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類對象鎖和類鎖的區(qū)別,,優(yōu)缺點及比較提高篇八詳解內(nèi)部類單例模式和 Java基礎(chǔ)問題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...
閱讀 2222·2021-09-07 09:58
閱讀 3400·2019-08-30 14:07
閱讀 1310·2019-08-29 12:32
閱讀 676·2019-08-29 11:06
閱讀 3698·2019-08-26 18:18
閱讀 3737·2019-08-26 17:35
閱讀 1386·2019-08-26 11:35
閱讀 616·2019-08-26 11:35