導讀:約苗平臺是國內目前最大的成人預防接種管理服務平臺。近年來,隨著各功能的不斷完善,用戶數量不斷增多,越來越多注冊數據、疫苗類別點擊數據、頁面瀏覽時長等數據被生成和積累,如何有效利用這些數據進行處理分析,對于約苗提高工作效率、優化運營決策有著不容小覷的作用?;诖思s苗平臺歷經三代架構演進,最終通過 Apache Doris 重構了數據平臺架構,統一了數據源出口,實現了近 300 倍的查詢提速,目前已在消息系統、運營平臺、數據平臺、日志系統中得到廣泛的應用,接入近百億的數據量,并且在持續增加中。
四川馬太科技有限公司是一家扎根于疾病防控領域,具有專業的研發與運營團隊的互聯網公司,長期致力于改善和提升中國公眾疾病防控水平,助力“健康中國2030”。旗下擁有國內用戶量最大的成人疾病預防信息與服務平臺“約苗”(以下簡稱“約苗平臺”)。“約苗平臺”運用互聯網+模式傳播健康科普知識,為疾病防控提供先進的服務和管理工具。圍繞公共衛生服務機構的疾病預防業務,開展政策、疾病教育及預約服務。發展至今已是國內最大的成人預防接種管理服務平臺,作為連接公共衛生服務機構與公眾的橋梁,現已連接近 5000 家公共衛生服務機構和 4000 余萬用戶,累計提供 2000 余萬次疫苗預約服務,并產生科普內容閱讀數 3.3 億次。
業務背景
隨著約苗平臺各功能的不斷完善,用戶數量不斷增多,越來越多注冊、疫苗類別點擊、頁面瀏覽時長等數據被生成和積累,如何有效利用這些數據進行處理分析,對于提高工作效率、優化運營決策、增加下單率有著不容小覷的作用,因此我們決定搭建約苗數據架構,通過數據分析處理結果賦能業務發展,從而提高企業競爭力。
基于此,我們踏上了數據架構搭建及優化之路,為滿足不同場景下的數據處理和分析需求,相關場景對數據架構提出了以下幾點要求:
-
用戶行為分析 : 通過用戶瀏覽內容或頁面的時長進行用戶分層、掌握用戶行為喜好,通過對新用戶的增量和用戶活躍度進行統計,以便工作人員調整運營策略,提高用戶留存率和活躍度。在該場景下,要求所有查詢需要在 5s 內返回結果。
-
平臺通知: 約苗平臺消息推送服務包含 App 推送、內置通知、短信、信公眾號推送,在該場景下,期望可達到毫秒級查詢響應,解決 C 端通知查詢延遲的問題。
-
市場報表統計: 市場部每天需對相關業務進行報表統計,但由于數據量巨大,通常會出現查詢速度較慢的問題,這將嚴重影響產出計算,期望可以實現秒級查詢響應。
為了滿足要求,約苗的數據架構已經經歷了三代演進。第一代架構基于 Elasticsearch,第二代架構引入了 ClickHouse,目前正在使用的是基于 Apache Doris 的第三代架構。本文將詳細介紹這三代架構的演進歷程和搭建經驗。
基于 Elasticsearch 的第一代架構

第一代數據架構是基于 Elasticsearch 來構建的,主要用于處理來自業務各系統和日志系統的數據。其中,業務數據首先存儲在 MySQL 中,然后使用 Flink CDC 對 MySQL Binlog 監聽,將數據同步到 Elasticsearch 中。當展示層發起聚合請求時,Elasticsearch 中的數據進入聚合層對應的服務中實時計算,最終將結果輸出到展示端。
架構搭建完后我們立即投入生產來驗證效果,但在使用中我們發現 Elasticsearch 在高并發讀取和寫入的過程中延遲非常高,而為改善該問題,我們又增設了多套集群配置,但仍然于事無補。除此之外,Elasticsearch 得數據查詢性能也會隨著數據增長而下降。在這個情況下,如果想要提高 Elasticsearch 響應速度,還需進一步增加集群配置,以提高 Elasticsearch 集群負載能力,成本投入非常大。
引入 ClickHouse 的第二代架構

基于上述問題,我們對架構進行了升級。為避免架構演進對代碼帶來過大沖擊,我們保留了第一代架構中基本的數據同步邏輯,在其基礎上增加了 Apisix、Kafka、ClickHouse 同步流程,在此基礎上對 Flink 同步流程進行了優化。為了降低 Elasticsearch 的壓力,我們將日志數據、行為數據和文件系統數據進行了整體的遷移都 ClickHouse 。在 ClickHouse 同步流程中,我們使用 APISIX 的 Kafka-Logger 對行為采集和日志系統的數據進行直接上報,使用同步工具對上報數據進行清洗過濾,最終存儲到 ClickHouse中。在使用 Flink 同步時,我們引入了 RabbitMQ 消息組件來保障數據同步的穩定性(因歷史原因未使用 Kafka,建議盡量使用統一的消息隊列組件)。
在使用過程中我們發現 ClickHouse 性能固然強悍,但需要不斷進行調優,工程師的學習成本非常高,同時隨著用戶的體量的不斷上升,用戶活躍度的持續提高,ClickHouse 的運維管理和調優成本也逐步攀高。而過高的集群壓力再次導致 C端部分業務出現數據同步和查詢出現長達 5s 的延遲,這依舊沒有解決根本問題。
基于 Doris 的新一代架構
01 選擇 Doris 的原因
為徹底解決早期架構出現的問題,我們進行了深度調研,旨在選擇更適合我們的實時數據倉庫。在初步了解后,我們首先放棄了 Hadoop。一方面,Hadoop 的開發和運維難度較大,會增加我們的工作量;另一方面,Hadoop 更適合處理大數據量的批處理場景,而我們需要支持流批一體,以解決C端用戶的查詢延遲較高的問題。進一步了解之后,我們排除了 Kudu 選項。這是因為 Kudu 使用成本非常高,特別是在沒有主鍵的情況下,Kudu 會占用大量存儲資源。此外,一般情況下,為了滿足讀性能,Kudu 會犧牲寫性能,這與我們的業務需求不符。在多方面的了解和對比后,我們發現 Apache Doris 最符合我們的要求,因為 Doris 具有以下優勢:
-
支持更靈活的查詢:Doris 可以支持靈活且多變的數據分析需求,以滿足需求方針對不同群體高頻次的營銷活動。
-
支持聯邦查詢:Apache Doris 1.2 版本推出了多源數據目錄(Multi-Catalog)通過簡單的命令便可以方便地連接到各自外部數據源并自動同步元數據,實現數據出口統一,實現統一的分析體驗。
-
查詢性能優異:Doris Join 能力強大,依托列式存儲引擎、現代的 MPP 架構、向量化查詢引擎、預聚合物化視圖、數據索引的實現,在低延遲和高吞吐查詢上,都達到了極速性能。
-
運維難度低:Doris 對于集群和和數據副本管理上做了很多自動化工作,使得集群運維起來非常的簡單,幾乎可以實現零門檻運維。與此同時,Apache Doris 社區非?;钴S,響應迅速,并且 SelectDB 為社區提供了一支專職的工程師團隊,免費為用戶提供技術支持服務。
02 數據架構的重建

引入 Doris 后,我們對數據架構進行了重構,Doris 的位置及作用可從上方架構圖得知。
由圖可知,Doris 的數據來源非常多樣化,包括通過 Flink-CDC、Binlog 的業務數據,經由 Kafka 的行為數據和日志數據,以及從自研的全量/增量工具同步的其他數據。此外,還有部分通過 Doris Stream Load 和 Routine Load 同步的數據。這些來自不同工具的數據統一由 ETL 處理,最終存儲到 Kafka 或 Doris 集群中。其中存儲到 Kafka 中的數據會通過 Routine Load 高效寫入到 Doris 中,最終由 Doris 統一提供數據服務。Doris 的引入解決了各類同步工具的數據源統一的問題,不再需要維護大量配置,而統一數據出口也讓數據處理變得更加簡單和高效。
在新一代架構中,Doris 已完全取代了 ClickHouse,相比于 ClickHouse,Doris 具有更好的性能和更廣泛的適用性,在約苗所服務的業務已逐步從統計分析擴展到業務系統。基于 Doris 的高性能、高并發的支持,約苗平臺的用戶端相關業務延遲問題也得到了成功解決,使得數據查詢和分析更加及時和準確。
新數據架構搭建經驗
在新架構的使用過程中,我們積累了許多實踐經驗,希望能夠與大家分享。以下是我們總結出來的一些經驗:
01 Routine Load 實現數據快速導入
在數據導入方面,我們強烈建議使用 Stream Load 和 Routine Load 這兩種工具,因為它們的性能遠遠高于其他導入方式。
Stream Load 和 Routine Load 是 Doris 常見的兩種數據導入方式,Stream load 是一個同步的導入方式,用戶通過發送 HTTP 協議發送請求將本地文件或數據流導入到 Doris 中。Routine Load 功能支持用戶提交一個常駐的導入任務,通過不斷的從指定的數據源讀取數據,將數據導入到 Doris 中。
我們曾經嘗試過其他的數據導入方式,經過多次壓力測試和性能對比,并結合我們的實際數據類型,最終選擇了 Stream Load 和 Routine Load 進行數據導入。Routine Load 是一種高效、穩定的數據導入工具,它依賴于 Kafka 并具有極好的可靠性。與其他數據導入方式相比,Routine Load 支持的配置豐富,能夠根據配置來過濾和合并數據,同時需要開通的網絡策略也更少,能夠很好地滿足同步數據的需求。最重要的是,Routine Load 可以達到每分鐘數百萬行的數據導入,完全符合我們對數據導入速度的要求。
在使用 Routine Load 進行數據導入時,難免會出現全量和增量數據同時同步的情況,或者在多線程消費下出現數據積壓的情況,這就需要保證數據的一致性。因此我們可以通過 Doris 支持的 Sequence 列來保證數據的一致性,用戶可以在導入時指定 Sequence 列,相同 Key 列下,REPLACE 聚合類型的列將按照 Sequence 列的值進行替換,較大值可以替換較小值,反之則無法替換。該方法將順序的確定交給了用戶,由用戶控制替換順序。具體的實現方式如下:
-
在定義表時,先定義一個列,比如定義為
order_id。 -
接著在創建 Routine Load 時指定根據
order_id列進行寫入,以保證數據寫入順序。 -
在寫入數據時,需要保證每條數據都擁有
order_id列有值。 -
在進行全量同步時,可以使用
Java-sync來實現,全量同步時需要保證order_id始終為 0。 -
在進行增量同步時,可以使用讀取 Binlog 的方式進行同步,增量同步的
order_id需要從 1 開始。另外可以記錄增量同步的order_id值,以保證數據準確寫入 Unique 表。
02 分區分桶和查詢實踐
在使用 Doris 進行數據存儲時,建表時需要盡量貼近實際使用場景,以此來創建分區列和分桶列。這里將分享我們我們在創建表時的一些經驗。
- 分區:分區是建立一張大表時非常關鍵的因素,它直接影響到可以檢索到的數據大小和需要處理的數據量。如果一個分區的數據量太大,那么就需要更多的 CPU 和內存來處理,從而導致查詢效率降低。如果利用時間分區,可以考慮使用動態分區來讓 Doris 自行管理分區。動態分區可以根據時間自動創建新的分區,并刪除舊的分區,從而保持數據的最新性和存儲效率。同時,動態分區還可以幫助我們保留部分數據或者區分冷熱數據,從而更好地管理數據存儲和查詢。
- 分桶:考慮在一個分區下如何讓分桶足夠均衡,是每個表必須要做的事情。如果分桶列數太多,可能會影響點查詢的性能,如果分桶不夠均衡,查詢速度會受到最大桶處理時間的影響。因此需要在性能和查詢需求之間做出取舍。另外,推薦大家使用Apache Doris 1.2.2 版本的 Auto Bucket 自動分桶推算功能,分桶個數不再依賴于人工設置,通過規則的智能計算即可保證合理的數據劃分,降低用戶學習成本的同時還可以最大化提升用戶開發效率。
舉例來說,當我們需要記錄行為記錄寬表時,一般會使用 Duplicate 模型進行記錄。在建表時,我們可以把report_time、event_id、product_id、plate_id、user_id 作為 Key 列,并使用 report_time 按月進行動態分區,每月記錄數據 1.5 TB 左右,使用 product_id、plate_id、event_id 進行自動分桶。
在使用過程中我們發現,當某一類事件遠遠超過了其他事件的總數時,會嚴重導致數據傾斜嚴重。為了解決這個問題,我們增加了在分桶列后新增了user_id,從而達到了數據均衡的效果。通過這一優化,查詢速度由原來的 13s 提升到 5s。另外我們在這個基礎上使用了物化視圖進一步對查詢性能進行優化,物化視圖介入之后,查詢速度從 5 秒提升到毫秒級別。
Doris 在約苗的應用實踐
01 靈活組合查詢,提高廣告投放效率
由于疫苗的特殊性,業務側同學在推廣的過程中需要更精準的用戶信息,例如年齡、性別、地區等業務同學指定的信息。這種情況下,數據平臺就需要提供詳細的用戶群體信息,以幫助業務同學實現精細化信息觸達。
在接入 Doris 后,我們主要利用 Doris 的 Join 實現了一套完全可以支持任意字段組合查詢的工具。類似于常見的 SQL 管理工具,不同的是,無需自行編寫 SQL,只需選擇需要的字段進行組合配置即可查詢,得到想要的結果,以此來滿足需求多變的業務場景,從而提高業務側工作效率。同時,Doris 還具有高效的數據寫入和查詢能力,使得運營人員可以更加快速地獲取所需數據,進行數據分析和應用開發。
基于此,業務同學可以通過多種業務數據聯合檢索,快速篩選出滿足自己需求的用戶信息,并導出最終檢索結果。例如,可以對年齡在 19 歲至 45 歲之間,瀏覽過 HPV 九價疫苗頁面并且頁面停留時長大于 1 分鐘的用戶數據進行檢索,并導出結果。通過這一方式,業務同學可以快速獲取目標用戶信息,從而精準地進行營銷推廣和活動推送。
02 聯邦查詢實踐,百萬數據分鐘級導入
在使用 Doris 之前,某些業務場景需要數據組的同事自行編寫大量復雜代碼來完成數據導入任務。在接入 Doris 之后,我們利用 Muti-Catalog 功能建立了 ES 和 MySQL 的外表,在遇到數據導入任務時,我們僅需要花幾分鐘時間編寫 SQL,依靠 Doris 快速完成數據導入任務。同樣的數據導入任務(80 萬數據的)之前需要 1 -2 天的時間完成,而使用 Doris 之后,僅僅需要幾分鐘即可完成數據導入。
除此之外,Apache Doris 提供的 Multi Catalog 功能也幫助我們的統一了來自多種同步工具的數據源,成功統一了數據出口,使得數據處理變得更加簡單和高效,同時我們也無需投入過多的人力和精力去維護大量其他數據相關的配置,極大的節約了成本的投入。
03 Join 能力加持,實現 300 倍查詢速度提升
除此之外,Doris 的 Join 性能十分優異,當 Doris 集群完成業務數據的全同步后,我們對 1億 和近百億的兩張表進行 Join 操作,可以在 5 秒內輸出數據結果,相較之前有接近 300 倍的查詢速度提升。為了驗證其是否偶然,我們接著對 10 張分別有 4000萬數據的表進行 Join,Doris 可以在 10s 內返回查詢結果。而在物化視圖加持下,可以達到毫秒級別的查詢響應。這充分說明了 Doris 在處理大規模數據的優勢。
04 極低運維成本,助力降本提效
在之前的架構中,我們使用 ClickHouse 進行數據存儲和查詢,并且需要單獨維護Zookeeper 來管理集群的配置信息和狀態信息,這些都增加了系統的運維成本和難度。而 Doris 架構簡單,只有 FE 和 BE 兩個進程,擴縮容快捷方便,運維難度和成本得到極大的降低,此外,Doris 還提供了更加友好和易用的數據備份和恢復功能,可有效保障數據的安全性和完整性。
總結規劃
當前約苗已經基于 Apache Doris 搭建了一套完整的實時數據倉庫,并在消息系統、運營平臺、數據平臺、日志系統中得到廣泛的應用,目前已經接入百億級別的數據量,并且在持續增加中。未來我們將基于 Doris 不斷探索,擴大 Doris 使用范圍,逐步推廣至秒殺、訂閱、黑名單、預約等各個業務場景,同時我們也將嘗試使用 Doris 的新特性,以實踐結果回饋社區,為社區發展獻一份力,為社區做出實質性的貢獻。
最后,感謝 SelectDB 技術團隊長期以來快速響應和技術支持,為我們穩定高效應用 Doris 保駕護航。
