網飛經驗之談:利用Apache Druid進行實時觀測,確保高質量體驗

2020-04-11     讀芯術

全文共4381字,預計學習時長13分鐘


來源:Pexels


隨著時代的發展,軟體的版本也在與時俱進,不斷更新,以期更加滿足用戶的需求。


但我們怎麼能信誓旦旦地保證版本的更新一定會讓用戶滿意呢?我們的努力真的能讓改變發生嗎?


在保證良好的用戶體驗的同時不斷進行技術創新,這對網飛公司來說並非易事。


通過使用重放設備里的實時日誌作為事件源,我們導出度量,以便了解和量化用戶設備處理瀏覽和回放的無縫程度。


Log to Metric Data Pipeline


一旦我們得到了這些指標,我們就把它們輸入資料庫。每一項指標都會標註出所使用設備的隱藏細節,例如,該設備是智能電視、iPad還是Android手機。這使我們能夠根據不同方面進行設備分類和數據查看。這反過來又使我們能夠排除掉可能只影響特定群體的問題,例如應用程式的版本、設備的特定類型或特定國家。


此聚合數據可立即通過控制面板或即席查詢進行查詢。這些指標還將持續檢查報警信號,例如新版本是否影響某些用戶或設備的回放或瀏覽。所有這些檢查旨在提醒負責團隊儘快解決問題。


在軟體更新期間,我們為一部分用戶提供新版本,並使用這些實時度量來比較新版本與以前版本的性能。度量中的任何回歸/退化都會給我們傳遞一個信號,讓我們中止更新,並將那些獲得新版本的用戶還原回以前的版本。


因為這些數據的處理速度可超過每秒200萬次,想要將其放入一個可以快速查詢的資料庫是非常困難的。我們需要足夠的維度以便有效隔離問題,這樣每天可生成1150多億行數據。在網飛,我們利用Apache Druid來幫助我們解決這個挑戰。


Druid


「Apache Druid 是一個高性能的實時分析型資料庫,它是為快速查詢和快速攝入數據的工作流而設計的。Druid 擅長即時數據可視化、即席查詢,運行分析和高性能並發處理。」 —druid.io


如上所述,Druid非常適用於我們的用例。因為它可以快速查詢和攝入具有高基數的對象數據。Druid isnot a relational Druid雖然不是關係數據庫,但一些概念是可轉移的。我們有數據源而非表。與關係數據庫一樣,這些表都是數據的邏輯組合,以列的形式呈現出來。但與關係數據庫不同的是,它不支持join操作。因此,要確保我們想要篩選或分組的列包含於每個數據源中。


數據源中,列的屬性可以分為3類:時間戳列,維度列和指標列。


Druid里的一切都是由時間決定的。每個數據源都有一個時間戳列,以此作為主要的分區機制。維度列用於篩選、查詢或分組。指標列是用於聚合數據,一般為數值型。


Druid通過刪除執行join操作的能力,並假設數據是由時間戳列鍵入,可對其存儲、分發和查詢數據的方式進行一些優化,這樣就可以將數據源擴展到數萬億行,並且仍然可以在10毫秒內響應查詢。


為了實現這種可伸縮性,Druid將存儲的數據劃分為時間塊,並可根據數據和用例為時間塊配置適當的持續時間。目前我們的數據和用例使用1小時的時間塊。時間塊內的數據存儲在一個或多個segment中。每個segment保存的數據行都在由其時間戳列關鍵列確定的時間塊內。Druid可以配置segment的大小,使行數或段文件的總大小有一個上限。


Example of Segments


在查詢數據時,Druid將查詢發送到集群中的所有節點,這些節點為查詢範圍內的時間塊保存segment。在將中間結果發送回查詢代理節點之前,每個節點都會在其保存的數據上並行處理查詢。最後結果由代理節點合併後返回給調用方。


Druid Cluster Overview Druid


攝取


資料庫的插入是實時的。事件(我們的例子中是指標列)是從Kafka流中讀取的,而不是單獨插入到數據源中。每個數據源使用一個主題。在Druid中,我們使用Kafka Indexing Tasks來創建多個分布在實時節點(中間管理器)中的索引工作器。


每個索引器都會訂閱主題,並從工作流中讀取其事件共享。索引器根據攝取規範從事件消息中提取值,並將創建的行累積到內存中。一旦創建了行,就可以對其進行查詢。如果查詢到達的時間塊仍由索引器填充,則索引器本身將提供服務。由於索引任務基本上要執行兩個作業,即接收和安排查詢,因此要及時將數據發送到歷史節點並以更優化的方式將查詢工作轉移給它們,這是非常重要的。


Druid可以在接收數據時進行roll-up操作,將需要存儲的原始數據最小化。Roll-up是一種集合或預聚合的形式。在某些情況下,對數據執行roll-up可以顯著減少需要存儲的數據量,甚至可能按數量級減少行數。然而,這種存儲縮減是有代價的:我們失去了查詢單個事件的能力,只能在預定義的查詢粒度下進行查詢。對於我們的用例,我們選擇了1分鐘的查詢粒度。


在攝取過程中,如果出現具有相同的維度的行,並且它們的時間戳在同一分鐘(我們的查詢粒度)內,那麼這些行將被roll-up。這意味著行是由所有度量值相加後再加上計數器而組成的,這樣一來我們就知道有多少事件對該行的值有貢獻。這種形式的roll-up可以顯著減少資料庫中的行數,這樣需要操作和聚合的行就會減少,從而加快查詢速度。


一旦累積的行數達到某個閾值,或者segment已打開太長時間,那麼這些行就會被寫入segment文件並卸載到深層存儲中。隨後索引器通知協調器該segment已準備好,以便協調器告訴一個或多個歷史節點執行加載。一旦segment成功加載到歷史節點中,它便從索引器中卸載,針對該數據的任何查詢都將由歷史節點提供服務。


數據管理


如您所想,隨著維度基數的增加,在同一分鐘內發生相同事件的可能性就會降低。roll-up後的管理基數是實現良好查詢性能的有力手段。


為了達到所需的攝取量,我們運行了許多索引器實例。即使通過roll-up將索引任務中的相同行合併後,在索引任務的同一實例中獲得這些相同行的可能性也非常低。為了解決這個問題並使roll-up操作最優化,我們安排了一個任務,將在給定時間塊內的所有segment都移交給歷史節點之後運行。


該壓縮任務從深層存儲中獲取時間塊內的所有segment,並通過map/reduce作業重新創建segment並實現完美roll-up。然後,新的segment將由歷史節點加載和發布,以替換和取代原先roll-up不足的segment。在我們的例子中,通過使用額外壓縮任務,行數提高了2倍。


想要弄清給定時間塊內的所有事件在何時接收完畢並非易事。因為Kafka上可能有延遲到達的數據,或者索引器可能花時間將segment傳遞給歷史節點。為了解決這個問題,我們在運行壓縮之前會給予限制並執行檢查。


首先,太晚到達的數據會被丟棄。我們認為這種做法太過老派,在實時系統中毫無用處。這種做法會為延遲的數據劃定範圍。其次,壓縮任務的調度也會有延遲,這使得在正常流中的segment有足夠的時間被卸載到歷史節點上。最後,當給定時間塊的壓縮任務啟動時,它會查詢segment的元數據,以檢查是否有任何相關segment仍在寫入或傳遞。如果有,它將等待幾分鐘後再試,以確保壓縮作業處理完所有數據。


如果沒有採取這些措施,我們發現有時會丟失數據。壓縮開始時仍在寫入的segment將被新壓縮的segment覆蓋,這些segment具有更高版本,因此具有優先權。這種做法實際刪除了那些尚未完成傳遞部分所包含的數據。


查詢


Druid支持兩種查詢語言:Druid SQL查詢和本地查詢。在底層,Druid SQL查詢被轉換為本地查詢。本地查詢作為JSON提交到REST端點,這是我們的主要使用機制。


大多數對集群的查詢都是由自定義的內部工具(如儀錶板和警報系統)生成的。最初設計這些系統是用於我們內部開發和開源的時間序列資料庫Atlas。因此,這些工具使用Atlas Stack查詢語言。


為了加快Druid採用查詢,並允許重複使用現有工具,我們添加了一個轉換層,它接受Atlas查詢,將它們重寫為Druid查詢,發出查詢並將結果重新格式化為Atlas結果。這個抽象層不會改變現有工具的使用,並且使用戶在不創建額外的學習曲線的情況下仍可訪問Druid數據存儲中的數據。


調整比例


在調整集群節點配置的同時,我們高速運行一系列可重複和可預測的查詢,以獲得每個給定配置的響應時間和查詢吞吐量的基準。這些查詢旨在隔離集群的各個部分,以檢查查詢性能的改進或回歸/退化。


例如,我們對最新的數據執行目標查詢,以便只對中間管理器進行查詢。同樣地,對於持續時間較長的數據,我們只查詢較舊的數據,以確保只查詢歷史節點來測試緩存配置。並且再次使用高基數維度查詢該組,以檢查合併所受的影響。我們繼續調整和運行這些基準測試,直到對查詢性能滿意為止。


在這些測試中,我們發現調整緩衝區的大小、線程數、查詢隊列長度和分配給查詢緩存的內存對查詢性能有顯著影響。然而,壓縮作業的引入對查詢性能的影響更為顯著,它通過完美的再次roll-up,對未充分roll-up的segment重新壓縮,。


我們還發現在歷史節點上啟用緩存非常有用,而在代理節點上啟用緩存則沒有什麼影響,因此我們便不在代理節點上使用緩存。這也可能是我們的用例的問題,但我們所做的幾乎每一個查詢都會遺漏代理上的緩存,這可能是因為查詢通常包含最新的數據,而這些數據不會像往常一樣出現在任何緩存中。


摘要


來源:Pexels


在為用例和數據速率進行了多次調整後,Druid已經證明了它就像我們最初期望的那樣全能。


我們已經有了一個可靠好用的系統,但還有更多的工作要做。隨著接收量和速率不斷增加,查詢數量不斷增多,複雜性也不斷增強。因為這些詳細數據的價值需要更多的團隊才能實現,我們經常添加更多的度量和維度來推動系統更加努力工作。我們必須繼續監視和調整以保持查詢性能的正常。


目前,我們以每秒接收超過200萬個事件、查詢超過1.5萬億行的速度獲取用戶體驗服務的詳細信息。所有這些幫助我們在保持高質量的網飛體驗的同時實現不斷創新。


以上是網飛公司的一個經驗談,相信能給予其他公司一定啟發。

留言點贊關注

我們一起分享AI學習與發展的乾貨

如轉載,請後台留言,遵守轉載規範

文章來源: https://twgreatdaily.com/eKCia3EBrZ4kL1ViyvqN.html