攜程度假商品千億日志系統架構演進
作者簡介
cd,攜程資深后端開發工程師,度假商品系統研發,專注于后端系統性能提升。
團隊熱招崗位:資深后端開發/專家、資深后端開發-商品后臺
在攜程旅游度假的線路類商品系統中,由于商品結構復雜,涉及底層數據表上千張,在日常供應商以及業務維護過程中,每日產生6億+的數據變動記錄。這些數據的變動留痕,不但可供錄入方查看,也對日常產研的排障起著至關重要的作用,同時也可以提供給BI做數據進一步分析。商品日志系統建設尤為重要,隨著商品日志系統不斷發展迭代,已經積累達到1700億條日志。
本文將介紹線路商品日志系統的演進過程以及在其中遇到的問題。
- 一、發展軌跡
- 二、演進過程
- 2.1 V1.0 DB單表存儲
- 2.2 V2.0 平臺化
- 2.3 V3.0賦能
- 三、結語
一、發展軌跡
線路商品日志系統的發展大致可以分為以下三個階段:
2019年以前:單表日志
在 2019 年以前,商品系統尚無統一的日志系統來記錄商品的變更,在系統中使用DB日志表,該表以非結構化的方式記錄商品基本信息的變動。
2020年~2022年:平臺化
在系統改造過程中,建立統一的商品系統日志平臺,通過配置的方式記錄商品的數據變動日志,覆蓋商品維護的全部流程。
2023年~2024年:開放
經過在線路商品系統的實踐,商品系統日志平臺經歷百億級數據的考驗,并以靈活的配置方式記錄數據變動日志,同時支持自定義索引字段,具有接入和使用成本較低的優勢。為此,通過對商品系統日志進行改造,逐步向門票、用車等業務線開放使用。
二、演進過程
2.1 V1.0 DB單表存儲
在2019年以前,記錄線路商品的變動日志較為簡單,在DB中建立一張日志表(id,LogContent)來記錄日志,數據變動以非結構化的文本記錄在LogContent字段內,且僅覆蓋商品最基本的信息,在使用時通過數據庫查詢工具執行sql語句like關鍵字進行查詢,這種方式帶來的問題也顯而易見:
- 數據量大,性能低
由于是單表文本字段存儲,導致表的數量非常巨大,達到單表10億+(370GB)的數據,查詢超時問題嚴重,不得不進行定期歸檔。
- 可讀性差,僅開發人員使用
由于日志內容以文本字段存儲,在進行日志查詢時,一般由開發人員使用 like 語句直接查詢 DB,例如:select id, LogContent from log where LogContent like '%1234%'。查詢速度緩慢,嚴重影響日常排障流程。
- 擴展性差
由于日志寫入與業務代碼強耦合,且采用非結構化存儲。對于新增日志,需要對業務代碼進行改動,在接入時存在一定的成本,且接入后無法直接提供給供應商或業務人員直接使用,最終仍需要開發人員進行查詢轉換。
2.2 V2.0 平臺化
2.2.1 技術選型
針對 V1.0 遇到的問題,重點在于海量日志數據的存儲與查詢,業內解決海量日志數據存儲與查詢的方案一般有以下幾個:
ES+Hbase
HBase 提供高并發的隨機寫和支持實時查詢,是構建在 HDFS 基礎上的 NoSql 數據庫,適用于海量日志數據的存儲,可支持到 PB 級別的數據存儲。但其查詢能力有所欠缺,支持 RowKey 快速查詢,若有復雜查詢則需要自建索引。ES 提供強大的搜索能力,支持各種復雜的查詢條件,適合快速檢索及靈活查詢的場景。ES + HBase 的組合,利用各個組件的優勢,結合起來解決海量日志數據存儲及查詢的問題,但架構較為復雜,需要保證兩個組件間數據的一致性。
MongoDB
支持多種查詢,具有文檔型及嵌套的數據結構,但其支持的數據量級一般在 10 億級別,對比 HBase 要欠缺得多。如果想要處理 TB 級以上的數據量,需要進行適當的架構設計和優化,例如利用分片集群來水平擴展數據等,付出的成本會比較高。
ClickHouse
Clickhouse 是一個開源的列式數據庫,采用列存儲的數據組織方式,具有高性能、可伸縮性、容錯性和低延遲查詢等特點。查詢性能出色,可實現秒級甚至毫秒級的查詢性能,對于數據壓縮和存儲效率高,可節省成本。適用于海量數據的存儲及查詢場景。
通過對以上方案進行對比,我們的數據量級已經超過 MongoDB 一般的處理能力,因此該方案被淘汰。對比 ES + HBase 與 ClickHouse,這兩個方案都比較適合海量日志的存儲與查詢,但是受限于內部成本控制,CK 集群的日志保存時長被控制在一定天數內,無法滿足我們業務場景的需求。最終,我們選擇 ES + HBase 的方案。
2.2.2 整體架構
基本原理即利用 HBase 解決存儲問題,利用 ES 解決搜索問題,并將 ES 的 DocID 與 HBase 的 RowKey 關聯起來。通過發揮各個組件的優勢,相互結合解決海量日志的存儲與查詢問題。如上圖所示,在接入日志 API 后,所有日志均經過 MQ 進行異步處理,如此既能夠將日志寫入與業務代碼的邏輯解耦,又能確保寫入速度的平穩,避免高峰流量對整個 ES + HBase 集群的寫入造成壓力。
2.2.3 RowKey設計
RowKey設計原則:
唯一性:RowKey應保證每行數據的唯一性;
散列性:數據均勻分布,避免熱點數據產生;
順序性:可以提高查詢性能;
簡潔性:減少存儲空間及提高查詢性能;
可讀性:以便人工查詢及理解;
對于線路商品日志,對于直接可讀性要求不高,查詢的場景我們是從ES中先查出RowKey,再用RowKey去hbase查詢日志原文,整個過程RowKey是人工不可見的,結合我們實際的場景,線路商品數據日志的RowKey由五部分構成{0}-{1}-{2}-{3}-{4}
{0}:傳入的pk 轉換為md5[pk]值16進制字符串,取前8為
{1}:tableId補0至8位
{2}:pk+4位隨機值補0至24位
{3}:log類型補0至16位
{4}:時間戳
2.2.4 擴展
對于線路商品信息的維護分散于不同的模塊中,例如錄入模塊、直連模塊等。鑒于此,我們抽象出統一的數據寫入服務,并提供統一的日志接入 API,API內部異步寫入日志。在底層的數據寫入服務中,將所有的寫入操作接入日志 API。通過這個方式,將擴展性統一到日志配置中心。
寫入流程
日志的寫入流程如上圖所示,客戶端調用日志 API 以進行數據變動日志的寫入操作。日志服務在接收到請求后,將其拋入 MQ,由后續的消費組進行消費處理。消費組件在接收到消息后,會進行相應的消費處理,并根據上述的RowKey生成策略為該條日志生成 RowKey,隨后將日志文本內容寫入 HBase,在寫入成功之后,再將索引數據寫入到 ES。其中,若 HBase 或 ES 中的任何一個寫入失敗,都會將此條日志寫入補償 redis 集群,再由補償邏輯進行后續補償,以確保整個日志的寫入成功。
查詢流程
日志的查詢流程如上圖所示:客戶端調用查詢 API 并傳入查詢參數,日志服務接收到請求參數后,將其轉換為 ES 分頁查詢請求,從 ES 集群中查出 RowKey,再匯總 RowKey 并從 HBase 中批量查出日志全文內容。
此外,我們利用上述查詢 API,建立一個日志查詢頁面,供研發人員使用。在該頁面,相關開發人員可以便捷地進行數據變動日志的查詢。上述日志平臺的建立,相對完美地解決線路商品海量數據變動日志的存儲及查詢問題。同時,抽象日志的配置中心,解決一定的擴展性問題。
整個系統的優點在于:基于表級別日志的商品日志記錄,覆蓋全面,配置靈活,索引結構化存儲,支持海量日志數據的存儲及查詢。
缺點是:對于使用方而言存在一定的局限性,過于“技術化”,開發人員使用較為方便,但供應商與業務人員使用困難。
2.3 V3.0賦能
2.3.1 業務賦能
存儲能力
隨著日志寫入量的增加,日志查詢效率逐漸下降,對 ES 和 HBase 的拆分勢在必行。如下圖所示,我們對 ES 和 HBase 進行橫向的拆分與擴容,并在日志配置中心制定匹配規則,根據接入日志類型的不同,將其匹配到不同的集群進行寫入。此外,對于接入方,我們也支持獨立集群申請,使用方可以根據自身情況決定是使用獨立的集群部署,還是使用公用集群。
搜索能力
搜索能力的提升主要由以下兩個部分:
索引字段擴展:支持的索引字段更多。前期我們絕大部分場景的日志的索引條件是產品id或者資源ID,隨著接入的日志變多,索引字段也變的豐富起來。對于日志搜索場景我們進行梳理,預留10個可支持不同查詢的索引字段(其中四個數值型、4個字符型,2個日期型)供使用方使用,覆蓋絕大多數的查詢場景。
ES索引分區:隨著接入的日志增多,單個索引文件也愈發龐大,直接影響日志的查詢性能。一般而言,對于日志類型數據,常見的方案是依據時間建立索引,該方案的優勢如下:
1)提升查詢性能。若日志攜帶時間范圍進行查詢,則可僅搜索特定時間段的索引,避免全量索引的查詢開銷。
2)便于數據管理??梢园凑諘r間刪除舊的索引,從而節省存儲空間。
通過對日志進行分類,主要包括商品信息、開關班、價格庫存等模塊。隨后結合業務使用場景、每天產生的增量數據以及服務器資源進行評估,最終決定按周建立索引,且索引數據保留一年。
1)利用定時任務在每周一時創建下一周的索引;
2)利用定時任務每周刪除已過期的索引;
基于以上存儲能力與搜索能力的擴展提升之后,我們在日志配置中心定制了【業務線<-->日志集群】的路由規則,來決定接入的其它業務線日志最終存儲的日志集群,提供了更加靈活與具有彈性的業務線接入能力。
2.3.2 供應商賦能
展示能力
在 V2.0 版本中,日志頁面僅限于研發人員使用,底層數據過于技術化,業務與供應商難以理解。通常情況下,如果能將日志的查詢前置到供應商及業務環節,將極大地減少研發人員平時工作中的排障時間。
為此,我們提供 B 端的日志查詢頁面,給供應商及業務人員平時排查問題使用。我們對日志內容進行格式化的轉換處理,將其轉換為供應商和業務人員能夠理解的信息,包括行轉列、新舊對比、KV 轉換、關聯數據查詢等。日志內容不再是抽象的文本,而是展示為與平時使用的商品系統相對應的內容。這對業務和供應商更加友好。
擴展能力
基于上一步展示能力的提升,對于新接入的日志如何能快速為業務及供應商提供 B 端的頁面進行查看,這一步將大幅節省開發排查時間。對底層日志數據需要轉換為業務及供應商能看懂的信息的場景進行分析,并總結 7 種數據展示的方式,分別如下:
1)文本字段類:此種展示內容最為簡易,無需進行轉換,用戶可直接理解日志記錄的內容,前端展示的即為此內容。
2)數據關聯類:此類日志內容中記錄的是一個 id,但實際內容存在于另一個關聯表的數據中,例如 id:1 表示的是跟團游,不能將 id:1 的日志展示給用戶,而需轉換為“跟團游”,這就需要進行一步關聯 db 表的查詢轉換。
3)枚舉類:此類日志內容記錄的是一個 key 值,實際用戶能理解的是該 key 值所代表的含義,例如產品鉆級:0,就需要轉換為:“不分級”,這就需要關聯枚舉值的配置文件進行查詢轉換。
4)位存儲類:此類日志內容記錄的是一種計算后的結果數值。例如支付方式是通過按位與計算然后累加的結果。這種情況就需要按照一定的計算方式將其還原回去。
5)字段組合:此類日志記錄的是分散的數據,但實際需要將數據結合在一起查看才會更具業務意義,例如資源適用人檔,日志中分為最大、最小記錄。實際展示時需要結合到一起展示范圍。
6)外部接口:此類日志記錄的是一種依賴外部接口的值,例如日志記錄的是城市 id:2,代表的是上海,這就需要調用外部接口將 2 轉換為上海。
7)差異對比類:此類日志需對結果進行解析以作對比,從而使用戶能夠更為直觀地理解。通常存在兩種情形:其一,日志內容所記錄的即為兩份對比數據,此種情況僅需依循規則予以解析即可;其二,若日志數據屬于當次的快照數據,則需與前一次快照數據進行對比,以找出差異。最終達成如下圖所示的展示效果。
針對以上這些日志解析的場景,我們最終構建一個日志轉換配置。對于新加入的日志,在絕大多數場景下,我們只需修改底層的數據提取及轉換配置,便可較為快速的配置出日志查詢頁面,提供給供應商使用。
三、結語
本文詳細介紹度假商品日志平臺的演進歷程,以及在各個階段遇到的問題及解決方案。在整個演進過程中,針對海量日志數據存儲與搜索的技術挑戰,我們采取一系列措施,實現千億級數據查詢在 500ms 內的響應。
同時將日志系統開放,將問題查詢解決前置到供應商及業務人員,極大降低一些數據變動查詢需求的復雜度,減輕研發及TS同事重復性的工作。此外,我們還對日志平臺進行橫向的擴容配置,以支持更多的業務線可以接入。截至目前,多個業務線總數據存儲量達到千億級別。