Dcard Backend 日記:優化一支 API 的過程

Eddie Wen
Dcard Tech Blog
Published in
5 min readApr 19, 2022

除了 Dcard 產品本身外,這幾年我們也有在發展自己的廣告投放平台,從零開始搭建一個廣告系統的過程中遇到過大大小小的問題,這篇文章會分享我們優化一支以 Redis 作為資料庫的 API 的經驗。

當使用者在系統後台新增廣告時,可以設定這檔廣告一天最多對同個人曝光幾次。這個設定是為了避免過多的重複曝光讓使用者感到疲乏,變成無效的投放。

對應這個需求,我們後端系統中有個負責計算每檔廣告每人每天曝光次數的服務。當 ML 團隊需要計算廣告適合程度時,就會需要有支 API 讓他們可以呼叫,拿到當下該使用者今日對於該檔廣告的曝光次數,如果超過廣告主設定的上限時,就應該排除該廣告。

API 的設計是可以傳入多個廣告 id 以及一位使用者,並回傳該名使用者對於這幾檔廣告當下的曝光狀況。

Photo by https://unsplash.com/@danmall

Performance Issue

某天 ML 團隊反應這支 API 有逐漸變慢的趨勢,想請我們研究看看有沒有優化的空間。

如前面所提到的,這個服務是在計算每位使用者的廣告瀏覽次數,所以基本上會跟目前的廣告數以及活躍使用者成正比。

首先因為後端的其他服務也有用到同一支 API,但使用情況並沒有像 ML 團隊反應的這麼慢,而其中第一個想到的差異就是,後端服務彼此間是使用 gRPC 做溝通,ML 團隊則是使用 JSON-encoded HTTP,而的確在我們服務的 profiling 中也觀察到 JSON encode/decode 佔了一大部分,所以就先請他們嘗試改用 gRPC。

其實在這一步請他們改成用 gRPC 的效果就已經不錯了,但後端這還是繼續研究是不是還有可以改善的地方。

Multiple Get

該服務是用 Redis 作為資料庫來存取紀錄,API 實作也只是用 for loop 去做 GET 來拿完所有的資料,顯然實際的使用情境超出了之前的預期,當一次請求包含著大量的廣告 id 時,這時就會花費過多的時間在跟 Redis server 來回溝通,所以我們打算先換成 MGET 來減少 request 次數。迅速發了一支 PR 在 staging 沒問題後便上線了,不過在上線後發現 server 狂噴 CROSSSLOT Keys in request don't hash to the same slot 錯誤。

這個錯誤是說我們不能在 cluster mode 使用 MGET 去存取多個 key(因為可能會分散在不同的 node 上)。之前在開這台 Redis 時為了避免將來需要切換 cluster mode 的 downtime,所以一開始我們便使用 cluster mode。沒做好的一點是沒有將測試環境也同樣設定成一樣的 cluster mode,這使得我們未能及早在測試中發現問題,所以後續的處理就是將測試環境也改為 cluster mode,果然測試一跑就出現一樣的問題了。

至於這個 cross slot 的問題,Redis 其實也有提供解法,可以自己實作 Hash tags 來強制這批資料會被分配到同個 slot 上。不過我們實際上還有一些其他資料需要取用,要使用這個方法還會有其他問題存在,目前就先不繼續討論這個解法。

Pipelining

既然原本的主要目標是減少 RTT,那除了 MGET 的方案外,Redis 也有提供 pipelining 可以使用。其運作方式是將多個指令一起送上伺服器端,執行完後再將運算結果一同送回客戶端,如此一來,便可以不用等待指令與指令之間的網路傳輸時間,原本 n 次 RTT 時間則可能可以省到剩下一次。

實作的改動也很簡單,就是將數個 GET 指令放置至同個 pipeline 中送出執行。下圖則是改為 pipeline 後的數據變化

上圖為該服務的 response time 監控數據,可以看到線圖直接掉到 10ms 以下,效果十分顯著。

這次優化的過程我們都只專注在 application 部分,當然過程中也有討論到可以從資料結構的設計下手,不過那就會牽扯到比較多商業邏輯,需要更嚴謹的規劃,而最後這些改動也的確有效地解決了我們當下的問題。這次的改動從 Redis 的角度來看並沒有什麼差別,所需要處理的指令基本上是一樣的,接下去可能會面臨的問題就是在 Redis 本身負荷的壓力,之後的優化可能會從資料結構的設計或是 Redis cluster 著手。

因為每一次的投放都跟收益相關,除了本篇文章的案例,廣告團隊還會遇到許多諸如此類高度的 performace 要求。使用者的感受是團隊最重視的,產品設計要非常重視使用者體驗,才能找到跟廣告收益的平衡點。如果你對於這類問題有任何想法,或是有興趣與我們一起設計廣告投放系統,我們目前正在尋找 Backend Developer — Advertising 的夥伴,歡迎來和我們聊聊!

--

--