天天看點

淘寶應對雙"11"的技術架構分析

淘寶應對雙"11"的技術架構分析

        雙“11”最熱門的話題是TB ,最近正好和阿裡的一個朋友聊淘寶的技術架構,發現很多有意思的地方,分享一下他們的解析資料:

淘寶海量資料産品技術架構

資料産品的一個最大特點是資料的非實時寫入,正因為如此,我們可以認為,在一定的時間段内,整個系統的資料是隻讀的。這為我們設計緩存奠定了非常重要的基礎。

圖1 淘寶海量資料産品技術架構

按照資料的流向來劃分,我們把淘寶資料産品的技術架構分為五層(如圖1所示),分别是資料源、計算層、存儲層、查詢層和産品層。位于架構頂端的是我們的資料來源層,這裡有淘寶主站的使用者、店鋪、商品和交易等資料庫,還有使用者的浏覽、搜尋等行為日志等。這一系列的資料是資料産品最原始的生命力所在。

在資料源層實時産生的資料,通過淘寶自主研發的資料傳輸元件DataX、DbSync和Timetunnel準實時地傳輸到一個有1500個節點的Hadoop叢集上,這個叢集我們稱之為“雲梯”,是計算層的主要組成部分。在“雲梯”上,我們每天有大約40000個作業對1.5PB的原始資料按照産品需求進行不同的MapReduce計算。這一計算過程通常都能在淩晨兩點之前完成。相對于前端産品看到的資料,這裡的計算結果很可能是一個處于中間狀态的結果,這往往是在資料備援與前端計算之間做了适當平衡的結果。

不得不提的是,一些對實效性要求很高的資料,例如針對搜尋詞的統計資料,我們希望能盡快推送到資料産品前端。這種需求再采用“雲梯”來計算效率将是比較低的,為此我們做了流式資料的實時計算平台,稱之為“銀河”。“銀河”也是一個分布式系統,它接收來自TimeTunnel的實時消息,在記憶體中做實時計算,并把計算結果在盡可能短的時間内重新整理到NoSQL儲存設備中,供前端産品調用。

容易了解,“雲梯”或者“銀河”并不适合直接向産品提供實時的資料查詢服務。這是因為,對于“雲梯”來說,它的定位隻是做離線計算的,無法支援較高的性能和并發需求;而對于“銀河”而言,盡管所有的代碼都掌握在我們手中,但要完整地将資料接收、實時計算、存儲和查詢等功能內建在一個分布式系統中,避免不了分層,最終仍然落到了目前的架構上。

為此,我們針對前端産品設計了專門的存儲層。在這一層,我們有基于MySQL的分布式關系型資料庫叢集MyFOX和基于HBase的NoSQL存儲叢集Prom,在後面的文字中,我将重點介紹這兩個叢集的實作原理。除此之外,其他第三方的子產品也被我們納入存儲層的範疇。

存儲層異構子產品的增多,對前端産品的使用帶來了挑戰。為此,我們設計了通用的資料中間層——glider——來屏蔽這個影響。glider以HTTP協定對外提供restful方式的接口。資料産品可以通過一個唯一的URL擷取到它想要的資料。

以上是淘寶海量資料産品在技術架構方面的一個概括性的介紹,接下來我将重點從四個方面闡述資料魔方設計上的特點。

關系型資料庫仍然是王道

關系型資料庫(RDBMS)自20世紀70年代提出以來,在工業生産中得到了廣泛的使用。經過三十多年的長足發展,誕生了一批優秀的資料庫軟體,例如Oracle、MySQL、DB2、Sybase和SQL Server等。

淘寶應對雙"11"的技術架構分析

圖2 MyFOX中的資料增長曲線

盡管相對于非關系型資料庫而言,關系型資料庫在分區容忍性(Tolerance to Network Partitions)方面存在劣勢,但由于它強大的語義表達能力以及資料之間的關系表達能力,在資料産品中仍然占據着不可替代的作用。

淘寶資料産品選擇MySQL的MyISAM引擎作為底層的資料存儲引擎。在此基礎上,為了應對海量資料,我們設計了分布式MySQL叢集的查詢代理層——MyFOX,使得分區對前端應用透明。

淘寶應對雙"11"的技術架構分析

圖3 MyFOX的資料查詢過程

目前,存儲在MyFOX中的統計結果資料已經達到10TB,占據着資料魔方總資料量的95%以上,并且正在以每天超過6億的增量增長着(如圖2所示)。這些資料被我們近似均勻地分布到20個MySQL節點上,在查詢時,經由MyFOX透明地對外服務(如圖3所示)。

淘寶應對雙"11"的技術架構分析

圖4 MyFOX節點結構

值得一提的是,在MyFOX現有的20個節點中,并不是所有節點都是“平等”的。一般而言,資料産品的使用者更多地隻關心“最近幾天”的資料,越早的資料,越容易被冷落。為此,出于硬體成本考慮,我們在這20個節點中分出了“熱節點”和“冷節點”(如圖4所示)。

顧名思義,“熱節點”存放最新的、被通路頻率較高的資料。對于這部分資料,我們希望能給使用者提供盡可能快的查詢速度,是以在硬碟方面,我們選擇了每分鐘15000轉的SAS硬碟,按照一個節點兩台機器來計算,機關資料的存儲成本約為4.5W/TB。相對應地,“冷資料”我們選擇了每分鐘7500轉的SATA硬碟,單碟上能夠存放更多的資料,存儲成本約為1.6W/TB。

将冷熱資料進行分離的另外一個好處是可以有效提高記憶體磁盤比。從圖4可以看出,“熱節點”上單機隻有24GB記憶體,而磁盤裝滿大約有1.8TB(300 * 12 * 0.5 / 1024),記憶體磁盤比約為4:300,遠遠低于MySQL伺服器的一個合理值。記憶體磁盤比過低導緻的後果是,總有一天,即使所有記憶體用完也存不下資料的索引了——這個時候,大量的查詢請求都需要從磁盤中讀取索引,效率大打折扣。

NoSQL是SQL的有益補充

在MyFOX出現之後,一切都看起來那麼完美,開發人員甚至不會意識到MyFOX的存在,一條不用任何特殊修飾的SQL語句就可以滿足需求。這個狀态持續了很長一段時間,直到有一天,我們碰到了傳統的關系型資料庫無法解決的問題——全屬性選擇器(如圖5所示)。

淘寶應對雙"11"的技術架構分析

圖5 全屬性選擇器

這是一個非常典型的例子。為了說明問題,我們仍然以關系型資料庫的思路來描述。對于筆記本電腦這個類目,使用者某一次查詢所選擇的過濾條件可能包括“筆記本尺寸”、“筆記本定位”、“硬碟容量”等一系列屬性(字段),并且在每個可能用在過濾條件的屬性上,屬性值的分布是極不均勻的。在圖5中我們可以看到,筆記本電腦的尺寸這一屬性有着10個枚舉值,而“藍牙功能”這個屬性值是個布爾值,資料的篩選性非常差。

在使用者所選擇的過濾條件不确定的情況下,解決全屬性問題的思路有兩個:一個是窮舉所有可能的過濾條件組合,在“雲梯”上進行預先計算,存入資料庫供查詢;另一個是存儲原始資料,在使用者查詢時根據過濾條件篩選出相應的記錄進行現場計算。很明顯,由于過濾條件的排列組合幾乎是無法窮舉的,第一種方案在現實中是不可取的;而第二種方案中,原始資料存儲在什麼地方?如果仍然用關系型資料庫,那麼你打算怎樣為這個表建立索引?

這一系列問題把我們引到了“建立定制化的存儲、現場計算并提供查詢服務的引擎”的思路上來,這就是Prometheus(如圖6所示)。

淘寶應對雙"11"的技術架構分析

圖6 Prom的存儲結構

從圖6可以看出,我們選擇了HBase作為Prom的底層存儲引擎。之是以選擇HBase,主要是因為它是建立在HDFS之上的,并且對于MapReduce有良好的程式設計接口。盡管Prom是一個通用的、解決共性問題的服務架構,但在這裡,我們仍然以全屬性選擇為例,來說明Prom的工作原理。這裡的原始資料是前一天在淘寶上的交易明細,在HBase叢集中,我們以屬性對(屬性與屬性值的組合)作為row-key進行存儲。而row-key對應的值,我們設計了兩個column-family,即存放交易ID清單的index字段和原始交易明細的data字段。在存儲的時候,我們有意識地讓每個字段中的每一個元素都是定長的,這是為了支援通過偏移量快速地找到相應記錄,避免複雜的查找算法和磁盤的大量随機讀取請求。

淘寶應對雙"11"的技術架構分析

圖7 Prom查詢過程

圖7用一個典型的例子描述的Prom在提供查詢服務時的工作原理,限于篇幅,這裡不做較長的描述。值得一提的是,Prom支援的計算并不僅限于求和SUM運算,統計意義上的常用計算都是支援的。在現場計算方面,我們對Hbase進行了擴充,Prom要求每個節點傳回的資料是已經經過“本地計算”的局部最優解,最終的全局最優解隻是各個節點傳回的局部最優解的一個簡單彙總。很顯然,這樣的設計思路是要充分利用各個節點的并行計算能力,并且避免大量明細資料的網絡傳輸開銷。

用中間層隔離前後端

上文提到過,MyFOX和Prom為資料産品的不同需求提供了資料存儲和底層查詢的解決方案,但随之而來的問題是,各種異構的存儲子產品給前端産品的使用帶來了很大的挑戰。并且,前端産品的一個請求所需要的資料往往不可能隻從一個子產品擷取。

舉個例子,我們要在資料魔方中看昨天做熱銷的商品,首先從MyFOX中拿到一個熱銷排行榜的資料,但這裡的“商品”隻是一個ID,并沒有ID所對應的商品描述、圖檔等資料。這個時候我們要從淘寶主站提供的接口中去擷取這些資料,然後一一對應到熱銷排行榜中,最終呈現給使用者。

淘寶應對雙"11"的技術架構分析

圖8 glider的技術架構

有經驗的讀者一定可以想到,從本質上來講,這就是廣義上的異構“表”之間的JOIN操作。那麼,誰來負責這個事情呢?很容易想到,在存儲層與前端産品之間增加一個中間層,它負責各個異構“表”之間的資料JOIN和UNION等計算,并且隔離前端産品和後端存儲,提供統一的資料查詢服務。這個中間層就是glider(如圖8所示)。

緩存是系統化的工程

除了起到隔離前後端以及異構“表”之間的資料整合的作用之外,glider的另外一個不容忽視的作用便是緩存管理。上文提到過,在特定的時間段内,我們認為資料産品中的資料是隻讀的,這是利用緩存來提高性能的理論基礎。

在圖8中我們看到,glider中存在兩層緩存,分别是基于各個異構“表”(datasource)的二級緩存和整合之後基于獨立請求的一級緩存。除此之外,各個異構“表”内部可能還存在自己的緩存機制。細心的讀者一定注意到了圖3中MyFOX的緩存設計,我們沒有選擇對彙總計算後的最終結果進行緩存,而是針對每個分片進行緩存,其目的在于提高緩存的命中率,并且降低資料的備援度。

大量使用緩存的最大問題就是資料一緻性問題。如何保證底層資料的變化在盡可能短的時間内展現給最終使用者呢?這一定是一個系統化的工程,尤其對于分層較多的系統來說。

淘寶應對雙"11"的技術架構分析

圖9 緩存控制體系

圖9向我們展示了資料魔方在緩存控制方面的設計思路。使用者的請求中一定是帶了緩存控制的“指令”的,這包括URL中的query string,和HTTP頭中的“If-None-Match”資訊。并且,這個緩存控制“指令”一定會經過層層傳遞,最終傳遞到底層存儲的異構“表”子產品。各異構“表”除了傳回各自的資料之外,還會傳回各自的資料緩存過期時間(ttl),而glider最終輸出的過期時間是各個異構“表”過期時間的最小值。這一過期時間也一定是從底層存儲層層傳遞,最終通過HTTP頭傳回給使用者浏覽器的。

緩存系統不得不考慮的另一個問題是緩存穿透與失效時的雪崩效應。緩存穿透是指查詢一個一定不存在的資料,由于緩存是不命中時被動寫的,并且出于容錯考慮,如果從存儲層查不到資料則不寫入緩存,這将導緻這個不存在的資料每次請求都要到存儲層去查詢,失去了緩存的意義。

有很多種方法可以有效地解決緩存穿透問題,最常見的則是采用布隆過濾器,将所有可能存在的資料哈希到一個足夠大的bitmap中,一個一定不存在的資料會被這個bitmap攔截掉,進而避免了對底層存儲系統的查詢壓力。在資料魔方裡,我們采用了一個更為簡單粗暴的方法,如果一個查詢傳回的資料為空(不管是資料不存在,還是系統故障),我們仍然把這個空結果進行緩存,但它的過期時間會很短,最長不超過五分鐘。

緩存失效時的雪崩效應對底層系統的沖擊非常可怕。遺憾的是,這個問題目前并沒有很完美的解決方案。大多數系統設計者考慮用加鎖或者隊列的方式保證緩存的單線程(程序)寫,進而避免失效時大量的并發請求落到底層存儲系統上。在資料魔方中,我們設計的緩存過期機制理論上能夠将各個用戶端的資料失效時間均勻地分布在時間軸上,一定程度上能夠避免緩存同時失效帶來的雪崩效應。

結束語

正是基于本文所描述的架構特點,資料魔方目前已經能夠提供壓縮前80TB的資料存儲空間,資料中間層glider支援每天4000萬的查詢請求,平均響應時間在28毫秒(6月1日資料),足以滿足未來一段時間内的業務增長需求。

盡管如此,整個系統中仍然存在很多不完善的地方。一個典型的例子莫過于各個分層之間使用短連接配接模式的HTTP協定進行通信。這樣的政策直接導緻在流量高峰期單機的TCP連接配接數非常高。是以說,一個良好的架構固然能夠在很大程度上降低開發和維護的成本,但它自身一定是随着資料量和流量的變化而不斷變化的。我相信,過不了幾年,淘寶資料産品的技術架構一定會是另外的樣子。

其他文章摘要

海量資料領域涵蓋分布式資料庫、分布式存儲、資料實時計算、分布式計算等多個技術方向。

對于海量資料處理,從資料庫層面來講無非就是兩點:1、壓力如何分攤,分攤的目的就是為了把集中式變為分布式。2、采用多種的存儲方案,針對不同的業務資料,不同的資料特點,采用RDBMS或采用KV Store,選擇不同資料庫軟體,使用集中式或分布式存儲,或者是其他的一些存儲方案。

将資料庫進行拆分,包括水準拆分和垂直拆分。

水準拆分主要解決兩個問題:1、底層存儲的無關性。2、通過線性的去增加機器,支援資料量以及通路請求包括TPS(Transaction Per Second)、QPS(Query Per Second)的壓力增長。其方式如把一張大資料表按一定的方式拆分到不同的資料庫伺服器上。

海量資料從集中式走向分布式,可能涉及跨多個IDC容災備份特性。

阿裡巴巴的資料對不同地域資料的處理方法

由三個産品密切配合解決:是Erosa、Eromanga和Otter。

Erosa做MySQL(或其他資料庫庫)的Bin-Log時時解析,解析後放到Eromanga。Eromanga是增量資料的釋出訂閱的産品。Erosa産生了時時變更的資料釋出到Eromanga。然後各個業務端(搜尋引擎、資料倉庫或關聯的業務方)通過訂閱的方式,把時時變更的資料時時的通過Push或Pull的方式拉到其業務端,進行一些業務處理。而Otter就是跨IDC的資料同步,把資料能及時反映到不同的AA站。

資料同步可能會有沖突,暫時是以那個站點資料為優先,比如說A機房的站點的資料是優先的,不管怎麼樣,它就覆寫到B的。

對于緩存。

1、注意切分力度,根據業務選擇切分力度。把緩存力度劃分的越細,緩存命中率相對會越高。

2、确認緩存的有效生命周期。

拆分政策

1、按字段拆分(最細力度)。如把表的Company字段拆掉,就按COMPANY_ID來拆。

2、按表來拆,把一張表拆到MySQL,那張表拆到MySQL叢集,更類似于垂直拆分。

3、按Schema拆分,Schema拆分跟應用相關的。如把某一子產品服務的資料放到某一機群,另一子產品服務的資料放到其他MySQL機群。但對外提供的整體服務是這些機群的整體組合,用Cobar來負責協調處理。

Cobar類似于MySQL Proxy,解析MySQL所有的協定,相當于可以把它看成MySQL Server來通路的。 

繼續閱讀