B 站數據服務中臺建設實踐

本期作者

孟帥帥

數據平臺部數據服務負責人

劉晨陽

數據平臺部資深開發工程師

呂小正

數據平臺部高級開發工程師

01 背景

隨着公司業務的發展,對於數據的需求會越來越多。怎麼在業務系統中高效的使用數據,讓業務系統處理大數據時化繁爲簡,數據服務化基本是必經之路。那麼什麼是數據服務化,簡單理解就是數據 SaaS,通過一些數據庫語言把數據轉化成服務,如 API、RPC、數據文件 等不同的數據方式提供給業務系統使用。

經過多年對各個業務系統對接調研發現,大家感受到了無標準、不統一及煙囪式建設的服務接口的痛點,希望可以建設一套標準的,中臺化的數據服務平臺。數據服務中臺以解決業務中痛點爲優先,主要的痛點如下:

  1. 數據接入方式多樣,對接效率較低

  2. 數據出口多,口徑不統一

  3. 數據及接口無法共享,建設成本高

  4. 數據鏈路不清晰,故障影響難以評估

  5. 服務質量標準不統一,質量問題較多

針對以上痛點,我們需要思考一個問題:我們到底需要一個什麼的數據服務平臺,平臺應該有哪些特性?從功能上看,我們認爲一個完整的數據服務平臺應包括以下功能:

  1. 接口的規範化定義

  2. 通用的數據服務網關

  3. 數據鏈路可管理可維護

  4. 服務可以觀測可運維

  5. 服務可複用

除了滿足以上功能外,我們也希望這個數據服務平臺具備以下特性:

  1. 靈活性:數據的迭代及變更,可以讓下游無感

  2. 便捷性:能夠快速的完成數據的服務化,接入效率高

  3. 低成本:讓數據可以複用而非複製,減少數據的冗餘生產

02 架構設計

圖片

數據服務中臺的經過多次迭代,如上圖所示。中臺構建於數據倉庫之上,自下向上包括:數據構建層、數據查詢層、服務接口、服務網關及服務的應用體系,除此之外,中臺還包括數據標準的管理、數據安全管理、運維監控等模塊。今天主要介紹中臺的核心服務化鏈路,即:數據構建 --> 數據查詢 --> 服務接口與網關。

2.1 數據構建

我們知道數倉中的數據一般是 hive 表,維度建模思想下,單表並不能完整表達業務邏輯,需要對數據表進行重新構建,構建統一的面向業務的數據模型層,同時數倉中物理表對於線上業務系統的使用有很高的延遲,也不可以直接讀取,所以在數倉的基礎上,抽象出數據構建層。

數據服務中臺中,我們把用戶分爲兩大類角色即:數據生產者及數據消費者。數據構建層是面向數據生產者 (一般指數倉開發) 進行數據定義的層次,核心能力包括:模型定義、模型加速及 API 的構建。

2.1.1 模型定義

B 站數倉建設以業務過程爲驅動,採用經典的維度建模的方式。維度建模以分析決策的需求出發構建模型,構建的數據模型爲分析需求服務,因此它重點解決用戶如何更快速完成分析需求,同時還有較好的大規模複雜查詢的響應性能。

爲了適應各種數據分析場景的數據獲取需求,我們支持構建多種邏輯數據模型,根據事實表與維表的關係,經常將數據模型分爲單例模型、星型模型、雪花模型及星座模型。

圖片

單例模型:1 張事實表,一般爲 dws 或者 ads 層的彙總實時表;

星形模型:1 張事實表 + N 張維度表,如播放相關事實表可以通過稿件 ID 關聯稿件維表獲取稿件相關屬性信息;

雪花模型:1 張事實表 + N 張維度表 + M 張非直接關聯事實表的維度表,如播放事實表通過稿件 ID 關聯稿件維度表,又通過稿件維度表中的品類 ID 關聯品類維度表;

星座模型:N 張事實表 + M 張與事實表關聯的維度表,如播放模型與 DAU 模型通過公共維度關聯,獲取公共維度下的 DAU 指標與播放指標。

2.1.2 模型加速

我們知道,數倉的模型一般存在 hive 中,hive 表對於線上 API 的使用有極大的困難,所以就需要把 2.1.1 中構建的數據模型進行模型加速。數據服務作爲一個通用的數據獲取平臺,既要滿足不同的應用場景,同時也要考慮成本因素。我們從性能及靈活度兩個方便,對使用場景了做了劃分。不同的使用場景,其加速方式及目標引擎有所不同。我們從性能及靈活性上對使用場景進行劃分:

圖片

明細加速: 實現把邏輯模型中的數據從冷引擎到熱引擎的鏡像複製,極大保留了模型的原始數據的業務特性,可以更好的支持對模型中不同的維度及指標做多維分析或者範圍查詢,在熱引擎中,加速數據的查詢效率。

預計算加速: 根據數據獲取需求,從邏輯模型中的明細數據進行預計算及處理,直接聚合到所需粒度的數據,並將數據寫入熱引擎中。由於數據已經預計算處理,在數據查詢時極大減少了引擎的計算,查詢效率更高,但也損失瞭如多維分析及維度下鑽查詢的靈活性。

不同的場景有不同的加速方式及引擎選擇的組合,針對四種類型的場景,推薦的組合如下:

在線場景: 預計算 + kv 存儲,一般應用於 C 端及 B 端的數據需求,有着極高的服務性能要求,且線上數據不會快速的變化,靈活度要求低。

準在線場景: 預計算 + tidb/mysql,一般應用於活動或者運營系統後臺,服務性能要求不會同線上場景那麼高,但同時要求支持範圍查詢,有一定的查詢靈活度;對於數據量較大的查詢場景,可以選擇分佈式數據庫 Tidb,其他可以選擇 Mysql。

OLAP 場景: 明細 + ClickHouse/Iceberg,一般應用於數據分析系統,需要對數據進行多維分析,所以需要明細加速。ClickHouse 作爲一款 OLAP 分析引擎,比較適合作爲目標引擎,如果考慮到成本因素,也可以使用湖倉一體技術 Iceberg,數據無需出倉既可在倉內完成數據的加速,性能雖不及 ClickHouse,但也可以達到秒級的響應,特別是在使用成本及查詢語法通用性上更有優勢。

離線場景: 離線數據獲取,一般訪問原始 hive 數據源即可。

2.1.3 API 構建

API 參數定義

爲了對 API 標準化,我們調研了公司內的各業務的 API 的使用要求,統一 API 標準元素,包括: APIID、API 名稱、請求方式、請求路徑、返回參數、請求參數、響應時間要求、QPS 預估、應用場景等。

圖片

API 取數邏輯構建

API 的取數邏輯的配置,中臺支持多種配置方法,包括:自定義 sql 構建、模型構建及指標維度構建。下面針對這三種構建方法分別介紹:

自定義 sql 構建:

爲了支持複雜邏輯的數據獲取,系統通過過引入 mybatis 相關的包,能夠支持類似 mybatis 動態 sql 方式,通過支持 Mybatis 標籤的 SQL 語法來編寫查詢邏輯。目前支持的標籤類型包括:if、foreach 和 where。根據用戶傳入的變量動態構建 SQL 實例,從而擴展了模型 API 化的能力。

select
  a.field1 AS alias_1,
  a.field2 as alias_2,
  a.field3 as alias_3,
  b.field1 as alias_4
from
  fact_table a
left outer join
  table_dim b
on a.id = b.id
<where>
    a.field = ${ input_1,type = number }
       <if test = 'input_2 !=null' >
    and b.field = $ { input_2,type = number }
    </if >
</where>

模型構建:

模型構建是一種可視化的配置方式,用戶不用要 sql 的代碼編輯能力,搜索想要服務化的模型,即可在模型上配置出 API 的取數邏輯;

圖片

指標維度構建:

指標維度構建是在模型構建的基礎上的一種更高級模式,用戶無需實現指定模型進行構建,可以先配置請求參數及返回參數,系統根據管理的模型元數據信息,按照一定的規則自動篩選出可以服務化的模型列表並給出推薦優先級。

圖片

2.2 數據查詢

數據查詢層是服務接口層與數據模型層的中間層,主要功能是通過接口獲取請求參數,經過解析、調度、翻譯及計算後返回數據結果。數據查詢層分爲兩種查詢方式:原子計算及複合計算。原子計算處理返回格式相對單一、指標維度統一及引擎單次處理即可用的數據查詢,一般用於線上的單點查詢、範圍查詢場景等;複合計算是在原子的計算的基礎上,按照一定的算法對原子計算的結果進行二次加工,一般包括數據編排、指標間結果或維度間結果的處理等,返回的數據結構一般可以直接應用於數據產品中。

2.2.1 原子計算

原子計算需要把獲取到的接口參數進行解析、拆分、翻譯及多次引擎的單次查詢獲取結果並返回,整體流程如下圖所示。流程整體分爲三部分:調度、翻譯、引擎查詢。

圖片

調度:

調度是一次原子計算的入口及出口,負責任務的解析、拆分及結果的處理。調度獲取到的參數是一段 DSL,包括指標、維度、篩選條件、分頁信息等。

步驟 1,解析的過程是把 DSL 信息與 API 配置進行匹配,並得出本次數據請求需要的模型。在一個 API 中,用戶可以配置多個數據模型,不同的指標維度查詢中,需要用的模型就會不同,解析過程需要根據查詢條件按照預設計好的規則進行排序擇優,追求最好的查詢準確性及效率。排序擇優的算法參考 Oracle 查詢優化器的做法,結合 CBO 及 RBO 算法選擇最優的查詢模型。

步驟 2,任務拆分是把解析的結果拆成子任務查詢,解決異構數據源的結果獲取問題。因爲一次查詢中可能會從多個模型中獲取,模型也可以在多個引擎或集羣中。爲保證結果的的準確獲取及查詢效率,我們採用了拆分子任務的方式進行併發數據查詢,子任務的查詢結果經過二次加工處理返回。

步驟 3,結果處理器是一個內存計算器,負責把子任務的查詢結果進行拼接、排序、分頁及格式轉換等,返回統一格式的數據結果,讓下游無感知數據的處理過程。

翻譯:

步驟 3-1,翻譯模塊是把子任務的查詢信息翻譯成對應引擎可以識別的 sql 語法。AST 是 abstract syntax tree 的縮寫,也就是抽象語法樹。翻譯模塊區別於引擎對執行 SQL 進行語法樹解析的過程,而是逆向從構建 AST 開始,並最終翻譯成 SQL 的過程。構建 AST 的算法如下圖所示:

圖片

第一層 AST 從下面的 "SELECT" 開始,第一層語法樹是構建出本次查詢需要用的有效數據,包括構建模型關係、模型字段的篩選及數據範圍的篩選

第二層 AST 從上面的 "SELECT" 開始,第二層語法樹是基於第一層構建的有效數據,進行運算表達式計算,如 sum,count(distnct ..),sum(case when then end),sum()/count() 等其他一些自定義語法表達式。

通過兩次的 AST 構建,可以完整表達一次原子計算所需的數據獲取邏輯。根據構建的 AST,系統會各個引擎的查詢特性及方言,優化查詢語法,提升查詢性能,最終轉化成可被引擎識別的查詢 SQL。

引擎適配器:

步驟 3-2,引擎層負責執行各個子任務的查詢 SQL,系統適配了目前公司內部多種數據引擎,包括自研 KV、TiDB、Mysql、ClickHouse、Iceberg 等。除接入不同的引擎連接協議,在不同引擎的連接方式上也做了優化,如:mysql、tidb 由單次短連接改爲連接池,減少連接的頻繁創建及銷燬帶來的開銷問題;自研 KV 多可用區連接,擴大在線服務可用的服務範圍;OLAP 引擎如 ClickHouse、Iceberg 的超時自取消功能,減少服務佔用及降低引擎壓力等。

2.2.2 複合計算

二次計算是解決單一 sql 或者單一引擎不能直接獲取數據結果的數據二次加工過程。基於原子計算得出的一個 m*n 的二維數組,可以計算指標在時間軸上的變化趨勢,如:同比、環比、增長率等,也可以算指標在不同維度間的關係,如:佔比、漏斗、下鑽等,同時也可以支持一些統計分析,如:協方差、TGI、置信度等。二次計算不僅支持通用的算法口徑,也可以支持自定義函數,二次計算的能力避免了數據輸出後在外部不受管控的加工,讓數據所得及所用,處理流程如下圖所示:

圖片

2.3 服務網關及接口

數據服務層是對模型中的數據進行服務化的過程,主要包含三個過程:服務網關、服務接口及數據查詢。

圖片

2.3.1 服務網關

API Gateway(API GW / API 網關),系統在系統邊界上提供給外部訪問內部接口服務的統一入口。在數據服務中,網關需要核心具備的功能包括:鑑權、限流及監控。網關使得業務對接數據服務時有了統一的標準,使得 API 有了複用的可能。

鑑權: 數據服務中,數據安全非常重要。數據服務中,爲每個應用分配一對 appKey 及 secret;對於已經發布上線的 API,負責人可以對應用進行授權,只有 API 被授權給某個應用,應用纔可以通過 appKey 及 secret 訪問該 API。

限流: 限流是對系統的保護及減少 API 相互影響的重要手段。應用在申請 API 調用時,需要給出合理的 QPS 預估,API 會根據總 QPS 需求調整資源部署;過大的 QPS 的請求且沒有限制時,容易超出服務負載而發生故障,對於複用的 API 也可能產生相互影響。所以每個應用申請的 API 都有 QPS 限制,超出閾值,多餘流量會廢棄。

監控: 服務監控可以比較直觀的觀察的 api 的運行狀態,監控類型包括訪問總請求數、訪問成功率、訪問失敗數及訪問失敗類型分佈,另外也補充了對 API 的安全監控及 QPS 監控等。

2.3.2 服務接口

根據數據場景及數據內容,接口形式分爲同步查詢與異步查詢,接口類型包括 DSL 接口、模板查詢接口、自定義 SQL 接口等。

同步查詢: 對於線上應用查詢,要求響應速度快且數據結果較小的查詢,建議同步查詢,可以快速獲取到結果。

異步查詢: 對於離線分析或者大數據量下載場景,響應速度要求不高,但是返回的數據量比較大,建議走異步查詢,異步查詢的流程如下圖所示:

圖片

DSL 接口: 用 DSL (Domain SpecificLanguage ,領域專用語言)來描述取數需求,計算層可以解析 DSL 並計算,通過 DSL 所有的簡單查詢服務減少到只有一個接口。

message OpenApiReq {
    OsHeader osHeader = 1;
    repeated OperatorVo filters = 2;
    repeated string metrics = 3;
    repeated string dims = 4;
    repeated string orderFields = 5;
    PageVo pageVo = 6;
    repeated OperatorVo metricFilters = 7;
}

模板查詢接口: 數倉同學按照業務需求,把數據的計算邏輯固定爲一個模板,下游在調用時不必關心表在哪裏及怎麼計算,只需根據模板中設置的變量傳參獲取數據,減少了不必要的溝通,提高了對接效率。

message SqlQueryReq {
    OsHeader osHeader = 1;
    repeated OperatorVo filters = 2;
}

SQL 接口: 業務方可以通過寫 SQL 的查詢數據 ,由服務提供者自己來維護 SQL ,走向 DevOps。

message AsyncSqlQueryReq{
    string appKey = 1;
    string secret = 2;
    string engine = 3;
    string sql = 4;
}

03 通用解決方案

3.1 口徑統一及溯源

如前文提到,口徑不統一及鏈路不清晰造成較高的維護成本,總體解決思路是統一入口及統一出口,並在過程中進行全鏈路管理,方案如下圖所示:

圖片

統一定義: 口徑不易維護的原因之一是管理不統一,散落在各個地方,導致大家沒有統一的視角管理及查看口徑。我們的的解決方案是口徑統一在指標平臺管理,其中把指標的定義及模型的定義解耦。指標定義是對分析對象的業務過程進行描述,計算方法的定義約束,可在數據模型建設之前確定,一般有數據產品角色實施;模型定義是根據數據需求及指標定義進行構建生產,模型中指定了模型字段與指標的關係,決定了模型中哪些字段可以生產哪些指標,一般有數倉開發角色實施。通過把指標定義與模型定義解耦,減少了不同角色間的溝通成本,讓口徑的定義可以延續到數據生產中。

統一出口: 口徑不易維護的另外的一個原因是定義與生產分離,出口不可控。我們打通了指標的定義及生產流程:模型的出倉可以自動化完成,出倉過程中的計算邏輯是基於指標平臺中的定義自動生成的,減少了人工的干預,避免定義與生產的不一致;其次,API 的取數邏輯非人工定義,也是基於指標定義,自動翻譯取數邏輯及路由計算引擎,避免生產與消費過程中的不一致。通過定義與生產的打通,生產與消費的打通,數據流通過程中完全基於統一的定義,達到了最終的定義與消費的一致性。

全鏈路監控: 數據從定義到生產到消費環節,有完整的監控鏈路,以此來確保口徑的持續一致性。指標一致性監控:維度建模的數倉中,同一個指標由於分析思路或者需求場景,最終生產此指標的模型可能是多個,那麼就需要保障指標在不同模型間的口徑一致性。通過一致性對比或者閉環公式校驗等手段,發現指標口徑不一致,然後通過口徑變更、模型換綁等治理方法來持續保障指標口徑的一致;出倉一致性監控:出倉過程中數據集成工具保障了實時的一致性,這裏需要保障的是,由於歷史數據變更導致的出倉前後數據不一致的問題;服務質量監控:從數據出口監控數據的質量情況,主要包括閾值監控,波動率監控等,發現質量問題,保障出口的最終口徑一致性。

3.2 降本增效

中臺的建設最終是要爲企業降本增效的。過往中,不同業務線重複建設,垂直產品線煙囪式的開發,使得數據成爲一個個孤島,雖能滿足單一業務場景,但是卻增加了各個業務線的合作成本。

我們的建設思路是,首先將公共、通用的部分抽離出來,形成可複用服務,讓業務可以快速完成數據鏈路的搭建,減少業務重複造輪子,降低研發成本;其次,統一服務標準,通過快速複用已建設的數據鏈路搭建的能力,讓數據從定義 --> 生產 --> 消費的整個週期縮短,提升對接效率,爲數據在不同部門間流轉創造可能。

圖片

降本:

提效:

3.3 服務高可用

圖片

服務隔離: 隔離是將服務或資源分割開。服務隔離是爲了在服務發生故障時,能減少傳播範圍和影響範圍,故障發生後不會發生滾雪球效應,從而保證只有出問題的服務不可用,我們根據服務的保障等級,劃分爲 5 個服務資源組,不同資源組相互獨立,不會產生相互影響;存儲資源隔離是通過資源隔離來減少資源競爭,保障服務間的相互不影響和可用性,我們將資源隔離做到 API 級別,不同 API 間及不同等級間做隔離,充分保障資源間的相互不影響。

異地雙活: 異地雙活的目的也就是容災,當某個地方服務出現了災難性故障,而服務仍然能正常提供服務。我們根據業務的部署情況,選擇距離業務較近的 A、B 兩地機房部署服務,正常情況下同機房的請求鏈路爲主鏈路,當某地服務宕機,則會自動降級到另外一地的服務。

緩存: 緩存可以讓數據更接近使用者,目的是讓訪問的速率更快。對於數據時效性要求低但是請求熱度高的接口可以進行緩存,命中緩存後,請求不會到達引擎執行層,可以有以下優勢:1. 請求鏈路減少 2. 系統響應時間更快 3. 降低了服務後端及引擎的負載。但是,緩存也會帶來問題,如:1. 需要保障緩存一致問題 2. 緩存維護成本。數據服務平臺中,緩存有兩層:本地緩存及分佈式緩存。

圖片

本地緩存:

分佈式緩存:

緩存版本管理:

04 落地成果

平臺從零到一建設一年左右,已經逐步承接公司 C 端及 B 端的數據需求。在體量上,平臺上的 API 數量已達 500 以上,日常 QPS 達數十萬,經歷 B 站多種大型賽事及活動的洗禮,支持數據平臺內各種數據服務需求,包括在線應用、數據產品、BI 工具產品及報表等。除了以上業務成果外,數據服務平臺通過沉底技術、制定標準、優化流程等,目前平臺的新流程在效率上有極大提升,創建 API 從近 5 天降低到 1 天內。在成本方面,平臺也通過多項綜合措施,包括模型複用、API 複用、應用治理,API 的生產成本可以降低 18% 左右。數據服務中臺化加快了研發週期、節省了研發成本,讓有限的資源快速高效迭代出新的業務。

05 未來規劃

穩定性

服務穩定性在任何時候都是生命線,當前的服務框架中實現細節仍有優化空間,魯棒性有待加強。未來需要針對服務框架中的實現問題,特別是在隔離級別,資源分配,單點故障等進行優化及迭代,讓服務可以輕鬆扛過災難級故障,未來也需要更多的故障演練來持續維護服務的穩定性。

智能化

當前服務由於從源頭管控了數據標準,使得數據在提供服務時需要較多的元數據註冊及數據構建工作,從中長期的角度看,這些註冊的元數據雖可以更好的維護數據標準,減少重複建設,但也提高了數據服務化的難易程度,未來需要打通更多的系統,減少不必要的信息錄入,引入自動化檢測手段,讓服務化更加智能。

服務治理

隨着數據服務越來越多的被廣泛使用,服務的治理越來越凸顯重要。我們發現,API 市場中存在着 api 無熱度、api 資源使用率低、api 訪問成功率低等問題,針對這些問題需要建立長效的治理機制,長期保障 API 的穩定、可靠與低成本。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/DZEHaWNZS8HTYi-ipGYrAQ