B站千億級點贊系統服務架構設計

陶然陶然發表於2023-02-06

   一、前言

  1、什麼是點贊

  點贊相信大家都不陌生,在B站,點贊是UP主們跟粉絲之間的特殊羈絆。B 站的點贊系統提供了對影片、動態、專欄、評論、彈幕等多種實體維度的點贊、點踩以及其餘配套的資料查詢的能力。

  稿件點贊(類推動態、專欄、評論等也具備該功能)  

  總獲贊數  

  點贊記錄  

  2、點贊服務需要提供哪些系統能力

  業務能力:

  以 “稿件” 為例,點贊服務需要提供

  對某個稿件點贊(取消點贊)、點踩(取消點踩)

  查詢是否對 單個 或者 一批稿件 點過贊(踩) - 即點贊狀態查詢

  查詢某個稿件的點贊數

  查詢某個使用者的點贊列表

  查詢某個稿件的點贊人列表

  查詢使用者收到的總點贊數

  平臺能力:

  點贊作為一個與社群實體共存的服務,中臺化、平臺化也是點贊服務需要具備的能力

  提供業務快速接入的能力(配置級別)

  資料儲存上(快取、DB),具備資料隔離儲存的能力(多租戶)

  容災能力:

  作為被使用者強感知的站內功能,需要考慮到各種情況下的系統容災。例如當:

  儲存不可用

  例如當DB不可用時,需要依託快取儘可能提供服務。

  同理當快取不可用時,DB也需要保證自己不當機的情況下儘可能提供服務。

  訊息佇列不可用

  當訊息佇列不可用時,依託B站自研的railgun,透過RPC呼叫的方式自動降級

  機房災難

  切換機房

  資料同步延遲

  比如點贊就遇到過因為Tidb的資料同步問題(斷流、延遲)導致的點贊計數同步延遲問題。

  當然還有其他比如 下游依賴當機、點贊訊息堆積 以及其他未知問題

  3、系統需要承載哪些壓力

  1.流量壓力

  全域性流量壓力:

  全站點贊狀態查詢、點贊數查詢等【讀流量】超過300k,點贊、點踩等【寫流量】超過15K

  針對寫流量,為了保證資料寫入效能,我們在寫入【點贊數】資料的時候,在記憶體中做了部分聚合寫入,比如聚合10s內的點贊數,一次性寫入。如此可大量減少資料庫的IO次數。

  同時資料庫的寫入我們也做了全面的非同步化處理,保證了資料庫能以合理的速率處理寫入請求。

  另外為了保證點贊狀態的正確性,且同時讓程式擁有自我糾錯的能力,我們在每一次更新點贊狀態之前,都會取出老的點贊狀態作為更新依據。例如:如果當前是取消點贊,那麼需要資料庫中已有點贊狀態。

  單點流量(熱點)壓力:

  熱門事件、稿件等帶來的系統熱點問題,包括DB熱點、快取熱點

  當一個稿件成為超級熱門的時候,大量的流量就會湧入到儲存的單個分片上,造成讀寫熱點問題。此時需要有熱點識別機制來識別該熱點,並將資料快取至本地,並設定合理的TTL。例如,UP主 【傑威爾音樂】釋出第一個稿件的時候就是一次典型熱點事件。

  2.資料儲存壓力

  點贊資料儲存超過千億級別

  如何高效的利用儲存介質存放這些資料才能既滿足業務需求,也能最大程度節省成本,也是一個點贊服務正在努力改造的工程 - KV化儲存

  3.面對未知災難

  DB當機、Redis叢集抖動、機房故障、網路故障等

  針對前言中提到的點讚的平臺能力、系統壓力與容災,我會在下文中作出更加詳細的介紹。

   二、點贊整體系統架構簡介

  為了在提供上述能力的前提下經受住流量、儲存、容災三大壓力,點贊目前的系統實現方式如下:

  1、系統架構簡介  

  整個點贊服務的系統可以分為五個部分

  1、流量路由層(決定流量應該去往哪個機房)

  2、業務閘道器層(統一鑑權、反黑灰產等統一流量篩選)

  3、點贊服務(thumbup-service),提供統一的RPC介面

  4、點贊非同步任務(thumbup-job)

  5、資料層(db、kv、redis)

  下文將重點分享下資料儲存層、點贊服務層(thumbup-service)與 非同步任務層(thumbup-job)的系統設計

  2、三級資料儲存

  基本資料模型:

  點贊記錄表:記錄使用者在什麼時間對什麼實體進行了什麼型別的操作(是贊還是踩,是取消點贊還是取消點踩)等

  點贊計數表:記錄被點贊實體的累計點贊(踩)數量

  ①、第一層儲存:DB層 - (TiDB)

  點贊系統中最為重要的就是點贊記錄表(likes)和點贊計數表(counts),負責整體資料的持久化儲存,以及提供快取失效時的回源查詢能力。

  點贊記錄表 - likes : 每一次的點贊記錄(使用者Mid、被點讚的實體ID(messageID)、點贊來源、時間)等資訊,並且在Mid、messageID兩個維度上建立了滿足業務求的聯合索引。

  點贊數表 - counts : 以業務ID(BusinessID)+實體ID(messageID)為主鍵,聚合了該實體的點贊數、點踩數等資訊。並且按照messageID維度建立滿足業務查詢的索引。

  由於DB採用的是分散式資料庫TiDB,所以對業務上無需考慮分庫分表的操作

  ②、第二層儲存

  快取層Cache:點贊作為一個高流量的服務,快取的設立肯定是必不可少的。點贊系統主要使用的是CacheAside模式。這一層快取主要基於Redis快取:以點贊數和使用者點贊列表為例

  1.點贊數  

  key-value = count:patten:{business_id}:{message_id} - {likes},{disLikes}用業務ID和該業務下的實體ID作為快取的Key,並將點贊數與點踩數拼接起來儲存以及更新

  2.使用者點贊列表  

  key-value = user:likes:patten:{mid}:{business_id} - member(messageID)-score(likeTimestamp)* 用mid與業務ID作為key,value則是一個ZSet,member為被點讚的實體ID,score為點讚的時間。當改業務下某使用者有新的點贊操作的時候,被點讚的實體則會透過 zadd的方式把最新的點贊記錄加入到該ZSet裡面來為了維持使用者點贊列表的長度(不至於無限擴張),需要在每一次加入新的點贊記錄的時候,按照固定長度裁剪使用者的點贊記錄快取。該設計也就代表使用者的點贊記錄在快取中是有限制長度的,超過該長度的資料請求需要回源DB查詢

  ③、第三層儲存

  LocalCache - 本地快取

  本地快取的建立,目的是為了應對快取熱點問題。

  利用最小堆演算法,在可配置的時間視窗範圍內,統計出訪問最頻繁的快取Key,並將熱Key(Value)按照業務可接受的TTL儲存在本地記憶體中。

  其中熱點的發現之前也有同步過:https://mp.weixin.qq.com/s/C8CI-1DDiQ4BC_LaMaeDBg

  ④、針對TIDB海量歷史資料的遷移歸檔

  遷移歸檔的原因(初衷),是為了減少TIDB的儲存容量,節約成本的同時也多了一層儲存,可以作為災備資料。

  以下是在KV資料庫(taishan)中點讚的資料與索引的組織形式:

  1.點贊記錄

  1_{mid}_${business_id}_${type}_${message_id} => {origin_id}_{mtime}

  2.使用者點贊列表索引  

  2_{mid}_${business_id}_${type}_${mtime}_{message_id} => {origin_id}

  3.實體維度點贊記錄索引  

  3_{message_id}_${business_id}_${type}_${mtime}_${mid}=>{origin_id}

  3、儲存層的最佳化和思考

  作為一個典型的大流量基礎服務,點讚的儲存架構需要最大程度上滿足兩個點

  ①、滿足業務讀寫需求的同時具備最大的可靠性

  ②、選擇合適的儲存介質與資料儲存形態,最小化儲存成本

  從以上兩點觸發,考慮到KV資料在業務查詢以及效能上都更契合點讚的業務形態,且TaiShan可以水平擴容來滿足業務的增長。點贊服務從當前的關係型資料庫(TiDB)+ 快取(Redis)逐漸過渡至KV型資料庫(Taishan)+ 快取(Redis),以具備更強的可靠性。

  同時TaiShan作為公司自研的KV資料庫,在成本上也能更優於使用TiDB儲存。

  4、點贊服務層(thumbup-service)

  作為面對C端流量的直接介面,在提供服務的同時,需要思考在面對各種未知或者可預知的災難時,如何儘可能提供服務

  儲存(db、redis等)的容災設計(同城多活)

  在DB的設計上,點贊服務有兩地機房互為災備,正常情況下,機房1承載所有寫流量與部分讀流量,機房2承載部分讀流量。當DB發生故障時,透過db-proxy(sidercar)的切換可以將讀寫流量切換至備份機房繼續提供服務。  

  

  在快取(Redis)上,點贊服務也擁有兩套處於不同機房的叢集,並且透過非同步任務消費TiDB的binLog維護兩地快取的一致性。可以在需要時切換機房來保證服務的提供,而不會導致大量的冷資料回源資料庫。

  服務的容災與降級

  (以點贊數、點贊狀態、點贊列表為例),點贊作為一個使用者強互動的社群功能服務,對於災難發生時使用者體驗的保證是放在第一位的。所以針對重點介面,我們都會有兜底的資料作為返回。

  多層資料儲存互為災備

  點讚的熱資料在redis快取中存有一份。

  kv資料庫中存有全量的使用者資料,當快取不可用時,KV資料庫會扛起使用者的所有流量來提供服務。

  TIDB目前也儲存有全量的使用者資料,當快取、KV均不可用時,tidb會依託於限流,最大程度提供使用者資料的讀寫服務。

  因為存在多重儲存,所以一致性也是業務需要衡量的點。

  首先寫入到每一個儲存都是有錯誤重試機制的,且重要的環節,比如點贊記錄等是無限重試的。

  另外,在擁有重試機制的場景下,極少數的不同儲存的資料不一致在點讚的業務場景下是可以被接受的

  多地方機房互為災備

  點贊機房、快取、資料庫等都在不同機房有備份資料,可以在某一機房或者某地中介軟體發生故障時快速切換。

  點贊重點介面的降級

  點贊數、點贊、列表查詢、點贊狀態查詢等介面,在所有兜底、降級能力都已失效的前提下也不會直接返回錯誤給使用者,而是會以空值或者假特效的方式與使用者互動。後續等服務恢復時,再將故障期間的資料寫回儲存。

  5、非同步任務層(thumbup-job)

  非同步任務主要作為點贊資料寫入、重新整理快取、為下游其他服務傳送點贊、點贊數訊息等功能

  首先是最重要的使用者行為資料(點贊、點踩、取消等)的寫入。搭配對資料庫的限流元件以及消費速度監控,保證資料的寫入不超過資料庫的負荷的同時也不會出現資料堆積造成的C資料端查詢延遲問題。  

  快取重新整理:點贊狀態快取、點贊列表快取、點贊計數快取

  同步點贊訊息

  點贊事件非同步訊息、點贊計數非同步訊息

  針對 WriteBack方式的思考

  由於目前點贊系統非同步處理能力或者說速率是能夠滿足業務的。所以當前寫DB與寫快取都放在非同步流程中。

  後續隨著流量的增加,實施流程中寫快取,再由非同步Job寫入持久層相對來說是一個更好的方案。

  點贊job對binLog的容災設計

  由於點讚的儲存為TiDB,且資料量較大。在實際生產情況中,binLog會偶遇資料延遲甚至是斷流的問題。為了減少binLog資料延遲對服務資料的影響。服務做了以下改造。

  監控:

  首先在運維層面、程式碼層面都對binLog的實時性、是否斷流做了監控

  應對

  脫離binlog,由業務層(thumb-service)傳送重要的資料資訊(點贊數變更、點贊狀態事件)等。當發生資料延遲時,程式會自動同時消費由thumbup-service傳送的容災訊息,繼續向下遊傳送。

   三、未來規劃

  1、點贊服務單元化:要陸續往服務單元化的方向迭代、演進。

  2、點贊服務平臺化:在目前的業務接入基礎上增加迭代資料分庫儲存能力,做到服務、資料自定義隔離。

  3、點贊業務形態探索:以社群為基礎,繼續探索透過點贊衍生的業務形態。

來自 “ 嗶哩嗶哩技術 ”, 原文作者:蘆文超;原文連結:http://server.it168.com/a2023/0206/6788/000006788288.shtml,如有侵權,請聯絡管理員刪除。

相關文章