上一篇主要從設計層面,分享了一些小經驗。
因軟體系統有其複雜性和多樣性,不同的場景、架構下,系統的瓶頸各不相同。
文章裡的一些想法和設計並不通用,主要針對的是高併發場景下海量資料的實時查詢。
這次再分享一個更貼近生活的案例。
有時看似簡單的邏輯,往往隱藏了最深的坑,甚至成為系統的效能瓶頸。
舉個例子
微博使用者主頁
這個頁面應該都不陌生,業務也並不複雜。
值得注意的是紅線圈出的功能:粉絲數量,微博總數,評論、轉發、點贊數量。
乍一看很簡單,再一看還是很簡單。
根據條件,從相關的業務表統計數量就好了呀。
比如, 從好友關係表count所有關注作者的人( type = 2 代表被關注)。
select count(1) from user_relation where uid = 'xxx' and type = 2
沒毛病啊,十分合理。這麼簡單的東西難道還能玩出花來?
第一版上線時
我就是這麼做的,結果整個服務直接掛掉了。
看了下監控,發現量變竟然引發了質變。
在系統資料量不大或流量不高時,使用count查詢來處理此業務沒有任何問題。
當資料達到一定量級時,比如圖中的粉絲量300W+,如果查詢的結果集數量過大,即便根據索引查詢,正常耗時也會達到幾秒、十幾秒,更別說在高併發下頻繁查詢。
使用者主頁,一頁查詢15條動態,每條動態都要顯示轉發、評論、點贊數,總共需要count45次。
再加上微博上億的使用者量,超高的併發,此處若實時地使用count查詢,將輕鬆GET以下異常:
The last packet sent successfully to the server was 5,652 milliseconds ago
因為MySQL伺服器所有的執行緒都用來執行這些count慢查詢了,導致後面的請求全部阻塞。
此時,資料庫效能監控皮膚會有一些顯著的指標特徵:CPU負載達到100%,執行執行緒數異常高。
第二版上線時
我專門加了一張表用來計數, 類似 (動態id, 點贊數, 評論數, 轉發數 )這樣的結構。
每次使用者點贊、評論、轉發時,直接在表上寫入數量+1。
查詢時直接從此表讀取數量,避免count查詢。
解決方法雖簡單了些,能搞定效能上的瓶頸即可。
此前,也參考過一些關於微博架構的資料,裡面有提到計數器服務。(如:https://www.cnblogs.com/kenshinobiy/p/4316217.html。)
但當時並沒理解為什麼要做計數器?什麼數這麼吊還要專門做個服務來計算?
微信朋友圈
微信朋友圈
朋友圈的設計,就完全不用展示數量。
直接避開了很多可能給系統帶來效能瓶頸的坑。
相比之下,微信的工程師一定能少掉不少頭髮。
當然,二者社交場景不同,介質也不同(WEB | APP),有些產品上的設計是不可避免的。
綜上
在MySQL進行實時查詢時,應該避免查詢出的結果集數量過大。
否則,即使像count這樣最簡單的查詢語句,也可能帶來效能問題。
當然,最終還是要看具體的使用場景。
比如,一個用來彙總每日報表的查詢語句,使用了 union,group by 等效能不佳的子句,整個查詢耗時5秒。
但它每天只執行一次,且查詢的資料表並不頻繁使用。即便這是一條慢查詢,對效能的影響也是微乎其微。
而像案例中的count語句,即便耗時只有1秒,如果每秒請求1000次,那它對資料庫CPU的消耗就是前者的200倍。
無聊可關注下公眾號: 躺平de程式設計師