eBay是如何進行大數據集元數據發現的

很多大數據系統每天都會收集數PB的數據。這類系統通常主要用於查詢給定時間範圍內的原始數據記錄,並使用了多個數據過濾器。但是,要發現或識別存在於這些大型數據集中的唯一屬性可能很困難。

在大型數據集上執行運行時聚合(例如應用程序在特定時間範圍內記錄的唯一主機名),需要非常巨大的計算能力,並且可能非常慢。對原始數據進行採樣是一種發現屬性的辦法,但是,這種方法會導致我們錯過數據集中的某些稀疏或稀有的屬性。

介紹

我們在內部實現了一個元數據存儲,可以保證實時發現大量來自不同監控信號源的所有唯一屬性(或元數據)。它主要依賴於後端的Elasticsearch和RocksDB。Elasticsearch讓聚合可以查找在一個時間範圍內的唯一屬性,而RocksDB讓我們能夠對一個時間窗口內具有相同哈希的數據進行去重,避免了冗餘寫入。

我們提供了三種監控信號源的元數據發現:指標、日誌和事件。

指標

指標是週期性的時間序列數據,包含了指標名稱、源時間戳、map形式的維度和長整型數值,例如http.hits 123456789034877 host=A。

在上面的示例中,http.hits是指標名稱,1234567890是EPOC UTC時間戳,34877是長整型數值,host=A是維度{K,V}鍵值對。我們支持發現指標名稱和帶有維度map的名稱空間。

日誌

日誌是來自各種應用程序或軟件/硬件基礎設施的日誌行。

我們用以下格式表示日誌:

日誌對用例(也稱爲名稱空間)來說總是可發現的。每個日誌行都可以是某種特定類型,例如stdout或stderr。

日誌信號的類型(也稱爲名稱)也是可發現的,如上例所示,鍵值map也是可發現的。

事件

事件類似於日誌和指標。它們可以被視爲一種稀疏指標,表示爲系統內的事件。它們是非週期性的。例如,路由器交換機變爲不可用時會被記錄爲事件。此外,它們可能會有點冗長,可能會包含大量的文本信息用以說明事件期間發生了什麼。

事件的一個簡單示例:

與日誌和指標類似,事件也有名稱空間和名稱,兩者都是可發現的。可發現的字段鍵讓我們能夠在已知字段上執行聚合操作,例如MIN、MAX和COUNT。

下面的截圖突出顯示了我們的產品控制檯中的發現屬性:

方法和設計

所有監控信號最初都由我們的ingress服務實例負責接收。這些服務節點使用自定義分區邏輯將不同的輸入監控信號(日誌、指標和事件)推送到Kafka數據總線主題上。元數據存儲ingress守護程序負責消費這些監控信號,然後將它們寫入到後端Elasticsearch。

我們收集的監控信號被推送到Kafka總線上,它們是我們的源數據流。Kafka的一個優點是它提供了持久存儲,即使下游管道處於維護或不可用狀態。我們還在入口服務上使用自定義Kafka分區器,以確保具有相同哈希值的鍵始終位於相同的Kafka分區上。不同的監控信號內部使用不同的哈希值。例如,我們使用基於名稱空間+名稱的哈希值來表示指標信號,而日誌信號則使用了基於“名稱空間+維度{K,V}”的哈希值。這種分組有助於降低下游Kafka消費者需要處理的數據量基數,從而有效地減少內存佔用總量。

與我們的元數據存儲入口守護進程類似,還有其他一些消費者將原始監控信號寫入到後端存儲,如Hadoop、HBase、Druid等。單獨的發現管道可以在隨後將這些原始監控信號輸出,而無需執行昂貴的運行時聚合。

我們使用RocksDB作爲元數據存儲的嵌入式數據緩存,避免了對後端Elasticsearch數據接收器的重複寫入。我們之所以選擇RocksDB,是因爲它的基準測試結果非常令人滿意,並且具有很高的配置靈活性。

元數據存儲入口守護程序在處理記錄時,會將記錄的鍵哈希與高速緩存中已存在的哈希進行對比。如果該記錄尚未加載到緩存中,就將它寫入Elasticsearch,並將其哈希鍵添加到緩存中。如果記錄已存在於緩存中,則不執行任何操作。

RocksDB緩存偏重於讀取,但在剛開始時(重置緩存)時出現了一連串寫入。對於當前負載,讀取超過了50億,以及數千萬的寫入,大部分寫入發生在前幾分鐘。因此,在剛開始時可能存在消費者滯後的情況。對於較低的讀寫延遲,我們努力將所有緩存數據保存在RocksDB的內存中,以避免二次磁盤存儲查找。我們還禁用了預寫日誌(WAL)和壓縮。在基準測試中,我們發現16GB的內存就足以存儲哈希值。

上圖表示寫入後端Elasticsearch的文檔數。峯值對應於重置高速緩存之後的那段時間。

出於監控的目的,我們將所有rocksDB統計數據作爲指標發送到我們的監控平臺中。

我們使用Elasticsearch 6.x爲後端聚合提供支持,用以識別監控信號中的不同屬性。我們構建了一個包含30個節點的Elasticsearch集羣,這些節點運行在配備了SSD和64 GB RAM的主機上,並通過我們的內部雲平臺來管理它們。我們爲Elasticsearch JVM進程分配了30 GB內存,其餘的留給操作系統。在攝取數據期間,基於監控信號中的不同元數據對文檔進行哈希,以便唯一地標識文檔。例如,根據名稱空間、名稱和不同的維度{K,V}對日誌進行哈希處理。文檔模型採用了父文檔與子文檔的格式,並按照名稱空間和月份創建Elasticsearch索引。

我們根據{K,V}維度對根文檔或父文檔的document_id進行哈希處理,而子文檔則根據名稱空間、名稱和時間戳進行哈希處理。我們爲每一個時間窗口創建一個子文檔,這個時間窗口也稱爲去抖動時段。去抖動時間戳是去抖動時段的開始時間。如果在去抖動期間發現了一個子文檔,這意味着子文檔的名稱空間和名稱的唯一組合與其父文檔拓撲會一起出現。去抖動時間越短,發現唯一屬性的時間近似就越好。Elasticsearch索引中的父文檔和子文檔之間存在1:N的關聯關係。

Elasticsearch中的父子文檔動態模板是這樣的:

子文檔的模板是這樣的:

我們爲Elasticsearch集羣維護了兩個負載均衡器(LB)。READ LB IP(VIP)用於客戶端節點,負責所有的讀取操作,WRITE LB VIP則用於數據節點。這樣有助於我們在不同的客戶端節點上執行基於聚合的計算,而不會給數據節點造成太大壓力。

如果你要頻繁更新同一個文檔,那麼Elasticsearch不是最好的選擇,因爲文檔的片段合併操作非常昂貴。在出現高峯流量時,後臺的文檔片段合併會極大地影響索引和搜索性能。因此,我們在設計文檔時將其視爲不可變的。

我們使用以下的命名法爲Elasticsearch集羣創建索引:

例如,以下是後端Elasticsearch服務器的索引:

我們按照月份來維護索引,並保留三個月的索引。如果要清除索引,就直接刪除它們。

我們的發現服務是一個作爲Docker鏡像進行部署的Web應用程序,它公開了REST API,用於查詢後端元數據存儲。

發現服務提供的關鍵REST API包括:

  • 在不同的監控信號(日誌/事件/指標)上查找名稱空間(或用例);

  • 查找給定時間範圍內名稱空間的所有名稱;

  • 根據輸入的名稱空間、名稱列表或給定的時間範圍查找所有監控信號的維度鍵值;

  • 根據輸入的名稱空間和給定時間範圍查找值鍵;

  • 根據輸入維度{K,V}過濾器查找所有名稱空間或名稱;

  • 對於給定的名稱空間、名稱和不同的維度過濾器,還可以根據該唯一輸入組合找到其他關聯維度。

我們的元數據存儲入口守護程序部署和託管在內部Kubernetes平臺(也稱爲Tess.io)上。元數據存儲入口守護程序的應用程序生命週期在Kubernetes上作爲無狀態應用程序進行管理。我們的託管Kubernetes平臺允許在部署期間自定義指標註解,我們可以在Prometheus格式的已知端口上發佈健康指標。監控儀表盤和警報是基於這些運行狀況指標進行設置的。我們還在發現服務上公開了類似的指標,以捕獲錯誤/成功率和平均搜索延遲。

性能

  • 我們能夠在10個元數據入口守護進程節點(下游Kafka消費者)上每分鐘處理160萬個指標信號而不會出現任何性能問題;

  • 可以在幾秒鐘之內發現任何唯一的元數據屬性;

  • 在我們的生產環境中,我們的去抖動窗口間隔設置爲12小時,在每個去抖動期間,我們擁有約4,000萬的唯一基數(最多可達6000萬)。

目前,我們發現生產環境中觸發的大多數查詢的平均延遲爲100毫秒。而且我們發現,跨名稱空間觸發的查詢比基於目標名稱空間的查詢要慢得多。

結論

將發現功能與實際數據管道分離讓我們能夠快速深入瞭解原始監控數據。元數據存儲有助於限制需要查詢的數據範圍,從而顯著提高整體搜索吞吐量。這種方法還可以保護原始數據存儲免受發現服務的影響,從而爲後端存儲節省了大量的計算資源。

查看英文原文:https://www.ebayinc.com/stories/blogs/tech/an-approach-for-metadata-store-on-large-volume-data-sets/

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