嗨嗨大家,歡迎閱讀第 14 期的 CodeFarmer 技術週報!
今天依舊是個下班後趕稿的日子,以下就來繼續把上兩期中購票系統的系統設計面試中,最後的 deep dives 部份給講完吧,如果還沒看過前面幾篇的話,可以參考以下連結:
Step 5. 深入討論 (Deep dives)
5-1. 前情提要
過了一週,怕大家已經忘記前面在講什麼,再次用一張圖來前情提要一下。以上是我們在前面 4 個步驟中討論完並提出的高階設計,接著將針對可以優化的細節來嘗試深入討論。
5-2. Deep dives 從何下手?
影片中提到可以把握 deep dives 階段去提出 1~3 個點,來展現在系統設計面試中提供給面試官評比是 mid-level、senior、staff 的訊號。
至於要從哪些面向來下手呢?就可以從最前面釐清需求時的非功能性需求 (non-functional requirements) 來發想,前面在第一篇時有列出這些:
低延遲的搜尋
針對購票功能,要能確保同一張票沒有重複訂購問題,因此會更注重高一致性
針對搜尋與活動列表,可以更注重高可用性
對搶票系統而言,讀寫比例可能會是讀的比例遠高於寫,假設實際購票的轉換率是 1%,那讀寫比例就會是 100:1
系統要有能力在熱門搶票活動上提升擴展性來應付短期高流量問題
因此就先從「低延遲搜尋」這個部份來深入討論。
5-3. 如何改善搜尋的效能
如果今天要實作一個基本的搜尋活動名稱的功能,最基本的 SQL 可以長類似這樣來實現 (其中的這個 {term} 代表使用者輸入的關鍵字):
SELECT * FROM Events WHERE name LIKE '%{term}%'
但這會有個問題是當今天表中的資料量龐大時,去檢查每一列的活動名稱中是否含有某個關鍵字是相當沒效率的做法,如果平常有在用 JavaScript 刷題時可能可以理解從一個陣列資料中去直接下 includes 的時間複雜度肯定不是最好,要優化效率的話常會採取「用空間換取時間」的方式去嘗試將陣列的資料結構轉成 hash map 的方式。
而針對這裡的搜尋功能就可以用上 ElasticSearch (以下簡稱 ES) 這個元件來改善,關於 ES 的原理就是將資料存下來的同時,也對每一筆資料去建立該資料的反向索引像是這樣:
周杰倫:Event1, 3, 5…
演唱會:Event 1, 2, 3, 4, 5…
如此一來當下次有使用者搜尋「周杰倫」時就可以直接去從這些索引中找是否有匹配的就可以增加效率。另外關於 ES 的優點還可以做到地理位置、時間區間等購票活動網站常見的篩選選項來搜尋。
但在使用 ES 時面試官可能會針對這個做法追問:「那要如何做到 ES 與主資料庫之間資料同步呢?」
5-4. 如何同步 ES 與主資料庫的資料
最直覺的做法就是每次在做活動的 CRUD 服務時,也同步到 ES 去更新資料,但這種作法會讓寫入邏輯變得更複雜,因為當其中一方(例如主資料庫或 ES)寫入失敗時,就需要回溯資料來維持一致性,導致整體系統更難維護。
為了解決資料同步的問題,可以使用 CDC (Change Data Capture,異動資料擷取) 相關的技術與工具。簡單來說,CDC 會在主資料庫發生變更時,擷取這些異動並轉換為事件流(stream),再由消費者(像是 ES、Cache 等這類需要同步主資料庫的系統)非同步地接收並處理這些事件,達到資料最終一致性的效果。
而在系統設計面試中可能不太需要太深入提到 CDC 的原理,因此簡單以一個箭頭示意即可。
另外值得一提的是透過 CDC 來更新 ES 時,如果在短時間內遇到大量資料寫入可能會有瓶頸問題,這裡還可以另外加上 message queue 來解決,但因為以活動資訊這個場景來說可能不太會常更動,所以暫時不考慮再深入這塊。
5-5. 針對熱門活動處理
最後,可以再針對熱門活動做一些 CDN caching、熱門關鍵字組合快取等處理,而搶票活動的流程也可以再考慮像是發號碼牌的方式實作一個虛擬等待房讓搶票者先後排隊依序進入避免 booking service 負荷不了。
有興趣深入了解這塊的讀者可以再參考下方參考資料的影片 50:38 時間點學習理解,這裡因為上班族需要好好準備明天上班的心情就先偷懶放上最後的完成圖了 🥹
參考資源
以上就是這期週報的所有內容了。若內容有什麼錯誤、問題、討論也都歡迎透過以下管道與我交流,或直接留言與回覆這封電子信我也能收到:
Email:codefarmer.tw@gmail.com