全民 K 歌推薦後臺架構
分享嘉賓:davidwwang 騰訊音樂 | 基礎開發組副組長
編輯整理:梁爾舒
出品平臺:DataFunTalk
導讀:首先介紹一下我們業務背景,騰訊音樂集團,於 2018 年是從騰訊拆分獨立上市,目前涵蓋了四大移動音樂產品,像包括 QQ 音樂,酷狗,酷我音樂以及全民 K 歌四大產品,現在總的月活用戶量已經超過了八億,其中全民 K 歌和其他三款 APP 有顯著的不同,我們是以唱爲核心,在唱歌的功能上不斷衍生出了一些音樂娛樂的功能以及玩法,當前的月活規模也在 1.5 個億以上,我們團隊在全民 K 歌 APP 中負責各個場景的推薦。
本篇文章將會給大家介紹一下全民 K 歌推薦系統在工程上的一些實踐,整篇分享分爲 3 個部分: 首先介紹下 K 歌推薦後臺架構;其次根據推薦的整體流程,從召回、排序、推薦去重三個方面分別介紹下 K 歌的相關實踐; 最後介紹下推薦相關的 Debug 系統。
01
K 歌推薦後臺架構
K 歌推薦的後臺架構整體可以分爲在線和離線兩個部分,離線的部分主要依賴兩個平臺:數據處理平臺和 VENUS 算法平臺。
-
數據處理平臺:這裏主要是依賴於騰訊內部的太極,還有 t 等一系列的用於大數據處理的平臺組件,使用這些組件來進行離線的數據處理、樣本處理、特徵處理,算法訓練等一系列算法相關的工作。
-
VENUS 算法平臺,主要是針對那些需要進行線上提供服務的算法,提供了一個從離線訓練到在線服務的一站式的管理工具,同時它實現了一個基於類似 tensorflow 的參數服務器的功能,可以支持大規模模型的在線訓練和線上預測的功能。
位於我們數據存儲層之上的是在線的部分,包括召回層、排序層和重排層,排序層根據業務複雜度的不同,又可以進一步分爲精排層和粗排層,在這三層之上是我們的中臺層,它包括了我們整個內部的 abtest 平臺,內容分發平臺等相應的一系列的平臺組件。除此之外還有我們的服務質量監控,以及我們的推薦質量監控,推薦 Debug 的一些輔助工具等一些支持系統。
接下來主要會對整個推薦的在線服務相關的模塊做一些介紹。
02
召回
首先,要介紹的是我們的召回部分,在整個 K 歌的實踐中,其實大概經歷了三個階段的演進,在業務的初期流量比較小的情況下,爲了支持業務的快速上線,我們主要採用的是一個基於 Redis 的 KV 倒排索引的方案,後期隨着整個業務的逐漸放量,還有我們整個的召回的路徑也越來越多,所以 V1 版的方案,它暴露出來了兩個問題,首先第一個就是說召回越來越多,對於頻繁修改,導致整個開發效率是比較低的,對於人力的成本投入也是比較高。第二個就是線上採用了對遠程分佈式 Redis 直接拉取的方案,那麼對於 Redis 存儲壓力是比較大的。爲了解決上面兩個問題,我們後來演化到了 V2 版本的方案,是一個基於雙 mongo 和本地 KV 緩存的方案,通過本地索引解決了性能問題,但它還會存在另外一個問題,因爲我們採用的是一個懶加載的本地 Cache 方案,所以說它會存在首次不命中的一個問題。爲了解決這個問題,我們在 V2 版的方案上要進一步迭代到 V3 版的方案。V3 版方案是基於雙 mongo 和本地雙 buffer 全緩存的一個方案。下面來對這三個方案來做一一的介紹。
1. 召回 V1: 基於 Redis 的 KV 倒排索引
這個方案它的問題是什麼?首先隨着業務複雜度的增加,整個召回的維度它是非常多樣的,而且我們不斷地做 abtest 的實驗,它整個召回的變化也是比較頻繁的,這樣人工構建的一個方案對於整個的開發工作量是非常大的,而且後續非常難以維護。然後第二個問題是在線是採用了直接拉取,並沒有增加一些緩存的一些邏輯在裏面,在召回這個階段,用戶的請求大概可以達到 1 比 100 以上的一個請求放大,對於整個 Redis 後端的存儲壓力以及存儲成本是相對比較高的。另外在線上的存儲採用了一個序列化的存儲協議,對於序列化的數據,我們在拉取回來之後,必然要涉及到一個反序列化,那麼頻繁的反序列化也會導致我們整個 CPU 的負載會偏高。那麼針對這三個問題,進行了第二版的方案優化。
2. 召回 V2: 雙 mongo + 本地 KV 索引
V2 版的方案中,在本地 Cache 裏其實採用了一個懶加載的方案,就會導致首次不命中的問題,不過在一般的情況下不會有太大的問題,但是在業務的高峯期它就可能有問題,在業務的高峯期可能會有零星的線上的報警,還可能會出現一些請求的毛刺。
3. 召回 V3: 雙 mongo+ 本地雙 buff 全緩存
03
排序
排序部分主要介紹三點:特徵平臺、特徵格式的選擇、特徵聚合與模型預測框架。
1. 特徵平臺
特徵平臺這邊主要解決的是特徵管理的問題,在最初的時候大家都是自己去構造特徵然後去上線,那會導致特徵散落在各個地方,不利於統一維護,特徵複用依賴於大家口口相傳,管理和維護成本是非常之高的。整個特徵平臺主要包含三個大的模塊:特徵註冊、特徵寫入、特徵拉取。在特徵註冊部分主要是提供了一個一站式的特徵管理界面,這樣就減少了前面所說的口口相傳的一個問題,另外就是將離線的數據和在線的存儲做了一個打通,減少了相應的特徵註冊成本,提高註冊的效率。在特徵寫入這個階段,我們是採用了一個組件化的開發方式,提供一個專用的免代碼開發的寫入組件,只需要相關的同學完成配置之後,就可以將相應的數據自動導入到線上的存儲裏,同時導入時支持了相當於限流的一個工具,可以支持的流量的按需控制。同時還開發了一些配套的通用驗證工具和一些成功率相關的監控。最後對於在線服務的特徵拉取這一塊,首先當然是要存儲結耦,於是提供了一個通用化的存儲協議來進行存儲。另外在特徵聚合框架內提供一個可配置的緩存支持,可以按需來進行選擇。另外就是特徵格式協議選擇的優化,進一步提升了我們整個特徵平臺線上特徵拉取過程的性能。
2. 特徵格式的選擇
提到了特徵協議,就來到了我們的第二部分,特徵格式的選擇。爲什麼單要拎出一頁 PPT 來單獨介紹特徵格式?原因就是說如果特徵格式的選擇不合適的話,它對整個線上的性能影響是非常之大的。其實最早我們選取的特徵格式,其實是谷歌的 Tfrecord 的格式,也就是 Tensorflow 支持的格式,這個格式簡單來說,就是通過一個 Map 和多層的 Vector 嵌套來實現了一個通用的幀格式。但是我們在線上在實際使用的過程中,發現線上非常消耗 CPU,假設我們 CPU 高負載到 90% 了,90% 裏面的 80%,它其實都是消耗在 Tfrecord 的這種格式的打解包以及打解包過程中相應的內存分配上,OK,大家現在可以知道,基本上這個點就是我們的業務一個瓶頸了。
通過我們對於業界的一些相關的平臺的調研和我們內部的壓測之後,我們選擇了右邊的特徵格式,它的主要改進點有兩個,第一個就是取消了 Map,因爲在我們的壓測中會發現它整個 Map 的打解包和序列化的性能都是非常之差。另外一個就是說我們去掉了 string,首先去掉 string 的原因,是因爲我們看到模型訓練的特徵是可以沒有 string 的。第二個原因其實是還可以減少網絡流量的浪費,對於同樣一個特徵,切換掉 string 後,新的特徵格式與老的特徵格式進行對比的話,在線上的存儲佔用基本上可以減少 1/2 左右,上面表格中新的特徵格式與老的特徵對比可以發現,我們在打解包的性能上,我們基本上可以拿到一個十倍的一個收益,在內存的分配大小上,我們基本上可以拿到五倍左右的一個收益,當然了,我們上線後的 QPS 表現上也基本上能拿到五倍左右的一個最終收益。
3. 特徵聚合與預測框架
關於模型預測這一塊兒,主要介紹兩個地方,首先就是說在真正我們喂到模型去進行打分預測的時候,我們用戶的特徵跟物料的特徵其實是做了一個分開去投傳的,爲什麼要分開?之前我們也是說把物料特徵和用戶才能全部拼在一起,拼成物料的特徵。其實分開打分主要是要解決線上的特徵拉取流量過大的問題,因爲在整個推薦這個場景,打分的時候,我們的特徵是非常多的,對於一個用戶的打分,基本上你拉取的整個特徵量可能是按 M 來計的,這對於整個線上的網絡 IO 的流量會比較大,我們在之前就達到了整個線上網絡 IO 流量的瓶頸,可能是單機都能達到 1G 以上,或者是接近對於實際的網卡,我們可能達到 8G 左右,相當大了。通過這樣的一個改進,就可以至少降低 1/3 的網絡 IO 的流量,對整個網絡帶寬的成本消耗是非常之明顯的。
第二個要介紹的是特徵一致性的保障。其實分爲兩個方面,一個是特徵值的一致性,另外一個是特徵處理的一致性。特徵值的一致性通過將在線的特徵做一個離線的上報,這樣的話在線和離線所用的特徵都是同一份數據源,那就消除了特徵穿越導致的不一致。關於特徵處理的一致性的解決方案,是說在前面介紹的 VENUS 的一個數據處理平臺上,提供了一個統一的特徵處理的插件,離線和在線都是通過同一個插件來做特徵的處理,這樣的話就可以規避線上線下特徵處理不一致的問題。通過以上兩個方案,我們完成了整個的特徵一致性的一個保障。
04
推薦去重
1. 方案選型
第三部分介紹的是在萬級別去重過濾的場景的實踐。去重過濾在業界有兩種主要方案,一個是明文列表的方案,一個是基於布隆過濾器的方案。那麼像 K 歌之前在千級別以下的去重過濾,採用的是明文列表方案,因爲它比較簡單,一般來說都是我們第一個想到的一個方案,
明文列表優勢:
-
簡單易實現
-
明文存儲,比較好跟蹤調試。方便限制長度和更新淘汰。
劣勢:
- 存儲空間佔用大,假設我們一個 itemId 30 字節,存 5000 個,存儲佔用可能就要到達到 150K。150K 對於線上的存儲來說,基本上就屬於大 Key 了,在線上會產生拉取性能問題,還有 QPS 的上限的問題。另外隨着它整個的數據量的增大,因爲會在本地將數據轉成一個類似 map 的結構,查詢效率隨着整個數目的增加,也會有一個顯著的下降,這也是它存在的一個問題。
布隆過濾器它相對的明文列表的方案來說優勢:
-
空間佔用小,因爲它是基於位數組的一個方案,是一個通過 hash 位來判斷它是否命中的方式。
-
另外是查詢效率高,對數目的增加並不敏感
劣勢:
-
存在一定的誤判率
-
原生的布隆過濾器,它不支持限制長度,也不支持更新淘汰。另外就是說在我們的業務場景中
-
用戶的曝光數目其實我們是不確定的,我們不知道用戶可以看多少個,但是布隆過濾器在初始化時需要指定它能支持的最大的 size。這兩方面是存在一定的衝突。
通過前面介紹的方案的對比,我們可以知道萬級別這種場景下,因爲 key 值太大了,所以使用明文列表的方案不太合適。布隆過濾這種方法也有它的問題,我們通過對原生的布隆過濾器做一些基本的改造,來完成我們線上的業務的一個訴求。
2. K 歌實現方案
另外就是說像這種消息隊列的上報,其實它是有一定的延遲的,雖然也很快,一般認爲大約是兩毫秒左右的一個延遲。如果延遲不管的話,其實對於用戶頻繁下拉的時候,它就可能會出現一個重複的問題。我如何解決?這一個重複的解決方案是這樣的,我們通過配合實時查詢後臺記錄的下發歷史的短列表來保證用戶的體驗上不刷到重複的。
整個線上業務實踐的數據結果,大概就像圖片裏表格內容所示,相比於明文列表的方案,採用布隆過濾器,我們在整個存儲的佔用上,拿到了五倍以上的收益,那麼在整個單 ID 的判斷效率上,我們也可以看到,基本上可以拿到十倍左右的一個收益。我們在拉取時延上,因爲整個它的存儲 key 的 value 變小了,所以說也拿到了接近 7 倍的一個收益。這個是我們整個在推薦系統部分的業務實踐。
05
Debug
1. Debug:調試
2. Debug:監控
3. Debug:日誌追溯
這樣的問題我們有幾個解決方案,當然並不是能徹底解決它,更多隻是逐漸優化它。對於大部分的關聯信息,我們是採取了一個單獨存儲的方式,通過在查詢的時候做關聯,來避免將大流量的數據是直接寫入到 ES 裏面。另外的話就是對於流量很大的個別場景,支持採樣,當然採樣有可能會帶來一個問題是它可能沒有辦法復原現場用戶反饋了,你也不知道它現場是什麼。我們有一個白名單平臺,它產品或運營,或者說我們自己的同學可以直接就在配置平臺配置上白名單,然後這樣的話它就可以把用戶的整個路徑上需要展示的數據給存到存儲裏面,然後便可以在前端能看到一個大概秒級延遲的展示,另外目前我們是配置的 ES 的最短更新週期大概是 30 秒。
今天的分享就到這裏,謝謝大家。
嘉賓介紹:
davidwwang
騰訊音樂 | 基礎開發組副組長
負責全民 K 歌基礎平臺和工具的研發, 和 K 歌國際版相關的推薦工作。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Xe4ZyUqLY3WJU0na4On6Hw