淘寶海量數據產品的技術架構

淘寶海量數據產品的技術架構是什麼,又是如何應對雙十一的海量訪問的?先看圖:html

20085857-197801ef9d9c448fa3c21a25ffa7aaae

按照數據的流向來劃分,咱們把淘寶數據產品的技術架構分爲五層(如圖1所示),分別是數據源、計算層、存儲層、查詢層和產品層。位於架構頂端的是咱們的數據來源層,這裏有淘寶主站的用戶、店鋪、商品和交易等數據庫,還有用戶的瀏覽、搜索等行爲日誌等。這一系列的數據是數據產品最原始的生命力所在。前端

 

在數據源層實時產生的數據,經過淘寶自主研發的數據傳輸組件DataX、DbSync和Timetunnel準實時地傳輸到一個有1500個節點的Hadoop集羣上,這個集羣咱們稱之爲「雲梯」,是計算層的主要組成部分。在「雲梯」上,咱們天天有大約40000個做業對1.5PB的原始數據按照產品需求進行不一樣的MapReduce計算。這一計算過程一般都能在凌晨兩點以前完成。相對於前端產品看到的數據,這裏的計算結果極可能是一個處於中間狀態的結果,這每每是在數據冗餘與前端計算之間作了適當平衡的結果。java

 

不得不提的是,一些對實效性要求很高的數據,例如針對搜索詞的統計數據,咱們但願能儘快推送到數據產品前端。這種需求再採用「雲梯」來計算效率將是比較低的,爲此咱們作了流式數據的實時計算平臺,稱之爲「銀河」。「銀河」也是一個分佈式系統,它接收來自TimeTunnel的實時消息,在內存中作實時計算,並把計算結果在儘量短的時間內刷新到NoSQL存儲設備中,供前端產品調用。python

 

容易理解,「雲梯」或者「銀河」並不適合直接向產品提供實時的數據查詢服務。這是由於,對於「雲梯」來講,它的定位只是作離線計算的,沒法支持較高的性能和併發需求;而對於「銀河」而言,儘管全部的代碼都掌握在咱們手中,但要完整地將數據接收、實時計算、存儲和查詢等功能集成在一個分佈式系統中,避免不了分層,最終仍然落到了目前的架構上。git

 

爲此,咱們針對前端產品設計了專門的存儲層。在這一層,咱們有基於MySQL的分佈式關係型數據庫集羣MyFOX和基於HBase的NoSQL存儲集羣Prom,在後面的文字中,我將重點介紹這兩個集羣的實現原理。除此以外,其餘第三方的模塊也被咱們歸入存儲層的範疇。存儲層異構模塊的增多,對前端產品的使用帶來了挑戰。爲此,咱們設計了通用的數據中間層——glider——來屏蔽這個影響。glider以HTTP協議對外提供restful方式的接口。數據產品能夠經過一個惟一的URL獲取到它想要的數據。github

 

以上是淘寶海量數據產品在技術架構方面的一個歸納性的介紹。web

分佈式MySQL集羣的查詢代理層——MyFOX

淘寶數據產品選擇MySQL的MyISAM引擎做爲底層的數據存儲引擎。在此基礎上,爲了應對海量數據,咱們設計了分佈式MySQL集羣的查詢代理層——MyFOX,使得分區對前端應用透明。redis

20085853-90aa1831a2304f9aba5477651e2749de

目前,存儲在MyFOX中的統計結果數據已經達到10TB,佔據着數據魔方總數據量的95%以上,而且正在以天天超過6億的增量增加着(如圖2所示)。這些數據被咱們近似均勻地分佈到20個MySQL節點上,在查詢時,經由MyFOX透明地對外服務:算法

20085853-627a9fdcd29242eb83bb6ed5081ef645

值得一提的是,在MyFOX現有的20個節點中,並非全部節點都是「平等」的。通常而言,數據產品的用戶更多地只關心「最近幾天」的數據,越早的數據,越容易被冷落。爲此,出於硬件成本考慮,咱們在這20個節點中分出了「熱節點」和「冷節點」(如上圖)數據庫

顧名思義,「熱節點」存放最新的、被訪問頻率較高的數據。對於這部分數據,咱們但願能給用戶提供儘量快的查詢速度,因此在硬盤方面,咱們選擇了每分鐘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中咱們能夠看到,筆記本電腦的尺寸這一屬性有着10個枚舉值,而「藍牙功能」這個屬性值是個布爾值,數據的篩選性很是差。

在用戶所選擇的過濾條件不肯定的狀況下,解決全屬性問題的思路有兩個:一個是窮舉全部可能的過濾條件組合,在「雲梯」上進行預先計算,存入數據庫供查詢;另外一個是存儲原始數據,在用戶查詢時根據過濾條件篩選出相應的記錄進行現場計算。很明顯,因爲過濾條件的排列組合幾乎是沒法窮舉的,第一種方案在現實中是不可取的;而第二種方案中,原始數據存儲在什麼地方?若是仍然用關係型數據庫,那麼你打算怎樣爲這個表創建索引?

這一系列問題把咱們引到了「建立定製化的存儲、現場計算並提供查詢服務的引擎」的思路上來,這就是Prometheus(以下圖所示)。

淘寶數據魔方技術架構解析【轉】

從圖中能夠看出,咱們選擇了HBase做爲Prom的底層存儲引擎。之因此選擇HBase,主要是由於它是創建在HDFS之上的,而且對於MapReduce有良好的編程接口。儘管Prom是一個通用的、解決共性問題的服務框架,但在這裏,咱們仍然以全屬性選擇爲例,來講明Prom的工做原理。這裏的原始數據是前一天在淘寶上的交易明細,在HBase集羣中,咱們以屬性對(屬性與屬性值的組合)做爲row-key進行存儲。而row-key對應的值,咱們設計了兩個column-family,即存放交易ID列表的index字段和原始交易明細的data字段。在存儲的時候,咱們有意識地讓每一個字段中的每個元素都是定長的,這是爲了支持經過偏移量快速地找到相應記錄,避免複雜的查找算法和磁盤的大量隨機讀取請求。

淘寶數據魔方技術架構解析【轉】

上圖用一個典型的例子描述的Prom在提供查詢服務時的工做原理,限於篇幅,這裏不作詳細描述。值得一提的是,Prom支持的計算並不只限於求和SUM運算,統計意義上的經常使用計算都是支持的。在現場計算方面,咱們對HBase進行了擴展,Prom要求每一個節點返回的數據是已經通過「本地計算」的局部最優解,最終的全局最優解只是各個節點返回的局部最優解的一個簡單彙總。很顯然,這樣的設計思路是要充分利用各個節點的並行計算能力,而且避免大量明細數據的網絡傳輸開銷。

用中間層隔離先後端- glider

上文提到過,MyFOX和Prom爲數據產品的不一樣需求提供了數據存儲和底層查詢的解決方案,但隨之而來的問題是,各類異構的存儲模塊給前端產品的使用帶來了很大的挑戰。而且,前端產品的一個請求所須要的數據每每不可能只從一個模塊獲取。

舉個例子,咱們要在數據魔方中看昨天作熱銷的商品,首先從MyFOX中拿到一個熱銷排行榜的數據,但這裏的「商品」只是一個ID,並無ID所對應的商品描述、圖片等數據。這個時候咱們要從淘寶主站提供的接口中去獲取這些數據,而後一一對應到熱銷排行榜中,最終呈現給用戶。

淘寶數據魔方技術架構解析【轉】

從本質上來說,這就是廣義上的異構「表」之間的JOIN操做。那麼,誰來負責這個事情呢?很容易想到,在存儲層與前端產品之間增長一箇中間層,它負責各個異構「表」之間的數據JOIN和UNION等計算,而且隔離前端產品和後端存儲,提供統一的數據查詢服務。這個中間層就是glider(如圖所示)。

除了起到隔離先後端以及異構「表」之間的數據整合的做用以外,glider的另一個不容忽視的做用即是緩存管理。上文提到過,在特定的時間段內,咱們認爲數據產品中的數據是隻讀的,這是利用緩存來提升性能的理論基礎。

glider中存在兩層緩存,分別是基於各個異構「表」(datasource)的二級緩存和整合以後基於獨立請求的一級緩存。除此以外,各個異構「表」內部可能還存在本身的緩存機制。細心的讀者必定注意到了圖3中MyFOX的緩存設計,咱們沒有選擇對彙總計算後的最終結果進行緩存,而是針對每一個分片進行緩存,其目的在於提升緩存的命中率,而且下降數據的冗餘度。

大量使用緩存的最大問題就是數據一致性問題。如何保證底層數據的變化在儘量短的時間內體現給最終用戶呢?這必定是一個系統化的工程,尤爲對於分層較多的系統來講。

用戶的請求中必定是帶了緩存控制的「命令」的,這包括URL中的query string,和HTTP頭中的「If-None-Match」信息。而且,這個緩存控制「命令」必定會通過層層傳遞,最終傳遞到底層存儲的異構「表」模塊。各異構「表」除了返回各自的數據以外,還會返回各自的數據緩存過時時間(ttl),而glider最終輸出的過時時間是各個異構「表」過時時間的最小值。這一過時時間也必定是從底層存儲層層傳遞,最終經過HTTP頭返回給用戶瀏覽器的。

緩存系統不得不考慮的另外一個問題是緩存穿透與失效時的雪崩效應。緩存穿透是指查詢一個必定不存在的數據,因爲緩存是不命中時被動寫的,而且出於容錯考慮,若是從存儲層查不到數據則不寫入緩存,這將致使這個不存在的數據每次請求都要到存儲層去查詢,失去了緩存的意義。

有不少種方法能夠有效地解決緩存穿透問題,最多見的則是採用布隆過濾器,將全部可能存在的數據哈希到一個足夠大的bitmap中,一個必定不存在的數據會被這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力。在數據魔方里,咱們採用了一個更爲簡單粗暴的方法,若是一個查詢返回的數據爲空(無論是數據不存在,仍是系統故障),咱們仍然把這個空結果進行緩存,但它的過時時間會很短,最長不超過五分鐘。

緩存失效時的雪崩效應對底層系統的衝擊很是可怕。遺憾的是,這個問題目前並無很完美的解決方案。大多數系統設計者考慮用加鎖或者隊列的方式保證緩存的單線程(進程)寫,從而避免失效時大量的併發請求落到底層存儲系統上。在數據魔方中,咱們設計的緩存過時機制理論上可以將各個客戶端的數據失效時間均勻地分佈在時間軸上,必定程度上可以避免緩存同時失效帶來的雪崩效應。

結束語

正是基於本文所描述的架構特色,數據魔方目前已經可以提供壓縮前80TB的數據存儲空間,數據中間層glider支持天天4000萬的查詢請求,平均響應時間在28毫秒(6月1日數據),足以知足將來一段時間內的業務增加需求。儘管如此,整個系統中仍然存在不少不完善的地方。一個典型的例子莫過於各個分層之間使用短鏈接模式的HTTP協議進行通訊。這樣的策略直接致使在流量高峯期單機的TCP鏈接數很是高。因此說,一個良好的架構當然可以在很大程度上下降開發和維護的成本,但它自身必定是隨着數據量和流量的變化而不斷變化的。我相信,過不了幾年,淘寶數據產品的技術架構必定會是另外的樣子。

 

其餘文章摘要:

【1】海量數據領域涵蓋分佈式數據庫、分佈式存儲、數據實時計算、分佈式計算等多個技術方向。

對於海量數據處理,從數據庫層面來說無非就是兩點:一、壓力如何分攤,分攤的目的就是爲了把集中式變爲分佈式。二、採用多種的存儲方案,針對不一樣的業務數據,不一樣的數據特色,採用RDBMS或採用KV Store,選擇不一樣數據庫軟件,使用集中式或分佈式存儲,或者是其餘的一些存儲方案。

 

【2】將數據庫進行拆分,包括水平拆分和垂直拆分。

水平拆分主要解決兩個問題:一、底層存儲的無關性。二、經過線性的去增長機器,支持數據量以及訪問請求包括TPS(Transaction Per Second)、QPS(Query Per Second)的壓力增加。其方式如把一張大數據表按必定的方式拆分到不一樣的數據庫服務器上。海量數據從集中式走向分佈式,可能涉及跨多個IDC容災備份特性。

 

【3】阿里巴巴的數據對不一樣地域數據的處理方法。

由三個產品密切配合解決:是Erosa、Eromanga和Otter。Erosa作MySQL(或其餘數據庫庫)的Bin-Log時時解析,解析後放到Eromanga。Eromanga是增量數據的發佈訂閱的產品。Erosa產生了時時變動的數據發佈到Eromanga。而後各個業務端(搜索引擎、數據倉庫或關聯的業務方)經過訂閱的方式,把時時變動的數據時時的經過Push或Pull的方式拉到其業務端,進行一些業務處理。而Otter就是跨IDC的數據同步,把數據能及時反映到不一樣的AA站。數據同步可能會有衝突,暫時是以那個站點數據爲優先,好比說A機房的站點的數據是優先的,無論怎麼樣,它就覆蓋到B的。

 

【4】對於緩存。

一、注意切分力度,根據業務選擇切分力度。把緩存力度劃分的越細,緩存命中率相對會越高。二、確認緩存的有效生命週期。

 

【5】拆分策略

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

二、按表來拆,把一張表拆到MySQL,那張表拆到MySQL集羣,更相似於垂直拆分。

三、按Schema拆分,Schema拆分跟應用相關的。如把某一模塊服務的數據放到某一機羣,另外一模塊服務的數據放到其餘MySQL機羣。但對外提供的總體服務是這些機羣的總體組合,用Cobar來負責協調處理。

網站應用架構演化

image

  • 單一應用架構
    • 當網站流量很小時,只需一個應用,將全部功能都部署在一塊兒,以減小部署節點和成本。
    • 此時,用於簡化增刪改查工做量的 數據訪問框架(ORM) 是關鍵。
  • 垂直應用架構
    • 當訪問量逐漸增大,單一應用增長機器帶來的加速度愈來愈小,將應用拆成互不相干的幾個應用,以提高效率。
    • 此時,用於加速前端頁面開發的 Web框架(MVC) 是關鍵。
  • 分佈式服務架構
    • 當垂直應用愈來愈多,應用之間交互不可避免,將核心業務抽取出來,做爲獨立的服務,逐漸造成穩定的服務中心,使前端應用能更快速的響應多變的市場需求。
    • 此時,用於提升業務複用及整合的 分佈式服務框架(RPC) 是關鍵。
  • 流動計算架構
    • 當服務愈來愈多,容量的評估,小服務資源的浪費等問題逐漸顯現,此時需增長一個調度中心基於訪問壓力實時管理集羣容量,提升集羣利用率。
    • 此時,用於提升機器利用率的 資源調度和治理中心(SOA) 是關鍵。

幾種通訊協議的比較

整體性能比較:Socket (BIO/NIO/Netty/MINA) > RMI > HTTP Invoker >= Hessian > REST >> Burlap > EJB >> Web Service

  1. 若是協議設計的比較好,Socket性能毫無疑問是最高,同時靈活性和複雜度也最高,若是採用高效的網絡框架如:Mina、Netty等能夠下降開發複雜度,通常在對性能有很是苛刻的條件下使用。
  2. RMI 的性能相對略低,可是與Socket還在同1個數量級,同時只能在Java系統間通訊,若是是基於互聯網使用,還存在穿越防火牆的問題。採用Spring 封裝的方式使用比原始RMI方式性能略高,主要緣由是:Spring採用了代理和緩存機制,節省了對象從新獲取的時間。
  3. HTTPInvoker是Spring特有的,只能在客戶端和服務器端都採用Spring框架下使用,與RMI本質相同,使用java的序列化技術傳輸對象,二者性能差異較小。
  4. Hessian 在數據量較小時性能表現出衆,甚至比RMI還高,在數據結構複雜的對象或者大量數據對象時,較RMI要慢20%左右;Hessian的優勢是精簡高效,同 時能夠跨語言使用,目前支持Java,C++, .net, python, ruby等語言。另外Hessian能夠充分利用web容器的成熟功能,在處理大量用戶訪問時頗有優點,在資源分配、線程排隊、異常處理等方面均可以由 web容器保證,而RMI自己不提供多線程的服務器。
  5. REST架構也是一種比較簡單、高效的Web服務架構,相對於Hessian性能略低,但還在同一個數量級,同時也是基於HTTP協議,目前也有比較多的成功案例。
  6. Burlap 在數據量很是小時性能尚可,同時性能隨着數據量的增長急劇下降,一般性能耗時是RMI的3倍左右,主要緣由是:Hessian採用二進制傳輸數據,而 Burlap採用XML格式,而XML描述內容太多,一樣的結構,其傳輸量要大不少,同時,XML的解析是比較耗資源的,尤爲大數據量狀況下更是如此。
  7. EJB基於RMI協議,性能不高,同時只能在Java系統內使用,不能跨語言,目前使用愈來愈少,目前阿里巴巴內部已經徹底放棄EJB。
  8. 在 這些遠程調用協議中,Web Service的性能是最低的,通常狀況下,Web Service的性能相對於Hessian性能要慢10~20倍左右,同時,對於一樣的訪問請求,Web Service的傳輸數據量約爲Hessian的6倍左右,對網絡帶寬消耗很是大,同時XML的解碼器廣泛性能不高,XML<->Java Bean的編碼、解碼很是耗費資源,對於併發和負載比較高的網站不是一個好的選擇。同時,Web Service的使用也不太方便。

總結:Hessian和REST架構我的認爲是比較優秀的高性能通訊協議,若是對性能要求特別苛刻能夠直接採用Socket方式,目前,阿里巴巴內部的遠程調用主要採用Hessian和Dubbo(基於Mina/Netty框架),經受了苛刻的高併發、高負載考驗。

更多參考

 

(原文:http://server.51cto.com/taobao2012/

相關文章
相關標籤/搜索