顔然,OceanBase 分布式資料庫事務研發負責人
OceanBase 初創成員之一,目前負責事務引擎、高可用架構、負載均衡、性能優化等方面的工作。
引言
衆所周知,事務特性是資料庫中重要的功能,是保證資料一緻性的關鍵。在近日OceanBase 開源技術直播上,OceanBase 分布式資料庫事務研發負責人顔然以資料庫事務的本質為主題,深入分享了事務的前世今生一季 OceanBase 在分布式事務上的解決方案。本文将從以下三方面進行闡述資料庫事務:
一、事務的前世
二、事務的挑戰
三、分布式事務
正文
事務的前世
每個人的手機和電腦中幾乎不會安裝一款叫“資料庫”的應用,但是幾乎每一款 App内部都會使用一個叫“資料庫”的更底層的系統來存儲資料。每個人日常生活中都會訂票、購物、付款,但也許還沒有意識到這些操作的背後都是由“資料庫”系統來支撐。那麼究竟什麼是“資料庫”?“資料庫”和“事務”又有什麼關系呢?
計算機系統在其發展的曆程中經曆了多次重要的事件,而上個世紀 60 年代迎來一次重大變革,為計算機系統帶來了巨變,自那之後,計算機就開始以數字化的方式重塑整個世界的程序。這次變革就是“磁盤”的大規模使用。在磁盤之前,計算機使用“錄音帶”來存儲資料。計算機使用的錄音帶,和聽歌使用的錄音帶,或者看電影用的錄像帶,本質是一種東西。這是個暴露年齡的比喻,畢竟這兩種東西現在已經隻能在博物館裡才能看到了。錄音帶的最大問題是不友善找東西,而“倒帶”是個極其需要耐心的活。這就決定了,當時的計算機所處理的資料都是通過後期謄錄到錄音帶上的,計算機隻是用于統計和彙報所需。
最初的磁盤是個大家夥,看下圖也許分辨不出大小,但實際上它有微波爐那麼大。這第一塊硬碟,也許不能稱為一塊,這一台硬碟隻能存儲 3.75MB 的資料。随着技術的不停疊代,硬碟越做越小,容量越做越大。再到後面,又出現了更新版的閃存媒體,硬碟就小到無孔不入了。但是不論大小,這些儲存設備随機通路的特性都是一樣的。
磁盤最重要的特性是“随機通路”,即使用者需要查找的東西,可以輕松地定位到。計算機的快速處理能力,加上磁盤對資料的快速定位和修改能力,珠聯璧合,為計算機系統打開了新世界的大門。自此,計算機開始參與到人類社會的方方面面。比如,航空公司的第一個售票系統Sabre,使用計算機和磁盤直接存儲每一個待售賣的航班客票資訊和購票人資訊。這些資訊存儲在硬碟上,被售票員實時通路和修改,直接替代了以紙質方式記錄客票資訊的方式。在此之後,銀行、通信、交通等各行業,都使用磁盤來存儲其業務資訊,并使用計算機來響應各種“實時處理”的需求。
對于資料處理需求的極速膨脹,專門用于資料存儲和通路的“資料庫管理系統”也應運而生,并被作為一個獨立的系統而廣泛接受。一個軟體系統之是以能獨立出來并被市場接受,原因在于其可複用性,也就是說,在衆多的業務場景中,使用了這個軟體要比不使用的時候,更友善且效率更高,世界也會是以而變得更美好。
資料庫管理系統幫助使用者解決的通用性問題主要有:
1. 資料存儲與管理
2. 資料通路
3. 資料變更
4. 高可用
首先,資料的存儲與管了解決了資料如何管的問題,它本質上是對儲存設備的提煉和抽象,讓使用者不用關心資料在硬碟上具體是如何放置的,而隻用關心資料的模型即可。從早期到現在,資料模型層出不窮,在關系模型之前,有網絡模型和層次模型,在關系模型之後,有對象模型、文檔模型、圖模型等。
其次,資料的通路解決如何在資料中做查詢和分析,在資料模型之上,資料庫系統會提供抽象的接口讓使用者友善使用,比如SQL語句就是在關系模型之上有超強表達能力的資料通路接口。
然後,資料變更解決的就是修改資料的問題,而修改資料的核心在于怎麼保證資料的正确性和一緻性,把資料改錯了,是使用者最不願看到的情況。而“事務”功能就是“資料變更”功能的核心。事務功能提供給使用者 4 個著名的特性:原子性 Atomicity、一緻性 Consistency、隔離性 Isolatation、持久性 Durability,簡稱 ACID。使用者将多個資料變更的操作以一個事務的整體送出到資料庫管理系統上進行操作,那麼資料庫管理系統就能保證使用者做的資料變更具有 ACID 的特性。
最後,高可用特性是近些年來人們對系統特别強調的一個要求。以前,系統往往強調可靠性,而現在系統則更多的改為可用性。究其原因,随着網際網路逐漸深入人們的生活,線上化項目的快進,服務不可用對于人們生活的影響越來越大,是以人們對于系統的要求也從可靠性變成了可用性。這是一種質的變化,可用性等于可靠性加上服務的連續性。高可用也和事務特性密切相關,這部分後文會進一步提及。
再來看回事務處理。資料庫管理系統本是給另一些軟體開發者使用的,用于存儲、查詢、修改業務資料以及表達業務邏輯。既然已經有了關系模型,有了 SQL 語句來給開發者使用。作為寫程式的開發者,對存儲在硬碟上的資料做一些修改,那麼直接改不可以嗎,為什麼還需要“事務”功能呢?難點就在于計算機系統并不太好操作,設想一下在售票系統售票過程中,如果我們修改了預定座位資訊,但是還沒有收錢,計算機故障了,待計算機恢複的時候,想要把這個錯誤的資訊核對出來并修複,那可就一點也不簡單了。而事務功能,則可以解決開發者這方面的顧慮。
事務的挑戰
那麼,事務功能究竟解決了哪些難題,進而讓使用者覺得世界更美好的呢?事務功能從本質上做了兩件重要的事情,即“故障恢複”和“并發控制”。前者保證在計算機系統出現故障時,資料修改的原子性和持久性。後者保證在系統内的資料處于并發操作時,操作與操作之間可以保持隔離性。這兩者共同的目的是保證資料庫管理系統在對資料進行操作時,資料本身要保持完整性和一緻性。比如,張三給李四的轉賬操作時我們要同時修改張三的賬戶和李四的賬戶,這次轉賬操作不會因為故障而導緻憑空多出來錢或少了錢,也不會因為同時進行張三和王五的轉賬操作,而導緻張三的錢變成負數。
“故障恢複”和“并發控制”挑戰性高嗎?是的,非常高。
首先我們看下“故障恢複”。計算機系統可能會出現什麼故障呢?存儲資料的硬碟會損壞?主機闆可能突然燒壞導緻系統停機?機器會斷電?網線會時斷時續?交換機會停擺,甚至夏季用電高峰期整個機房可能面臨無電可用的情況,然而儲備的柴油發電機隻能支援幾個小時?上述情形都是真實遇到過的情況。
事務的“故障恢複”功能就是為計算機編制好應對這些異常的手冊,通過異常處理的流程,保證原子性和持久性。目前最常見的,也是 OceanBase 使用的故障恢複的方案,即使用日志。
日志指的是一本記錄操作的“流水賬”。為了實作很多個資料變更操作一起成功,而記錄修改的硬碟又不能讓多個寫操作一次性完成。事務功能的做法是把一個事務内所有要做的修改轉化成在日志系統裡的記錄,把所有要修改的内容都記錄完之後,記錄一個結束标記“搞定”,整個事務是否原子的完成,就取決于這個“搞定”标記最終是否完成。一旦最終的标記完成,就意味着事務内的所有修改都已經被記錄下來了,即使實際資料變更說因為故障沒有完成,但是當系統重新恢複後,日志裡都記錄着這個事務需要做的所有事情,再重新做一遍即可。
保證剛剛完成的事務不會丢失的秘密就在于這個日志。資料庫會把這個日志記錄在不隻一塊硬碟上,保證如果有一塊硬碟意外損壞了,還有其他硬碟上存儲的日志可以用來恢複事務的修改。為了提升保障能力,比如保障當機器損壞時日志不會丢,當機房故障時日志不會丢,甚至城市内的多個機房都發生故障時日志亦然不會丢,日志需要儲存在更多的地方。OceanBase 的三地五中心架構下,日志會存儲在三個城市的五個資料中心裡,因為有Paxos 協定加持,是以有任意兩個城市存儲成功,事務就能送出成功,同時還保證了任何一個城市的全部機房出現故障,事務資料都不會丢。
悄悄告訴大家一個内幕消息,目前還沒有資料庫能對地球故障進行容災,如果地球沒了,資料就都丢了,看來 OceanBase 還需努力。
資料庫的高可用特性也和剛才說的多個副本的處理機制密切相關。最新産生的日志通過 Paxos 協定同步到其他副本,根據部署的規格不同,可能是同一個城市的其他機房,也可能是另一個城市的機房,那麼日志和對應的資料就在多個機房或者多個城市有備份,當出現機器或者機房故障時,根據 Paxos 協定,會激活另外一個具有完好副本的機房,繼續提供資料庫的服務。這也是OceanBase 資料庫高可用能力的基石。
再來,我們談談“并發控制”。如果資料庫系統一次隻處理一個使用者的一個事務,那麼就沒有“并發控制”的需要了。一個事務接着一個事務做,最安全。但是這樣太低效了,放着 CPU、硬碟、網絡這些資源利用不起來。資料庫管理系統作為一種系統軟體,其内在的使命就是把計算機系統的硬體能力盡可能發揮出來,讓同樣的硬體可以更好地滿足實際業務的需要。“效率”是系統軟體與生俱來的使命,而“高效”也是複雜性的由來所在。
事務的“并發控制”其根本是在解決對于資料讀和寫的并發問題。
首先,資料庫系統要決定并發控制的粒度,常見的并發控制粒度有以資料存儲頁面為機關的和以資料行為機關的。一般來說,粒度越小越好。粒度越小,互相之間的影響越小。OceanBase 采用的并發控制粒度是“行”。其次,要解決讀和寫的并發問題。這裡要分别列舉來說一說:
第一,讀與讀之間的并發操作;
第二,寫與寫之間的并發操作;
第三,讀與寫之間的并發操作。
讀與讀之間,不需要特别的處理,一份資料無論被讀多少次,資料還在那裡,不增不減。
寫與寫之間,不能同時進行,否則資料就會被改亂了。OceanBase 通過行級别的互斥鎖機制,即一個事務修改過的行,都會通過行鎖保護上,在事務結束之前,這些行是不允許被其他事務修改的。讀與寫之間,多版本并發控制是目前的大勢所趨,OceanBase 采用的也是多版本并發控制。事務執行過程中更改的資料都是以新版本的形式存儲在系統中,保證更新前的資料依然在系統中,在事務提前之前,事務更改的資料都是不生效的,這時有讀取操作讀到這些行時,可以直接讀之前的資料。這是多版本并發控制的最大魅力,一行資料在做修改時,并不影響同時間進行的資料讀取。通過讀與寫之間是隔離開的設計,系統就可以盡可能“榨幹”硬體的能力。
分布式事務
分布式環境給事務功能帶來了新的挑戰,所有的一切都是因為分布式系統裡的“延遲”帶來的。分布式系統什麼樣的延遲問題帶來了這麼大的挑戰呢?
關于延遲這件事情,伴随着計算機系統的每一次通信,而通信是所有計算機操作的基礎。
CPU通路記憶體耗時50納秒,而同一個機房内的網絡延遲是 100 微秒。也就是說,CPU 的一個從本機記憶體裡取資料的操作,從發起指令開始到把資料取到,需要 50 納秒。如果需要取的資料在另一台機器上,即使是在同一個機房内,那也需要 100 微秒,兩者相差 2 千倍。
這種延遲差别帶來兩個影響,一是要考慮通信的延遲,設計專門的算法;二是一台機器故障時,其他的機器不會立即知道,容災處理需要考慮這種新的故障場景。
考慮事務功能要解決的“故障恢複”和“并發控制”這兩件事情,在分布式環境下會遇到哪些新的挑戰呢?
那麼可以先來看“并發控制”。多版本并發控制機制需要一種表達全局快照版本的方式,通常有兩種:一種拿的快照叫Read View,另一種拿的快照叫 Read Version。這兩者的差别是,事務是否是在送出時才能确定版本号。如果事務不是在送出時才确定版本号,那麼快照版本需要包含所有活躍的事務清單,這是很難擴充的一種架構。
OceanBase 使用的是後者,即一個快照版本是一個時間戳,是以命名為ReadVersion。這種模式的擴充能力要強很多,但是依然需要有一個全局的單點提供時間戳。
在 OceanBase 裡,提供全局時間戳的叫全局時間戳服務 GTS,雖然這個時間戳服務每秒可以處理幾百萬次請求,使用者的業務需求應該都能滿足,但是我們還是希望 OceanBase 的處理能力是完全可擴充的。OceanBase 采用了聚合方式取時間戳,每台伺服器同一時間并發的事務可以聚合起來取時間戳,這大大降低了對于時間戳服務的請求OceanBase TPC-C 測試時,每分鐘要執行 15 億個事務,時間戳服務支援起來完全不在話下。
對比業界另一個分布式資料庫CockRoachDB。CRDB 采用的快照模式與OceanBase 是一樣的,都是 Read Version 方式。但是,CRDB 沒有采用全局時間戳服務,而是使用了混合邏輯時鐘 HLC。
其實HLC 的優勢是其本質是邏輯時鐘,基于通信的方式保證有因果關系的事件之間時鐘值是有嚴格順序的,這種模式不需要一個單點來生成時間戳。但是,HLC 有一個巨大的缺陷,在資料的一緻性上無法做到與傳統資料庫相同的全局有序的事務處理,按照 CRDB 系統自己的描述,他們隻能保證單行的線性一緻性。
那怎麼了解單行的線性一緻性呢?就是資料庫隻保證如果兩個事務修改的是同一行,那麼其先後關系才是明确的。反過來了解,如果兩個事務修改的是不同的行,那麼資料庫不保證其資料修改的先後順序。到底繞不繞?給大家舉一個這種場景下會遇到的問題就容易了解了:交易業務中的一個常見場景,如使用者下單買一件商品,有一張表存儲這個訂單是否支付完成的狀态,另一張表存儲這個訂單是否發貨的狀态。付款系統處理了使用者付款動作,并修改了訂單狀态為已支付,然後通知發貨系統,發貨系統确認訂單已付款後,發起了發貨動作,并修改訂單狀态為已發貨。已支付和已發貨是先後修改的資料,但是是在兩張不同的表裡。如果這時有另一個查詢動作來查詢這個訂單的這兩行資料,在 CRDB 中是有可能看到已發貨的狀态,同時是未支付的狀态。也就是說,看到了後面一次的修改,但是沒有看到前面一次的修改,這會給資料庫的使用者添加很多負擔,也是 OceanBase 為什麼沒有選擇這種方式的原因。
再看“故障恢複”。在分布式環境下,事務的更新會出現在多台機器上,用于完成“故障恢複”的事務日志也會分别在不同的機器上記錄,這時日志記在了多處,想要保證記錄操作的日志全部都能記全,就需要新的流程。這個新流程就是“兩階段送出協定”。兩階段送出的新增流程就是先讓每台機器記錄自己的記錄檔,但是先别标記最終成功,待協調者确認了所有記錄檔都記錄成功後,再記下事務送出成功的記錄,這就保證了日志記錄原子成功的特性。
這就是經典兩階段送出協定的流程,而 OceanBase 的極緻優化,則讓兩階段送出協定可以在更短的時間内完成。OceanBase的政策是不依賴協調者最終記的事務成功日志,而是依賴每一個參與者上記錄的記錄檔,在兩階段執行過程中如果出現了系統故障,那麼由參與者互相校驗事務的日志是否記全了,最終依然可以通過确認所有的參與者都記全了各自的記錄檔,來确認事務最終送出的狀态。如果不是所有人都記全了,那麼事務就沒有完成送出,最終就會呈現復原狀态。
OceanBase 的兩階段送出協定将事務的送出延遲從傳統協定中三次日志同步延遲減小到隻有一次日志同步延遲,極大提高了事務送出的效率。
繼續對比 CRDB。與傳統資料庫和 OceanBase 有很大差别的一點,CRDB 的整個事務持久化模型由 KV 系統完成,事務不再操作獨立的日志系統,是以 CRDB 失去了用日志系統記錄系統變更的機會,完全依賴于 KV 系統的操作。從 CRDB 的設計原則來看,這是在簡化分布式系統的複雜度,但是這個簡化也引入了大量的負擔。在 CRDB 中,事務送出時依然要保證事務内的修改在故障時的原子性。但是,事務内不同的修改是直接操作 KV 中的對應的行,CRDB 要保證事務内修改的所有行原子的生效,相當于,每一個行都是 CRDB 事務的參與者,雖然最終也能完成事務“故障恢複”的工作,但是開銷很大。
總結
時至今日,事務特性不隻在資料庫管理系統中使用,也被應用于其他系統裡以描述類似的需求,比如最新的 Intel 伺服器 CPU 支援以事務特性來通路記憶體,這就把事務的原子性和并發控制引入了記憶體系統中。這麼做的好處是可以讓程式開發者更友善地使用記憶體來表達複雜的修改邏輯。
事務的核心就是把複雜的操作邏輯隐藏在系統内部,給使用人員暴露出簡單的接口,讓使用的人更友善。把複雜留在系統内,把簡單留給使用者,這是資料庫系統的最大價值,也是 OceanBase 各種功能開發中秉承的原則。
Q&A彩蛋環節
結合使用者關注的問題,顔然老師做了解答,具體問答見下,希望能給到大家一點幫助。
Q、OceanBase作為一款分布式資料庫,其内部是如何界定一個事務是屬于分布式還是屬于單機事務,比如以下情況:a. 同一個事務涉及到多個副本在同一個observer節點b. 同一個事務涉及的多個副本在不同的observer節點
A:OB是按分區區分是否為分布式事務。OB會自動追蹤一個事務内的修改,到事務最終執行COMMIT指令時,如果發現修改都在一個分區上,就會通過分區内的一階段送出邏輯完成事務原子送出,如果修改的内容出現在多個分區,就會走兩階段送出。
Q、OceanBase作為一個分布式資料庫是如何做到交換機級故障後,服務快速恢複的?
A:OB通過多副本來容災,如果要容交換機故障,隻需要跨交換機部署OB的多個副本,多個副本通過Paxos協定保證一緻性。如果交換機出現故障,隻要故障的交換機之外還有剩下多數的副本,這些副本會重新選主并恢複資料庫服務。
Q、OB分布式事務的兩階段送出優化為一次落盤兩次RPC,能否具體講講故障情況下怎麼保證一緻性?
A:事務的所有修改依然是依賴日志來進行持久化,OB的優化是讓所有參與者同時記錄日志,隻要這次日志都記錄成功了,事務的所有變更就是完整的,根據這些日志裡記錄的修改,就能恢複事務内所有變更,如果因為故障有參與者沒有記錄成功,那麼識别出這種情況後,就走事務的復原邏輯,将事務内的所有修改復原掉。是以,整體隻需要依賴參與者的一次日志持久化就能保證前面的邏輯。兩次RPC通信,就是用來确認所有參與者都持久化成功,以及告訴所有參與者事務最終是否成功。
Q、請問一下OB多版本控制,同個主鍵不同版本資料是如何組織的,是存在同一個樹裡還是怎麼實作?
A:一共有兩種組織方式:在記憶體中和硬碟上。記憶體MemTable是btree組織的行結構,行内多版本是連結清單結構,硬碟SSTable中是按照行資料和行内的多版本進行連續存儲。
Q、OB分布式事務的内部類似一個優化後的XA方案,其對事務查詢還有其他優化嗎?比如說prepare後,對查詢可以直接讀取,但是對于寫操作還是需要二階段異步完成後才可以寫?是否提供微服務級的分布式事務協調?外部XA調用?或者是其他事務解決方案?
A:OB現在也支援通過XA協定與其他系統做聯合事務,在這個使用模式下,OB就是XA裡的一個RM,隻是OB這個RM内部還是會通過兩階段協定保證自己内部的分布式事務的原子性。相當于是級聯的兩層兩階段送出。XA協定本身不解決并發控制的問題,隻解決原子送出。如果XA prepare後就允許讀取的話,相當于沒有并發控制,可能出現一些事務的資料問題,比如一個RM的XA prepare即使成功,其他RM可能失敗,最終事務還會復原,即剛被讀取的資料就又被復原掉了。微服務使用XA來解決事務的需求是一種較優的方向,相比較外部的事務系統,X對業務開發最為友好。
更多有關事務的問答内容,請掃描下方二維碼進行檢視:
看完以上内容,如果您有任何疑問,歡迎通過以下方式與我們進行交流:
釘釘群:33254054
github:
https://github.com/oceanbase/oceanbase部落格問答:
https://open.oceanbase.com/answergitee:
https://gitee.com/oceanbase