面試題:說說看你對數據庫事務和 ACID 的理解?并發事務可能會產生哪些問題,該如何解決?什么是快照讀和 MVCC,解決了什么問題?
面試題概覽:
- 什么是數據庫的事務,說說你對事務特性的理解;
- 說說看Mysql是如何實現原子性的;
- Mysql的InnoDB是如何實現數據持久化的;
- 數據庫并發事務可能會出現什么問題,以及該如何解決;
- 知道什么是快照讀嗎,它是用來解決什么問題的;
面試官:什么是數據庫的事務,說說你對事務特性的理解?
數據庫事務是數據庫管理系統執行過程中的一個邏輯單位,由一個有限的數據庫操作序列構成。這些操作要么全部執行成功,要么全部不執行,是一個不可分割的工作單位。
對于事務的特性,可以從以下幾個方面來理解:
一、原子性(Atomicity)
原子性是指事務是一個不可分割的工作單位,事務中的操作要么全部完成,要么全部不完成。在數據庫操作中,如果事務中的某個操作失敗,則整個事務會回滾到事務開始前的狀態。這種特性通過數據庫的Undo機制來實現,即在事務執行過程中,如果出現錯誤或用戶執行ROLLBACK語句,系統可以回滾到事務開始前的狀態。
二、隔離性(Isolation)
隔離性是指并發執行的事務之間相互隔離,不允許一個事務的執行結果影響其他事務的執行。這種特性避免了多個事務并發執行時可能出現的數據不一致問題。數據庫系統通常通過鎖和其他并發控制技術(如MVCC)來實現隔離性。表現形式是,當一個事務正在對某個數據進行操作時,其他事務不能對該數據進行并發修改,以防止數據不一致的問題發生。
三、持久性(Durability)
持久性是指一旦事務提交,它對數據庫中數據的改變就是永久性的,即使在系統崩潰后,事務的修改結果也不會丟失。這種特性通過數據庫的Redo機制來實現。當事務提交后,系統將把事務的所有操作寫入到日志文件中,以便在系統恢復后通過Redo日志重新執行這些操作,保證數據的一致性。
四、一致性(Consistency)
一致性是指事務必須將數據庫從一種一致狀態轉換到另一種一致狀態。這么說有點抽象,我個人的具體理解是:一致性體現在兩點。
同一個表的在本次事務中有聯系的多條記錄的狀態要對的上,比如轉賬前后兩個賬戶的金額總和應該不變(兩條同一張表的update語句)。
不同表在本次事務中有聯系的多條記錄的狀態要對的上,比如消費后增加用戶積分并減少用戶金額,那么用戶的金額減少后,不能因為故障導致用戶積分沒增加(兩條不同表的update語句)。
上述四個特點中,一致性是事務的最終目的。只要其他三個特性都滿足了,那么一致性自然而然也就會滿足,也就是說原子性,隔離性和持久性是需要作出的努力,一致性是我們想要的結果。
面試官:原子性——說說看Mysql是如何通過undo日志實現原子性?
首先是MySQL如何通過undo日志實現原子性的詳細解釋:
一、undo日志的作用
undo日志,也被稱為回滾日志,是MySQL中用于記錄事務在執行過程中對數據的修改前的狀態(即舊值)的一種日志。當事務需要回滾時,MySQL可以利用undo日志將數據恢復到事務開始前的狀態,從而保證事務的原子性。
undo日志分為3類:
- insert操作對應的undo日志
- delete操作對應的日志
- update操作對應的undo日志
下面是undo日志的具體結構,其他的不用關注,重點關注圖中倒數第二格的 <len, value>,里面包含增刪改操作前的具體字段和值,數據庫回滾就是通過這些字段和值來進行的。
二、實現原子性的過程
(1) 事務開始:
- 當一個事務開始時,MySQL會監控該事務對數據庫的所有修改操作。
(2) 記錄undo日志:
- 在事務執行過程中,每當對數據庫進行寫操作(如INSERT、UPDATE、DELETE)時,MySQL會將修改前的數據狀態(舊值)保存在undo日志中。
- undo日志是邏輯日志,它記錄的是修改前的數據狀態,而不是物理存儲的修改。
(3) 事務提交或回滾:
- 如果事務成功執行并提交,那么這些修改將持久化到數據庫中,而undo日志則會在一定時間后被清理(通常是在事務提交后,且確保沒有其他并發事務需要回滾到該事務之前的狀態時)。
- 如果事務在執行過程中遇到錯誤或用戶顯式要求回滾,那么MySQL會利用undo日志中的信息,將數據庫恢復到事務開始前的狀態。
三、undo日志的具體實現
1.undo日志的存儲:
undo日志被存儲在InnoDB存儲引擎的專用頁面中,這些頁面被稱為undo頁面。
undo頁分為兩種:insert類型的undo日志(里面只放insert類型的undo日志) 和 update類型的undo日志(放update和delete類型的undo日志)。
undo頁面以鏈表的形式組織,每個undo頁面都包含了多條undo日志,Innodb會為每一個事務一條或兩條undo鏈表(如果該事務同時包含增刪改操作就會生成兩條undo鏈表)。
之所以要將同一個事務產生的undo日志組織在同一個鏈表而非所有事務的undo日志組織成一個鏈表也是為了回滾時可以按事務的維度找到只和本事務相關的undo日志進行回滾。
兩種不同類型的undo日志頁分別用 insert undo 鏈表 和 update undo 鏈表管理。
把 undo 日志分成 2 個大類是因為insert類型的 undo 日志在事務提交后可以直接刪除,而其他類型的 undo 日志還需要為 MVCC(多版本并發控制)服務,不能在事務提交后馬上刪除。
2.回滾操作:
當事務需要回滾時,MySQL會沿著undo日志鏈表,按照與事務執行相反的順序,逐條應用undo日志中的信息,將數據恢復到事務開始前的狀態。
- 對于INSERT操作,undo日志記錄的是“刪除”操作,即如果事務回滾,需要撤銷插入的數據。
- 對于DELETE操作,undo日志記錄的是“插入”操作,即如果事務回滾,需要恢復被刪除的數據。
- 對于UPDATE操作,undo日志記錄的是修改前的舊值,即如果事務回滾,需要將數據更新回舊值。
面試官:持久性——Mysql的InnoDB是如何實現數據持久化的?
說到數據庫持久化就繞不開 WAL 機制 和 redo日志。
一、WAL和redo log的基本概念
WAL是一種數據安全寫入機制,其核心思想是在事務進行修改之前,先將修改操作記錄到日志中,然后再將修改應用到數據庫中。這樣做的好處是,即使系統崩潰或斷電,也可以通過日志來恢復數據,保證數據的持久性和一致性。
redo日志是InnoDB存儲引擎獨有的物理日志,記錄了數據操作的細節,包括事務開始和結束的標志、修改的數據頁和對應的操作等。它主要用于故障恢復,當數據庫發生異常關閉或崩潰時,InnoDB可以通過redo日志來恢復數據。
redo日志以固定大小的多個文件(如ib_logfile0、ib_logfile1)形成的文件組的形式存在,是一個可覆蓋的循環日志。InnoDB 的 redo log 是固定大小的,比如可以配置為一組 4 個文件,每個文件的大小是 1GB,那么這塊“粉板”總共就可以記錄 4GB 的操作。從頭開始寫,寫到末尾就又回到開頭循環寫,如下面這個圖所示。
write pos 是當前記錄的位置,一邊寫一邊后移,寫到第 3 號文件末尾后就回到 0 號文件開頭。checkpoint 標記了日志中已經刷盤成功的數據所對應的redo日志數據。
write pos 和 checkpoint 之間的是redo日志上可被新的事務的增刪改操作所覆蓋的部分,可以用來記錄新的操作。如果 write pos 追上 checkpoint,表示redo日志滿了,這時候不能再執行新的更新,得停下來將Buffer Pool中的臟頁刷盤,把 checkpoint 推進一下才能繼續寫redo日志。
有了 redo log,InnoDB 就可以保證即使數據庫發生異常重啟,之前提交的記錄都不會丟失,這個能力稱為 crash-safe。
下圖是一條簡單redo日志的數據結構:
其中 space ID 表示本修改所對應的數據頁所在的表空間,page number是所修改的頁號,offer是所修改的數據在頁中的偏移量。通過這3個信息,就可以在故障恢復時找到要恢復的數據頁和數據頁中的具體位置。
二、redo log寫入的具體過程
(1) 事務開始:
當一個事務開始時,MySQL會為該事務分配一個唯一的事務ID,并將該事務的相關信息存儲在內存中的事務控制塊(Transaction Control Block, TCB)中。
(2) 修改操作記錄到redo log buffer:
在事務執行過程中,所有的修改操作(如插入、更新、刪除等)都會被寫入redo log緩沖區(redo log buffer)。redo log buffer是一個內存緩沖區,用于暫存待寫入redo log的修改操作。
(3) 事務提交和redo log刷盤:
當事務提交時,MySQL會將該事務的所有修改操作按照順序寫入redo log文件中。這個過程稱為redo log的刷新(flush)。
需要注意的是,在事務提交之前,MySQL并不會立即將redo log buffer中的修改操作持久化到磁盤上的redo log文件中。而是會等待一個合適的時機來進行持久化操作。基于innodb_flush_log_at_trx_commit配置參數的刷盤策略如下。
innodb_flush_log_at_trx_commit:
- 值=1:每次事務提交時都會將日志刷新到磁盤,確保了最高的持久性(默認值)。
- 值=2:日志寫入到操作系統的緩存(log buffer)并每秒刷寫到磁盤(可能會存在少量數據丟失的風險,但提高性能)。
- 值=0:日志寫入到操作系統的緩存(log buffer),并每次檢查點時刷寫到磁盤(可能存在更多的數據丟失風險)。
三、恢復數據的詳細過程
(1) 啟動InnoDB:當MySQL服務重啟時,InnoDB存儲引擎會開始啟動。
(2) 定位checkpoint:InnoDB會通過redo日志找到最近一次checkpoint的位置。checkpoint信息保存在日志文件的開始部分,包括checkpoint號、checkpoint lsn(記錄了產生該checkpoint時flush的LSN,確保在該LSN前面的數據頁都已經落盤,不再需要通過redo log進行恢復)和checkpoint offset(記錄了該checkpoint產生時,redo log在ib_logfile中的偏移量)。
(3) 獲取并解析redo日志:
從checkpoint相對應的位置開始,InnoDB會獲取需要重做的日志。
接著,InnoDB會解析獲取的日志,并將其保存到一個哈希表中。哈希表以(space,offset)為鍵,存儲了redo日志的信息。
(4) 數據恢復:
InnoDB會遍歷哈希表中的redo日志信息。
對于每條redo日志,InnoDB會根據(space ID,page number,offset)讀取指定頁面,并進行日志覆蓋,即根據redo日志中的記錄來恢復數據頁的內容。
面試官:數據庫并發事務可能會出現什么問題,以及該如何解決?
一、數據一致性問題
1.臟讀
一個事務讀取了另一個事務未提交的數據,這些數據可能會被回滾,從而導致讀取到無效數據。
以下是一個具體的臟讀示例:
事務A首先執行了一個select操作,從account表中讀取了id=1的賬戶的money值,此時得到的mnotallow=0。
接著,事務A嘗試執行一個update操作,將id=1的賬戶的money值設置為2000。另一個事務(事務B)修改了該賬戶的money值,并且這個修改還未提交。
如果事務A最終不提交其修改,那么事務B讀取到的mnotallow=2000就是一個“臟讀”,即讀取到了其他事務還未提交的數據。
事務B基于這個“臟讀”的數據進行業務處理可能會導致問題,例如修改了其他表里的數據,最終數據不一致。
為了避免臟讀,數據庫系統通常實施更高的事務隔離級別,如READ COMMITTED或更高,以確保事務只能讀取到已提交的數據。
2.不可重復讀
一個事務在兩次讀取同一數據時,因其他事務的提交導致本事務數據發生了變化,從而兩次讀取無法獲得一致的結果。不可重復讀會在事務需要基于多次讀取結果進行復雜計算時產生影響。
以下是一個不可重復讀示例:
為了避免不可重復讀,數據庫系統需要實施適當的事務隔離級別(如READ COMMITTED或更高)或使用其他并發控制機制來確保事務在讀取數據時不會受到其他事務修改數據的影響。
在READ COMMITTED隔離級別下,事務只能讀取到其他事務已經提交的修改,從而避免了臟讀和不可重復讀(但幻讀仍然可能發生)。而在更高的隔離級別(如REPEATABLE READ或SERIALIZABLE)下,數據庫系統會進一步限制并發操作,以減少或消除幻讀現象,但會嚴重影響并發性能。
3.幻讀
一個事務(通過條件)讀取多條記錄后,因其他事務的插入或刪除,導致再次讀取時獲得的記錄集發生變化。幻讀問題通常發生在插入或刪除操作頻繁的場景中。
以下是一個幻讀的具體示例:
再舉一個關于幻讀的例子加強一下大家對幻讀的理解:
假設有一個銀行系統,它有一個賬戶表(accounts),用于記錄客戶的賬戶余額和其他相關信息。現在,有兩個事務T1和T2同時運行,并且它們都對滿足某個條件的賬戶集合進行操作。
(1) 事務T1:
- 開始事務。
- 事務T1的目標是查詢并處理所有余額大于500元的賬戶。它首先執行一個查詢操作,找出所有余額大于500元的賬戶,并假設找到了賬戶A、B和C(這些賬戶的余額都大于500元)。
- 此時,事務T1尚未提交,也沒有對查詢結果進行任何處理。
(2) 事務T2:
- 同時開始事務。
- 事務T2的目標是向系統中添加一個新的賬戶D,并且這個新賬戶的余額設置為600元。它執行一個插入操作,將新賬戶D添加到賬戶表中,并提交事務。
(3) 事務T1(繼續):
- 在事務T1中,經過一段時間后(在事務T2提交之后),事務T1決定對之前查詢到的賬戶集合(賬戶A、B和C)進行處理。但在處理之前,它再次執行了一個相同的查詢操作,以確認要處理的賬戶集合。
- 然而,這次查詢的結果中除了賬戶A、B和C之外,還多了一個新的賬戶D(因為事務T2已經添加了新賬戶D,并且D的余額大于500元,滿足查詢條件)。
- 這導致事務T1在處理賬戶集合時遇到了一個“幻影”賬戶D,這個賬戶在事務T1的第一次查詢中并不存在,但在第二次查詢中卻出現了。
(4) 結果:事務T1在處理賬戶集合時,由于幻讀現象,它必須處理一個額外的賬戶D,這可能導致一些意外的行為或錯誤。例如,如果事務T1的目標是向所有余額大于500元的老賬戶發送一條通知,那么由于幻讀現象,新賬戶D也會收到這條通知。
為了避免幻讀,數據庫系統需要實施更高的事務隔離級別(如SERIALIZABLE)或使用其他并發控制機制(如鎖機制或MVCC)來確保事務在讀取數據時不會受到其他事務插入或刪除數據的影響。
可能得面試官追問:不可重復度和幻讀看上去形式上都是兩次讀取的結果不同,那么不可重復讀和幻讀的區別是什么?
(1) 發生場景不同:
- 不可重復讀通常發生在數據集合中具體數據項的值被其他事務修改后。
- 幻讀通常發生在查詢結果集合因其他事務的插入操作而發生變化時。
(2) 關注點不同:
- 不可重復讀關注的是數據項值的變化。
- 幻讀關注的是查詢結果集合中記錄數量或內容的變化。
并發事務除了出現數據一致性問題之外,還可能存在其他問題(如死鎖和性能下降等)。
二、死鎖
死鎖是指兩個或多個事務在執行過程中,因為相互持有對方所需要的資源而陷入無限等待的狀態。死鎖會導致系統資源無法有效利用,嚴重時可能會使系統陷入癱瘓。常見的死鎖場景包括兩個事務互相等待對方釋放鎖,以及多個事務循環等待。
三、性能下降
并發事務增多會增加系統的CPU、內存和I/O負載,影響整體性能。具體表現為:
- 鎖競爭:多個事務同時請求同一個資源,導致鎖競爭,進而引發事務等待和超時。
- 資源消耗:并發事務會占用大量系統資源,如CPU、內存和磁盤I/O等。
說到事務并發就不得不先說數據庫的隔離級別。事務的隔離級別是數據庫中用于控制并發事務間相互影響的機制。
以下是關于事務隔離級別的詳細解釋以及選擇建議:
事務隔離級別的類型
(1) 讀未提交(READ UNCOMMITTED):
- 允許事務讀取其他事務尚未提交的數據。
- 可能導致臟讀,即讀取到其他事務未提交的已update的但之后可能回滾的無效數據。
- 并發性能較高,但數據一致性較差。
(2) 讀提交(READ COMMITTED):
- 事務只能讀取已經提交的數據。
- 避免了臟讀,但仍可能產生不可重復讀和幻讀。
- 適用于大多數在線事務處理(OLTP)應用,能在一定程度上保證數據一致性。
(3) 可重復讀(REPEATABLE READ):
- 在同一事務中多次讀取同一數據會得到相同的結果,即使其他事務已經提交了修改。
- 解決了不可重復讀問題,但仍可能產生幻讀(即新插入的數據對于當前事務不可見,但可能導致后續插入沖突)。
- 適用于需要保證數據一致性的復雜業務邏輯。
(4) 序列化(SERIALIZABLE):
- 提供了最高的事務隔離級別,事務之間完全隔離,互不干擾。
- 避免了臟讀、不可重復讀和幻讀等所有并發問題。
- 但由于事務需要串行執行,性能損失較大,適用于對數據一致性要求極高的關鍵業務。
面試官:知道什么是快照讀嗎,它是用來解決什么問題的?
(下面的內容可能有點長,希望大家能耐心看完,畢竟面試加分的本質就是答出面試官所問的這個問題相關的但沒有問出來的點)。
快照讀(Snapshot Read)是數據庫事務處理中的一種讀取數據的方式,它確保事務在讀取數據時看到的是數據在某個時間點(即事務開始時)的狀態,就像拍攝了一張數據在那個時間點的“快照”一樣。這種讀取方式不會受到其他并發事務的影響,即使其他事務在讀取過程中對數據進行了修改,快照讀仍然能夠讀取到事務開始時的數據版本。
快照讀的主要特點是:
- 一致性:快照讀保證了事務在讀取數據時的一致性,因為讀取的是事務開始時的數據快照,所以不會受到其他并發事務的干擾。
- 并發性:由于快照讀不需要對數據加鎖,因此可以提高數據庫的并發性能。多個事務可以同時進行快照讀,而不會相互阻塞。
- 隔離性:快照讀提供了一種事務隔離的機制,使得每個事務都像在獨立的環境中運行一樣,不會受到其他事務的影響。
在MySQL等數據庫系統中,快照讀通常是通過多版本并發控制(MVCC,Multi-Version Concurrency Control)來實現的。
MVCC是數據庫管理系統中用于實現并發控制的一種方法。通過維護數據的多個版本來實現并發控制。
在MVCC中,每個數據項都有多個版本,每個版本都記錄了一個時間點或事務ID,表示該版本被創建或修改的時間。當一個事務讀取數據時,它會讀取一個特定時間點的快照,這個快照包含了在該時間點之前提交的所有事務的結果。通過這種方式,事務可以看到一個一致的視圖,而不受其他事務的影響。
具體來說,MVCC的實現通常依賴于以下幾個關鍵組件:
一、Undo Log
定義:Undo Log是數據庫中用于記錄數據修改歷史的日志。當事務進行更新或刪除操作時,數據庫會生成相應的Undo Log,以便在需要時能夠回滾到之前的版本。
在MVCC中,Undo Log不僅用于事務回滾,還用于維護版本鏈,一條undo日志會作為版本鏈中的一個節點,節點之間通過undo日志的回滾指針連接。
當事務進行更新或刪除操作時,數據庫會生成一條undo日志作為新的數據版本,并通過undo日志的回滾指針將新舊版本(舊的undo日志)連接起來,形成版本鏈。
二、版本鏈
定義:版本鏈是指每個數據項都維護一個記錄其修改歷史的鏈表。鏈表中的每個節點代表數據項的一個版本,這里的“一個版本”就是一條undo日志,包括歷史記錄的數據內容、修改時間戳或事務ID等信息。
實現:在數據庫中,每個數據行都會有一個隱藏的回滾指針(roll_pointer)字段,該字段指向該行的上一個版本(如果存在的話)。這樣,通過回滾指針,可以將各個版本連接起來,形成一個版本鏈。
下圖呈現了兩個事務對數據表修改過程中生成版本鏈的過程。
假設一開始hero.name = "劉備"。那么版本鏈如下:
三、讀視圖(Read View)
定義:讀視圖是數據庫在特定時刻為某個事務創建的一個快照,該快照包含了在該時刻所有未提交事務的信息。
實現:讀視圖通常包含以下關鍵信息:
- 當前活躍的事務編號集合(m_ids):記錄了當前所有未提交事務的事務ID。
- 最小活躍事務編號(min_trx_id):當前活躍事務中的最小事務ID。
- 最大事務編號+1(max_trx_id):預分配的事務編號,用于判斷新事務是否在當前讀視圖的范圍內。
- 讀視圖創建者的事務編號(creator_trx_id):記錄了創建該讀視圖的事務ID。
作用:讀視圖用于判斷哪些數據版本對當前事務是可見的。具體來說,當一個事務讀取數據時,它會根據讀視圖中的信息,在版本鏈中查找滿足條件的版本。
四、MVCC的讀操作實現
當事務進行讀操作時,數據庫會根據讀視圖和版本鏈來判斷應該讀取哪個版本的數據。具體來說,數據庫會按照以下步驟進行讀操作:
- 從版本鏈的最新版本開始,逐個比較數據版本的事務ID和讀視圖中的信息。
- 如果數據版本的事務ID小于讀視圖中的最小活躍事務編號(min_trx_id),則說明該版本已經提交,對當前事務可見,可以直接讀取。
- 如果數據版本的事務ID大于或等于讀視圖中的最大事務編號(max_trx_id),則說明該版本是在當前事務之后生成的,對當前事務不可見,繼續查找下一個版本。
- 如果數據版本的事務ID在最小活躍事務編號和最大事務編號之間,則需要進一步檢查該事務ID是否在當前活躍事務集合(m_ids)中。如果在集合中,則說明該事務尚未提交,對當前事務不可見;如果不在集合中,則說明該事務已經提交,對當前事務可見。
- 重復以上步驟,直到找到對當前事務可見的數據版本或遍歷完整個版本鏈為止。
五、MVCC解決的問題
MVCC主要解決了數據庫在高并發環境下的讀寫沖突問題,以及數據一致性問題。具體來說,它解決了以下幾個方面的問題:
- 臟讀:在沒有MVCC的情況下,一個事務可能讀取到另一個未提交事務修改過的數據。如果后者回滾,那么前者讀取的就是“臟”數據。MVCC通過確保事務只能讀取到已提交的數據版本,從而避免了臟讀問題。
- 不可重復讀:在數據庫操作期間,如果沒有適當的隔離機制,一個事務多次讀取同一數據可能會得到不同的結果。這是因為其他事務可能在此期間修改了這些數據。MVCC通過為每個事務提供一個一致的數據快照,從而解決了不可重復讀問題。
- 幻讀:幻讀是指在同一個事務中,執行相同的查詢語句,但第二次查詢卻返回了第一次查詢中沒有的新記錄。MVCC可以在一定程度上減少幻讀的發生,尤其是在讀取時沒有主動加鎖的情況下。但需要注意的是,MVCC并不能完全解決幻讀問題,在某些情況下仍然需要使用其他機制(如間隙鎖)來防止幻讀。
- 提高并發性能:由于讀操作不需要等待寫操作完成,寫操作也不會阻止其他事務進行讀取,因此MVCC可以顯著提高系統的并發處理能力。這對于讀多寫少的場景尤為有效。
- 減少鎖的使用:雖然MVCC本身也是一種形式的鎖定機制(通過版本鏈和讀視圖實現),但它減少了傳統意義上的行鎖或表鎖的需求。這有助于減少鎖競爭和鎖開銷,從而提高系統的性能。
(面試官可能得追問:MVCC的缺點)
維護MVCC并不是沒有成本的,下面是MVCC所帶來的問題:
- 存儲開銷增加:為了支持MVCC,數據庫需要存儲數據的多個版本。這會增加存儲空間的使用,特別是在頻繁更新數據的場景下。舊版本的數據可能會迅速積累,導致存儲空間的快速增長。
- 寫性能下降:在MVCC機制下,每次更新操作都需要創建新的數據版本,并將舊版本的數據標記為無效或刪除。這些額外的操作會增加寫操作的復雜性和時間開銷,從而導致寫性能下降。
- 清理過期版本的開銷:隨著時間的推移,數據庫中會積累大量的過期版本數據。這些過期版本數據不再被任何事務所需,因此需要定期清理。然而,清理過期版本數據是一個復雜的任務,需要數據庫系統掃描整個數據庫來識別并刪除無效的數據版本。這個過程可能會消耗大量的計算資源和時間。