(使用 Goku 進行兩級分片)html
時間序列數據模型 時間序列數據git
Goku 遵循 OpenTSDB 的時間序列數據模型。時間序列由一個鍵和一系列時間數字數據點組成。key = metric name + 一組標記鍵值對。例如,「tc.proc.stat.cpu.total.infra-goku-a-prod {host = infra-goku-a-prod-001,cell_id = aws-us-east-1}」。數據點 = 鍵 + 值。值是時間戳和值對。例如,(1525724520,174706.61),(1525724580,173456.08)。github
時間序列查詢服務器
除了開始時間和結束時間以外,每一個查詢都由如下部分或所有組成:度量標準名稱、過濾器、聚合器、降採樣器、速率選項。網絡
1)度量名稱示例:「tc.proc.stat.cpu.total.infra-goku-a-prod」。架構
2)對標記值應用過濾器,以減小在查詢或組中拾取系列的次數,並在各類標記上聚合。Goku 支持的過濾器示例包括:徹底匹配、通配符、Or、Not 或 Regex。app
3)聚合器規定將多個時間序列合併爲單個時間序列的數學方法。Goku 支持的聚合器示例包括:Sum、Max / Min、Avg、Zimsum、Count、Dev。ide
4)降採樣器須要一個時間間隔和一個聚合器。聚合器用於計算指定時間間隔內全部數據點的新數據點。性能
5)速率選項可選擇計算變化率。有關詳細信息,請參閱 OpenTSDB 數據模型(http://opentsdb.net/docs/build/html/user_guide/query/index.html)。ui
挑戰
Goku 解決了 OpenTSDB 中的許多限制,包括:
1)沒必要要的掃描:Goku 用倒排索引引擎取代了 OpenTSDB 的低效掃描。
2)數據大小:OpenTSDB 中的數據點是 20 字節。Pinterest 採用 Gorilla 壓縮來實現 12 倍壓縮。
3)單機聚合:OpenTSDB 將數據讀取到一個服務器上並進行聚合,而 Goku 的新查詢引擎是將計算遷移到更接近存儲層的位置,該存儲層在葉節點上進行並行處理,而後在根節點上聚合部分結果。
4)序列化:OpenTSDB 使用 JSON,當有太多數據點要返回時,JSON 會很慢;Goku 使用 thrift 二進制代替。
架構 存儲引擎
Goku 在內存存儲引擎中使用了 Facebook Gorilla 來存儲過去 24 小時內的最新數據。
(存儲引擎簡介。請查看 Gorilla 論文(http://www.vldb.org/pvldb/vol8/p1816-teller.pdf) 及其 GitHub 存儲庫(https://github.com/facebookarchive/beringei) 來了解詳細信息)
如上所述,在存儲引擎中,時間序列被分紅稱爲 BucketMap 的不一樣分片。對於一個時間序列,也被分爲能夠調整時長的 bucket(Pinterest 內部以每 2 小時爲一個 bucket)。在每一個 BucketMap 中,每一個時間序列都被分配一個惟一的 ID 並連接到一個 BucketTimeSeries 對象。BucketTimeSeries 將最新的可修改緩衝區存儲區和存儲 ID 保存到 BucketStorage 中的不可變數據存儲區。在配置存儲 bucket 時間以後,BucketTimeSeries 中的數據將被寫入 BucketStorage,變爲不可變數據。
爲了實現持久性,BucketData 也會寫入磁盤。當 Goku 從新啓動時,它會將數據從磁盤讀入內存。Pinterest 使用 NFS 來存儲數據,從而實現簡單的分片遷移。
分片和路由
咱們使用兩層分片策略。首先,咱們對度量名稱進行散列,以肯定某一時間序列屬於哪一個分片組。咱們在度量名稱 + 標記鍵值集上進行散列,以肯定時間序列在所在組中的哪一個分片。此策略可確保數據在分片之間保持平衡。同時,因爲每一個查詢僅進入一個組,所以扇出保持較低水平,以減小網絡開銷和尾部延遲。另外,咱們能夠獨立地擴展每一個分片組。
查詢引擎
倒排索引
Goku 經過指定標記鍵和標記值來支持查詢。例如,若是咱們想知道一個主機 host1 的 CPU 使用率,咱們能夠進行查詢 cpu.usage {host = host1}。爲了支持這種查詢,Pinterest 實現了倒排索引。(在 Pinterest 內部,它是從搜索項到位集的散列映射。)搜索項能夠是像 cpu.usage 這樣的度量名稱,也能夠是像 host = host1 這樣的標記鍵值對。使用這個倒排索引引擎,咱們能夠快速執行 AND、OR、NOT、WILDCARD 和 REGEX 操做,與原始的基於 OpenTSDB 掃描的查詢相比,這也減小了許多沒必要要的查找。
聚合
從存儲引擎檢索數據後,開始進行聚合和構建最終結果的步驟。
Pinterest 最初嘗試了 OpenTSDB 的內置查詢引擎。結果發現,因爲全部原始數據都須要在網絡上運行,性能會嚴重降低,並且這些短時間對象也會致使不少 GC。
所以,Pinterest 在 Goku 中複製了 OpenTSDB 的聚合層,並儘量地早地進行計算,以儘可能減小線上的數據。
典型的查詢流程以下:
用 Statsboard 客戶端查詢(Pinterest 的內部度量監控 UI)任何代理 goku 實例
代理 goku 基於分片配置將查詢扇出到同一組內的相關 goku 實例
每一個 goku 讀取倒排索引以獲取相關的時間序列 ID 並獲取其數據
每一個 goku 基於查詢聚合數據,如聚合器、降採樣器等
代理 goku 在收集每一個 goku 的結果並返回客戶端後進行第二輪聚合
性能
與以前使用的 OpenTSDB / HBase 解決方案相比,Goku 在幾乎全部方面都表現更好。
另外一個在使用 Goku 先後高基數查詢延遲對比圖,以下圖所示:
下一步 基於磁盤的長期數據存儲
Goku 最終將支持超過一天時間數據的查詢。對於像一年這樣的時長查詢,Pinterest 將不會過度強調一秒鐘內發生的事情,而是關注總體趨勢。所以,Pinterest 將進行降採樣和壓縮,把以小時計的 bucket 合併爲更長期的時長,從而減少數據量並提升查詢性能。
(Goku 階段#2——基於磁盤:數據包括索引數據和時間序列數據)
複製目前,Pinterest 有兩個 goku 集羣進行雙行寫入。此設置提升了可用性:當一個集羣中存在問題時,能夠輕鬆地將流量切換到另外一個集羣。可是,因爲這兩個集羣是獨立的,所以很難確保數據的一致性。例如,若是寫入一個集羣成功而另外一個未成功時,則數據寫入失敗,數據由此變得不一致。另外一個缺點是故障轉移老是在集羣層面發生。爲此,Pinterest 正在開發基於日誌的集羣內複製,以支持主從分片。這將提升讀取可用性,保持數據一致性,並支持分片級的故障轉移。
分析用例
全部行業都須要普遍的分析,Pinterest 也不例外,例如面臨詢問實驗和廣告推廣效果等問題。目前,Pinterest 主要採用離線做業和 HBase 進行分析,這意味着不會有實時數據產生,並避免大量沒必要要的預聚合。因爲時間序列數據的性質,Goku 能夠很容易地適應它,不只能夠提供實時數據,還能夠提供按需聚合。