1. 程式人生 > >京東評價系統海量資料儲存設計

京東評價系統海量資料儲存設計

作者:韋仕,京東商城交易平臺評價社群負責人,2010年加入京東,先後參與了使用者、商品、評論等系統的架構升級工作。

京東的商品評論目前已達到數十億條,每天提供的服務呼叫也有數十億次,而這些資料每年還在成倍增長,而資料儲存是其中最重要的部分之一,接下來就介紹下京東評論系統的資料儲存是如何設計的。

整體資料儲存包括基礎資料儲存、文字儲存、資料索引、資料快取幾個部分。

基礎資料儲存

基礎資料儲存使用mysql,因使用者評論為文字資訊,通常包含文字、字元等,佔用的儲存空間比較大,為此mysql作為基礎資料庫只儲存非文字的評論基礎資訊,包括評論狀態、使用者、時間等基礎資料,以及圖片、標籤、點贊等附加資料。而不同的資料又可選擇不同的庫表拆分方案,參考如下:

  • 評論基礎資料按使用者ID進行拆庫並拆表;
  • 圖片及標籤處於同一資料庫下,根據商品編號分別進行拆表;
  • 其它的擴充套件資訊資料,因資料量不大、訪問量不高,處理於同一庫下且不做分表即可。

因人而異、因系統而異,根據不同的資料場景選擇不同儲存方案,有效利用資源的同時還能解決資料儲存問題,為高效能、高可用服務打下堅實基礎。

文字儲存

文字儲存使用了mongodb、hbase,選擇nosql而非mysql,一是減輕了mysql儲存壓力,釋放msyql,龐大的儲存也有了可靠的保障;二是nosql的高效能讀寫大大提升了系統的吞吐量並降低了延遲。儲存的升級過程嘗試了cassandra、mongodb等分散式的nosql儲存,cassandra適用於寫多讀少的情況,而 mongodb也是基於分散式檔案儲存的資料庫,介於關係型資料庫與非關係型資料庫之間,同時也是記憶體級資料庫,mongo寫效能不及 cassandra,但讀寫分離情況下讀效能相當不錯,因此從應用場景上我們選擇了mongodb。mongodb確實不錯,也支援了系統穩定運行了好幾年。

但從今後的資料增長、業務擴增、應用擴充套件等多方面考慮,hbase才是最好的選擇,它的儲存能力、可靠性、可擴充套件性都是毋庸置疑的。選擇了 hbase,只需要根據評論ID構建Rowkey,然後將評論文字資訊進行儲存,查詢時只需要根據ID便能快速讀取評論的文字內容,當然也可將評論的其它欄位資訊進行冗餘儲存,這樣根據評論ID讀取評論資訊後不用再從mysql進行讀取,減少資料操作,提升查詢效能。

資料索引

京東的評論是以使用者和商品兩個維度進行劃分的。對於使用者而言,使用者需要發表評論、上傳晒圖、檢視自己的評論等,因此mysql資料庫中只要根據使用者ID對評論資料進行拆庫拆表進行儲存,便能解決使用者資料讀寫問題。而對於商品而言,前臺需要將統計商品的評論數並將所有評論展示出來,後臺需根據評論的全欄位進行檢索同時還帶模糊查詢,而評論資料是按userId進行庫表拆分的,現在要按商品去獲取評論,顯然當前的拆分庫是無法實現的。起初考慮過根據商品編號再進行拆庫拆表,但經過多層分析後發現行不通,因為再按商品編號進行拆分,得再多加一倍機器,硬體成本非常高,同時要保持使用者及商品兩維度的分庫資料高度一致,不僅增加了系統維護成本及業務複雜度,同時也無法解決評論的資料統計、列表篩選、模糊查詢等問題,為此引入了全文檢索框架solr(前臺)/elasticsearch(後臺)進行資料索引。

資料索引其實就是將評論資料構建成索引儲存於索引服務中,便於進行評論資料的模糊查詢、條件篩選及切面統計等,以彌補以上資料儲存無法完成的功能。京東評論系統為此使用了solr/elasticsearch搜尋服務,它們都是基於Lucene的全文檢索框架,也是分散式的搜尋框架(solr4.0後增加了solr cloud以支援分散式),支援資料分片、切面統計、高亮顯示、分詞檢索等功能,利用搜索框架能有效解決前臺評論資料統計、列表篩選問題,也能支援後臺系統中的關鍵詞顯示、多欄位檢索及模糊查詢,可謂是一舉多得。

搜尋在構建索引時,屬性欄位可分為儲存欄位與索引欄位,儲存欄位在建立索引後會將內容儲存於索引文件中,同時也會佔用相應的索引空間,查詢後可返回原始內容,而索引欄位建立索引後不佔用索引空間也無法返回原始內容,只能用於查詢,因此對於較長的內容建議不進行儲存索引。

評論搜尋在構建索引時,主鍵評論ID的索引方式設定為儲存,其它欄位設定為索引,這樣不僅減少索引檔案的儲存空間,也大大提升了索引的構建效率與查詢效能。當然,在使用搜索框架時,業務資料量比較小的也可選擇將所有欄位進行儲存,這樣在搜尋中查詢出結果後將不需要從資料庫上查詢其它資訊,也減輕了資料庫的壓力。

為了更好地應對前後臺不同的業務場景,搜尋叢集被劃分為前臺搜尋叢集和後臺搜尋叢集。

前臺搜尋叢集根據商品編號進行索引資料分片,用於解決評論前臺的評論數統計、評論列表篩選功能。評論數統計,如果使用常規資料庫進行統計時,需要進行sql上的group分組統計,如果只有單個分組統計效能上還能接受,但京東的評論數統計則需要對1到5分的評論分別進行統計,分組增加的同時隨著統計量的增加資料庫的壓力也會增加,因此在mysql上通過group方式進行統計是行不通的。而使用solr的切面統計,只需要一次查詢便能輕鬆地統計出商品每個分級的評論數,而且查詢效能也是毫秒級的。切面統計用法如下:

索引資料

評論列表,只需根據條件從搜尋中查詢出評論ID集合,再根據評論ID到mysql、Hbase中查詢出評論的其它欄位資訊,經過資料組裝後便可返回前臺進行展示。

mysql

後臺搜尋叢集,評論後臺系統需要對評論進行查詢,其中包括關鍵詞高亮顯示、全欄位檢索、模糊查詢等,為此solr/elasticsearch都是個很好的選擇,目前使用elasticsearch。

未來也計劃將前臺搜尋叢集切換為elasticsearch。

資料快取

面對數十億的資料請求,直接擊穿到mysql、搜尋服務上都是無法承受的,所以需要對評論資料進行快取,在此選擇了高效能快取redis,根據不同的業務資料進行叢集劃分,同時採用多機房主從方式部署解決單點問題,這樣只需要對不同的快取叢集進行相應的水平擴充套件便能快速提升資料吞吐能力,也有效地保證了服務的高效能、高可用。

當然,快取設計時還有很多細節可以進行巧妙處理的,如:

  • 當用戶新發表一條評論,要實現前臺實時展示,可以將新增的評論數向首屏列表快取中追加最新的評論資訊;
  • 評論數是讀多寫少,這樣就可以將評論數持久化到redis當中,只有當資料進行更新時通過非同步的方式去將快取重新整理即可;評論數展示可通過nginx+lua的方式提供服務,服務請求無需回源到應用上,不僅提升服務效能,也能減輕應用系統的壓力;
  • 對於評論列表,通常訪問的都是第一屏的資料,也就是第一頁的資料,可以將第一頁的資料快取到redis當中,有資料更新時再通過非同步程式去更新;
  • 對於秒殺類的商品,評論資料可以結合本地快取提前進行預熱,這樣當秒殺流量瞬間湧入的時候也不會對快取叢集造成壓力;通過減短key長度、去掉多餘屬性、壓縮文字等方式節省記憶體空間,提高記憶體使用率。

資料容災與高可用

引入了這麼多的儲存方案就是為了解決大資料量儲存問題及實現資料服務的高可用,同時合理的部署設計與相應的容災處理也必須要有的。以上資料儲存基本都使用多機房主從方式部署,各機房內部實現主從結構進行資料同步。如圖:

資料容災與高可用

mysql叢集資料庫拆庫後需要對各分庫進行多機房主從部署,系統應用進行讀寫分離並根據機房進行就近呼叫,當主機房資料庫出現故障後將故障機房的資料操作都切換到其它機房,待故障排除後再進行資料同步與流量切換。

使用主從機房部署的方式所有資料更新操作都要在主庫上進行,而當主機房故障是需要通過資料庫主從關係的重建、應用重新配置與釋出等一系列操作後才能解決流量切換,過程較為複雜且影響面較大,所以這是個單點問題,為此實現資料服務多中心將是我們下一個目標。

多中心根據特定規則將使用者分別路由到不同機房進行資料讀寫,各機房間通過資料匯流排進行資料同步,當某一機房出現故障,只需要一鍵操作便能快速地將故障機房的使用者流量全部路由到其它機房,實現了資料的多寫多活,也進一步實現了服務的高可用。資料多中心如下:

資料多中心

hbase叢集目前使用的是京東的公有叢集,實現了雙機房主備部署,主叢集出現故障後自動將流量切換到備用叢集,而當hbase整個叢集故障時還可對其進行降級,同步只寫入快取及備用儲存mongo,待叢集恢復後再由後臺非同步任務將資料回寫到hbase當中。

搜尋叢集根據商品編號進行索引資料分片多機房主從部署,並保證至少3個從節點並部署於多個機房當中,當主節點出現故障後從這些從節點選取其中一個作為新的主提供服務。叢集主節點只提供非同步任務進行索引更新操作,從節點根據應用機房部署情況提供索引查詢服務。

Redis快取叢集主從部署仍是標配,主節點只提供資料的更新操作,從節點提供前臺快取讀服務,實現快取資料的讀寫分離,提升了快取服務的處理能力。當主節點出現故障,選取就近機房的一個從節點作為新主節點提供寫服務,並將主從關係進行重新構建。任何一從節點出現故障都可通過內部的配置中心進行一鍵切換,將故障節點的流量切換到其它的從節點上。

 總結

整體資料架構並沒有什麼高大上的設計,而且整體資料架構方案也是為了解決實際痛點和業務問題而演進過來的。資料儲存方案上沒有最好的,只有最適合的,因此得根據不同的時期、不同的業務場景去選擇合適的設計才是最關鍵的,大家有什麼好的方案和建議可以相互討論與借鑑,系統的穩定、高效能、高可用才是王道。

文章出處:開濤的部落格(公眾號ID:kaitao-1234567)