騰訊開心鼠英語存儲演進漫談

今年騰訊開心鼠項目的用戶量每天都在肉眼可見的急劇增長,某些周的複合增長率甚至達到了10%,隨着用戶量的增長和業務複雜性的增加,數據庫的高峯性能壓力和存儲壓力不斷變大,下面整體介紹下我們進行的一系列存儲架構的調整以及未來的規劃。

年初項目的整體架構如下,大部分模塊都在以(ip+port)的方式使用同個DB實例。

在量較小的情況下,核心DB的CPU和存儲負載都沒有太大壓力,但量變大後整體風險逐步暴露,主要有:

耦合度高:任何一個業務svr的SQL性能都會影響所有業務的性能,一旦某個svr沒控制好,可能整體全掛

擴展性差:整個存儲的磁盤空間,都寫性能都會受到單機限制。

階段1:一主多從

結合高峯期數據庫審計日誌和對業務模塊的梳理,發現數據庫訪問有以下特點:

讀寫QPS比例大致在10 : 1;

寫主要集中在用戶的物品數據,課程數據,學習數據等;

讀主要分佈在賬號體系,集訓營,管理端,支付物流,學習記錄等;

慢查詢主要集中在集訓營和管理端的業務上。

針對讀寫比例的特點,優先優化讀請求。

在一主一從的基礎上擴充兩個RO組,從數據實時性和業務重要性對讀操作進行拆分。

數據實時性

針對實時性要求高的用戶賬號,物品數據讀取主庫;

針對用戶課程數據,學習數據讀取從庫;

業務重要性

APP主功能,支付等讀取主庫和從庫1;集訓營和管理端讀取從庫2;數據統計、分析讀取從庫3;

進行讀庫的拆分後,線上運行穩定,主庫CPU負載從40%下載到了20%。

階段2:橫向拆分

在整個讀拆分過程發現,從業務劃分來看,數據庫數據主要分爲APP用戶數據,集訓營數據,支付物流數據,運營活動數據;

其中除了用戶賬號數據是多個業務模塊所需的,其他的基本上是獨立的,於是我們考慮將數據庫進行拆分,拆分爲4個數據庫:

公共數據庫:用戶APP賬號數據,微信序賬號數據,課程靜態數據;

集訓營數據庫:集訓營分配數據,上課數據,老師數據;

支付/物流數據庫:支付數據,物流數據,運營活動數據;

UGC數據庫:用戶課程數據,學習數據,活動數據。

每個數據庫對應一個modle模塊,將各表的增刪改查等操作封裝,各自以RPC方式去調用其他庫的數據庫操作。

橫向拆分主要涉及代碼的改造和數據的遷移,兩個核心問題是:

儘可能降低遷移的改造成本

如何保障業務不中斷平滑遷移

以UGC數據庫爲例,該庫數據主要分爲兩大類:

用於數據分析的少量關係化查詢的 用戶活動數據 ;

存在大量關係化查詢的 用戶課程數據 。

在用戶規模不大時,原有的UGC數據都存儲在mysql中,但隨着用戶數增長,mysql的存儲空間和寫性能都無法滿足訴求。

用戶課程數據

該部分數據需要支持關係化查詢,我們採用了騰訊雲的mysql集羣解決方案TDSQL,TDSQL主要優勢在於:

整體容量隨着分片數的增加而增加,並且是動態擴容的,不影響業務;

讀寫分離,擁有更好的讀寫性能;

提供Proxy代理,業務像使用單機Mysql一樣;

SQL語句完全兼容,業務遷移成本低;

完備的監控指標和告警支持,同時支持性能分析。

數據表的平滑遷移

選擇合適的sharedkey在TDSQL庫中創建新的表;

使用mysqldump的數據導出服務,導出已有的全量數據,再導入到TDSQL;

通過騰訊雲的DTS(數據傳輸服務),接入在線教育的統一binlog notify服務,將數據增量變更寫入到TDSQL;

將項目中數據庫配置進行讀庫和寫庫的改造;

進行項目中的該表讀操作改造,將其改造到TDSQL對應表;

線上業務監控及觀察一段時間,如有問題,及時回滾讀庫配置;

讀請求遷移平穩後,對項目中的該表寫操作改造,將其改造到TDSQL對應表;

線上業務及監控觀察一段時間,如有問題,及時回滾寫庫配置;

停止DTS服務和binlog notify服務,遷移完成。

當然我們的UGC業務有一定的延時誤差是被允許接受的;如果對延時的接受程度較低,可以採用Mysql,TDSQL的雙寫方案來進行改造;先遷移寫再遷移讀來進行遷移改造,這裏不再贅述,歡迎隨時討論。

用戶活動數據

以用戶學習數據爲例,主要爲用戶完成學習活動過程中,在各個環節的表現,該部分數據主要用於數據分析,基本全爲寫操作,我們最終選擇mongdb來存儲這部分數據,主要優勢在於:

mongdb面向集合存儲,模式自由,可以方便地擴展學習數據字段;

mongdb支持大數據量的存儲,可以滿足這種隨時間膨脹的特厲害的存儲訴求;

mongdb支持一定程度的關係化查詢,滿足按用戶ID,活動ID來查詢數據;

強大的聚合工具,完美配合MapReduce等數據分析工具;

支持數據複製和恢復能力,便於分析數據的傳輸。

數據表的平滑遷移

在mongdb中建立對應的數據庫表

改造項目代碼,將原有數據庫表的寫請求存儲到Kafka隊列中

使用遷移服務,對原有數據表的全量數據進行遷移,寫入到mongdb中

步驟3完成後,啓動Kafka對應topic的消費服務,開始將數據平滑寫入到mongdb

針對各庫的每張表都可以採用類似的方案進行遷移,至此我們可以開始對單庫進行優化。

階段3:單庫優化

以公共數據庫爲例,通過分析業務高峯期的的數據庫審計日誌,發現:

50%的請求基本集中在用戶賬號表上,10%的請求集中在課程靜態數據上;

下面將討論如何優化這兩部分請求。

用戶賬號表

該表主要特點有:

寫請求佔比大致爲11:1;

寫請求主要集中在密碼、login時間等少數字段上;

大部分用戶數據:像手機號,ID等基本不變,同時這些字段可接受一定的時延。

於是將用戶賬號表拆解成兩部分:

對於時延不敏感數據拆成一張mysql表,同時將表數據緩存到redis中;

寫請求較多的拆解成爲redis中的緩存。

對比了業界主要的緩存更新方案,考慮對業務無入侵、實時性優、監控完善等特點;

選用便於接入的在線教育統一的DTS更新服務。

課程相關數據表

該表數據特點:

基本爲靜態數據表,不常變更;

佔用空間大致在10M左右,且可預見的數據都不會很大。

可採用內存緩存的來進行讀加速,在服務啓動時初始化後定時更新。

同時還對各庫進行了一系列的慢查詢優化,索引優化,儘量保證單庫的可用性及性能。

階段4:整體優化

在針對性對單庫進行性能優化後,在實際開發和維護過程中還存在些痛點:

業務方調用成本高,得理解不同數據庫的差異;

多數據庫的安全防護,統計功能、監控等功能都很分散;

UGC數據大表業務高峯寫入QPS很高;

UGC數據大表無法在線DDL操作。

問題1/2:DataProxy統一代理

除了針對各個svr合理調整連接數外,引入DB代理也是個較好的解決方案;於是採用Data Proxy的方案,由其代理各個數據庫的訪問,提供統一接口給業務方調用;同時在proxy可進行各種安全防護措施,比如過載保護,緩存崩塌保護,穿透保護等;另外還能對外提供數據統計、延時監控功能。

問題3:Kafka平滑寫入

由於業務特點,高峯期用戶數在平時的10倍以上,單純的擴容數據庫機器在大部分時間內都是極其浪費的;同時由於數據特點,少量數據的讀延遲是可以接受的;於是引入KAFKA隊列,先由PROXY統一寫入到隊列中,再由消費服務平滑寫入TDSQL與MongoDB;這樣就能較低成本扛住高峯期流量和突增流量,並且有很好的擴展性。

問題4:在線DDL暫停消費服務

對於大表的DDL一直是數據庫優化過程中的一個老大難問題;數據庫表的變更,不管是索引變更還是字段變更基本都會縮表從而引發業務中斷;在引入隊列平滑寫入情況下,可在線隨時停止消費服務後進行的DDL變更,但不會影響正常業務的運轉。

最終存儲架構

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章