大資料圖資料庫之TAO資料庫

張俊林部落格發表於2014-09-17


節選自《大資料日知錄:架構與演算法》十四章


14.1.2  TAO圖資料庫

        Facebook是目前世界上最著名的社交網站,如果從資料抽象的角度來看,Facebook的社交圖不僅包括好友之間的關係,還包括人與實體以及實體與實體之間的關係,每個使用者、每個頁面、每張圖片、每個應用、每個地點以及每個評論都可以作為獨立的實體,使用者喜歡某個頁面則建立了使用者和頁面之間的關係,使用者在某個地點簽到則建立了使用者和地點之間的關係……如果將每個實體看作是圖中的節點,實體之間的關係看作是圖中的有向邊,則Facebook的所有資料會構成超過千億條邊的巨型實體圖(Entity Graph)。實體圖中的關係有些是雙向的,比如,朋友關係;有些則是單向的,比如使用者在某個地點簽到。同時,實體還具有自己的屬性,比如某個使用者畢業於史丹佛大學,出生於1988年等,這些都是使用者實體的屬性。圖14-2是Facebook實體圖的一個示意片段。

                                            

                                    圖14-2  Facebook實體圖(Fbid是Facebook內部唯一的ID編號)

       Facebook將所有的實體及其屬性、實體關係資料儲存在TAO圖資料庫中,網站頁面的資料讀寫請求都由TAO來提供服務。TAO是一個採用資料“最終一致性”的跨資料中心分散式圖資料庫,由分佈在多個資料中心的數千臺伺服器構成,為了能夠實時響應應用請求,TAO以犧牲強一致性作為代價,系統架構更重視高可用性和低延時,尤其是對讀操作做了很多優化,以此保證在極高負載的情況下生成網站頁面時的高效率。

     TAO為客戶端封裝了圖操作相關的資料訪問API,使得客戶端不僅可以訪問實體及其屬性,也可以方便地訪問各種實體關係資料。比如,對於關係資料的訪問可以提供如下關係列表方式的查詢介面:

(ID, aType)->[anew,…, aold]

其中,ID代表某個實體的唯一標記,aType指出關係型別(朋友關係等),關係列表則按照時間先後順序列出ID指向的其他滿足aType型別關係的實體ID列表。例如,(i,COMMENT)就可以列出關於i的所有評論資訊。


1.TAO的整體架構

     TAO是跨越多個資料中心的準實時圖資料庫,其整體架構如圖14-3所示。首先,TAO將多個近距離的資料中心組合成一個分割槽(Region),這樣形成多個分割槽,每個分割槽內的快取負責儲存所有的實體和關係資料。其中,在一個主分割槽的資料庫和快取中集中儲存原始資料,其他多個從分割槽儲存資料副本(這是一種比較獨特的設計方式,建議讀者在此處先花些時間考慮一下其設計的出發點,然後閱讀後續內容)。

                 

       之所以如此設計架構,是出於如下考慮:快取結構是TAO中非常重要的一部分,對於快速響應使用者讀請求有巨大的幫助作用,而快取需要放在記憶體中,如果記憶體資源成本低且足夠大,那麼理想的情況是每個資料中心都存放完整的資料副本以快速響應使用者的讀操作,避免使用者跨資料中心讀取資料這種耗時操作。但是考慮到要儲存的資料量太大(PB級),每個資料中心都分別儲存一份完整的備份資料成本過高,所以退而求其次,將在地域上比較接近的多個資料中心作為一個整體來完整地儲存所有的備份資料,因為資料中心地域接近,所以通訊效率也較高,這樣就在成本和效率之間做了一種權衡和折中。

       在每個分割槽會儲存完整的實體及其關係資料,TAO在分割槽內的儲存架構可劃分為三層(見圖14-3),底層是MySQL資料庫層,因為資料量太多,將資料分表後形成若干資料切片(Shard),一個資料切片由一個邏輯關聯式資料庫儲存,一臺伺服器可儲存多份資料切片。第二層是與底層資料切片一一對應的快取層,稱之為主Cache層(Leader Cache),主Cache負責快取對應的邏輯資料庫內容,並和資料庫進行讀寫通訊,最上層是從Cache層(Follower Cache),多個從Cache對應一個主Cache,負責快取主Cache中的內容。TAO將快取設計成二級結構降低了快取之間的耦合程度,有利於整個系統的可擴充套件性,當系統負載增加時,只要新增儲存從Cache的伺服器就能很方便地進行系統擴容。


2.TAO的讀寫操作

      客戶端程式只能與最外層的從Cache層進行互動,不能直接和主Cache通訊(見圖14-4)。客戶端有資料請求時,和最近的從Cache建立聯絡,如果是讀取操作且從Cache中快取了該資料,則直接返回即可,對於網際網路應用來說,讀操作比例遠遠大於寫操作,所以從Cache可以響應大部分網站負載。

      如果從Cache沒有命中使用者請求(Cache Miss),則將其轉發給對應的主Cache,如果主Cache也沒有命中,則由主Cache從資料庫中讀取,並更新主Cache(圖14-4中標A和D的位置展示了這一邏輯),然後發訊息給對應的從Cache要求其從主Cache載入新資料。

                               

       對於讀取操作,所有的分割槽不論主從都遵循上述邏輯,但是對於客戶端發出的寫操作,主分割槽和從分割槽的行為有所不同。對於主分割槽來說,當從Cache接收到寫操作請求,將其轉給對應的主Cache,主Cache負責將其寫入對應的邏輯資料庫,資料庫寫操作成功後,主Cache向對應的從Cache發出訊息告知原資訊失效或者要求其重新載入。對於從分割槽來說,當從Cache接收到寫請求時,將其轉給本分割槽對應的主Cache,此時主Cache並不直接寫入本地資料庫,而是將這個請求轉發到主分割槽的主Cache(圖14-4中標C的位置說明了此種情況),由其對主資料庫進行寫入。

     也就是說,對於寫操作,不論是主分割槽還是從分割槽,一定會交由主分割槽的主Cache來更新主資料庫。在主資料庫更新成功後,主資料庫會通過訊息將這一變化通知從分割槽的從資料庫以保持資料一致性,也會通知從分割槽的主Cache這一變化,並觸發主Cache通知從分割槽的從Cache更新快取內容(見圖14-4標B的位置)。

    請思考:為何從分割槽的主Cache在讀操作未命中時從本地資料庫讀取,而不是像寫操作一樣轉發到主分割槽?由本地資料庫讀取的缺點是很明顯的,會帶來資料的不一致,因為從資料庫可能此時是過期資料,那麼這麼做的目的何在或者說有何好處?

     答案:因為讀取資料在Cache中無法命中的概率遠遠大於寫操作的數量(在Facebook中,大約相差20倍),所以跨分割槽操作對寫操作來說,整體效率影響不大,但是如果很多讀操作採取跨分割槽的方法,讀取操作效率會大幅降低。TAO犧牲資料一致性是為了保證讀取操作的低延遲。


3.TAO的資料一致性

      TAO為了優先考慮讀操作的效率,在資料一致性方面做出了犧牲,採取了最終一致性而非強一致性。在主資料庫有資料變化通知從資料庫時,採取了非同步通知而非同步通知,即無須從資料庫確認更新完成,即可返回客戶端對應的請求。所以主資料庫和從資料庫的資料達到一致有一個時間差,在此期間,可能會導致從分割槽的客戶端讀出過期資料,但是經過較小的時延,這種資料變化一定能夠體現到所有的從資料庫,所以遵循最終一致性。

     具體而言,在大多數情況下,TAO保證了資料的“讀你所寫”一致性。即發出寫操作的客戶端一定能夠讀到更新後的新數值而非過期資料,這在很多情況下是很有必要的,比如,使用者刪除了某位好友,但如果還能在訊息流看到這位好友發出的資訊,這是不能容忍的。

     TAO是如何做到這一點的?首先,如果資料更新操作發生在主分割槽,由上述寫入過程可知,一定可以保證“讀你所寫”一致性,比較棘手的情形是從分割槽的客戶端發出寫請求。在這種情形下,從Cache將請求轉發給主Cache,主Cache將寫請求再次轉發給主分割槽的主Cache,由其寫入主資料庫,在寫入成功後,從分割槽的主Cache通知本分割槽的從Cache更新快取值,以上操作是同步完成的,儘管此時從分割槽的資料庫可能還未接收到主資料庫的更新訊息,但是從分割槽的各級Cache已經同步更新了,之後在這個從分割槽發出的讀請求一定可以從各級Cache中讀到新寫入的內容。通過這種手段就可以保證從分割槽的“讀你所寫”一致性。


相關文章