爲什麼已有Elasticsearch,我們還要重造實時分析引擎AresDB?

作者 | Jian ShenZe WangDavid WangJeremy Shi, and Steven Chen

AresDB在Uber被廣泛使用,爲我們的實時數據分析儀表盤提供支持,使我們能夠針對業務的各個方面大規模製定數據驅動的決策。通過開源這個工具,我們希望社區中的其他人可以利用AresDB分析自己的數據。

在Uber,我們能夠利用實時分析技術獲得商業洞察力,提高運營效率,而且能基於數據驅動做決策,以改善Uber平臺上的體驗。例如,我們的運營團隊依靠數據來監控市場健康狀況,從而發現平臺上潛在的問題;基於機器學習模型的軟件則利用數據來預測乘客需求和司機數量;數據科學家利用數據來改進機器學習模型以便更好地進行預測。

過去,我們採用過許多第三方數據庫作爲我們的實時分析解決方案,但沒有一款數據庫既能夠實現所有功能,同時又滿足我們對於可伸縮性、性能、成本和運營的要求。

AresDB發佈於2018年11月,是一個開源的實時分析引擎,它使用了一種非同尋常的算能來源——圖形處理單元(GPU),使我們的分析能力得以大規模增長。作爲一種新興的實時分析工具,GPU技術在過去幾年中取得了顯著進步,非常適用於實時計算和並行數據處理。

在以下章節中,我們將詳述AresDB的設計,以及它如何使我們更有效地統一、簡化和改進Uber的實時分析數據庫解決方案。在閱讀這篇文章後,我們希望你能夠在自己的項目中嘗試一下AresDB,並根據你自己的分析需求發掘這個工具的價值!

首先送上AresDB開源項目地址:https://github.com/uber/aresdb

Uber的實時分析應用

數據分析對Uber業務的成功起到至關重要的作用,在其他功能中,這些分析被用於:

  • 構建儀表盤(DashBoard)來監控我們的業務指標

  • 根據我們收集的聚合指標制定自動化決策(比如行程定價欺詐檢測)

  • 構建即席查詢(ad hoc queries)以診斷並解決業務運營問題

我們可以將這些功能按照不同需求歸納如下:

儀表盤 決策系統 即席查詢
查詢模式 固定 固定 隨意
查詢QPS
查詢延遲
數據集 子集 子集 所有數據

儀表盤和決策系統利用實時分析系統,在較高QPS和較低延遲下,基於相對較小但非常有價值的數據子集(具有最大的數據時效性)構建相似查詢。

爲什麼我們需要再造分析引擎?

在Uber,實時分析最常被用來計算時間序列聚合,通過這些計算,能讓我們洞察用戶體驗從而相應地改進我們的服務,我們可以在一個時間範圍內針對任意過濾或連接(Join)後的數據請求特定維度(如按天、按小時、按城市ID和行程狀態)的指標。多年來,Uber已經部署過多種解決方案,通過不同的方式來解決這個問題。

我們嘗試過的第三方解決方案包括:

  • Apache Pinot:一個用Java編寫的開源分佈式分析數據庫,可用於大規模數據分析。Pinot的內部使用Lambda架構來批量或實時查詢列存儲中的數據,使用反向位圖索引進行過濾,並依賴星形樹(譯註:圖論術語Star-Tree,僅有一個頂點不是葉子的樹)緩存聚合結果。但是,它不支持基於鍵的去重、Upsert操作、Join操作,以及像地理空間過濾這樣的高級查詢特性。此外,Pinot作爲一個基於JVM的數據庫,執行查詢操作時會消耗較高的內存。

  • Elasticsearch:基於Apache Lucene構建,用於對存儲文檔和反向索引進行全文關鍵字搜索。Elasticsearch已被業界廣泛採用並擴展了對聚合的支持,在Uber它被用於實現各種流媒體分析的需求;它的反向索引支持過濾,但尚未優化基於時間範圍的存儲和過濾;它的記錄存儲格式爲JSON文檔,因而會額外增加存儲和查詢訪問的開銷。Elasticsearch與Pinot一樣,它也是一個基於JVM的數據庫,因此不支持Join操作,並且執行查詢操作需要更高的內存開銷。

雖然這些技術各具優勢,但都缺少關鍵功能來滿足我們的使用場景。我們需要跳出思維定式,更確切地說是要基於GPU來實現一個統一又精簡的解決方案。

利用GPU進行實時分析

爲了以較高幀率將視圖渲染爲逼真的圖像,GPU會高速並行處理大量的幾何圖形和像素。雖然在過去幾年裏,處理單元的時鐘頻率增長已趨近平穩,但是根據摩爾定律,這只不過是單純增加了芯片上的晶體管數量。因此,每秒以十億次(GFLOP/s)計的GPU計算速度正在迅速增加,下方圖1比較了在過去幾年中NVIDIA的GPU和Intel的CPU的理論計算速度。

image

圖1:近年來CPU和GPU單精度浮點性能的比較,圖片來自NVIDIA的CUDA C編程指南

在我們設計實時分析查詢引擎時,集成GPU處理便是我們的最佳選擇。在Uber,最常見的實時分析查詢場景需要處理數天的數據和數百萬到數十億條記錄,然後在短時間內過濾和聚合這些數據,通用GPU的並行處理模型完全適用於這種類型的計算任務,因爲他們能夠做到:

  • 非常快速地並行處理數據。

  • 提供更大的計算吞吐量(GFLOPS/s) ,非常適用於處理可並行化的繁重計算任務(每單位數據)。

  • 從計算到存儲(從ALU到GPU全局內存)的數據訪問吞吐量比中央處理器(CPU)更大,而具有I/O(內存)約束的並行任務需要大量數據,因此GPU是處理這種數據的理想選擇。

當我們決定使用基於GPU的分析數據庫之後,我們又評估了現有的一些能滿足我們需求的解決方案:

  • Kinetica:最初於2009年面向美國軍事和情報應用市場推出的基於GPU的分析引擎。儘管它的GPU技術在分析方面展示了巨大潛力,但我們發現,想要滿足我們的用例還需要更多關鍵特性,包括:Schema變更、部分插入或更新、數據壓縮、列級內存/磁盤備份保留配置,以及按地理空間連接。

  • OmniSci:一個基於SQL的開源查詢引擎,看起來似乎是一個很有前景的選擇,但在評估之後,我們意識到它缺少Uber使用場景中的關鍵特性,例如去重。雖然OminiSci在2017年開源了他們的項目,但在對他們這個基於C++的解決方案進行一些分析之後,我們得出的結論是,無論是給代碼庫貢獻代碼還是新建一個分支都行不通。

  • GPUQPCoGaDBGPUDBOcelotOmniDBVirginian:學術機構經常使用這些基於GPU的實時分析引擎,然而出於其學術目的,這些解決方案側重於開發算法和驗證設計概念,不能處理現實世界中的生產場景,我們的業務規模遠超這些方案的應用範圍,因此我們調低了對他們的期望。

總的來說,這些引擎證明了通過GPU技術處理數據具有巨大的優勢和潛力,同時也激勵我們自研基於GPU的實時分析解決方案來滿足Uber的需求,有了這些構思,我們構建了開源的AresDB。

AresDB架構概述

抽象來看,AresDB將大部分數據存儲在主機內存中,內存連接到CPU,由CPU處理數據輸入過程,如遇異常可通過磁盤恢復數據。查詢時,AresDB將數據從主機內存傳輸到GPU內存,以便在GPU上並行處理。如下方圖2所示,AresDB由一個內存存儲器、一個元數據存儲器和一個磁盤存儲器組成:

image

圖2:AresDB單實例架構包含內存和磁盤存儲以及元存儲

與大多數關係型數據庫管理系統(RDBMS)不同,AresDB中沒有數據庫作用域或Schema作用域,所有表都同屬於一個AresDB集羣或實例並具有相同作用域,以使用戶能夠直接引用的。用戶可以將自己的數據存儲爲事實表(Fact Table)和維度表(Dimension Table)。

事實表(Fact Table)

事實表存儲一個無限的時間序列事件流,用戶用事實表存儲實時發生的事件(Event)或事實(Fact),每個事件都關聯一個事件時間,該表通常通過事件時間來查詢。爲了說明用事實表存儲的信息類型,我們舉一個Uber打車的例子,其中每一次行程都是一個事件,叫車時間通常被定義爲事件時間,如果一個事件關聯了多個時間戳,那麼只有其中一個可以被指定爲事實表中顯示的事件時間。

維度表(Dimension Table)

維度表存儲實體(包括城市、客戶端和驅動程序等)的當前屬性。例如,用戶可以在維度表中存儲城市信息,如城市名稱、時區和國家。與事實表相比,維度表不會隨時間無限增長,大小總是有限的(例如,對Uber來說,城市表的上限是世界上實際的城市數量)。維度表不需要一個特別的時間列。

數據類型

下方的表格詳述了AresDB目前支持的數據類型:

數據類型 容量(字節) 詳細信息
Bool 1/8 布爾型數據,存儲爲一個二進制位
Int8, Uint8 1 整數類型,用戶可以根據字段的基數(Cardinality)和內存開銷來選擇
Int16, Uint16 2
Int32, Uint32 4
SmallEnum 1 字符串會被自動翻譯成枚舉類型,SmallEnum類型可以保存基數不高於256的字符串
BigEnum 2 與SmallEnum相似,基數上限是65535(譯註:2^16-1)
Float32 4 浮點數,我們支持Float32,未來若有需求再支持Float64
UUID 16 通用唯一識別碼
GeoPoint 4 地理點
GeoShape 可變長度 多邊形或多個多邊形

用AresDB存儲字符串會自動轉換爲枚舉類型(enum),這樣做可以提高存儲和查詢的效率,支持大小寫敏感的等式檢查,但不支持高級操作,例如:字符串拼接、字符串子串、glob匹配和正則表達式匹配,我們打算日後增加完整的字符串支持。

關鍵特性

AresDB的架構支持以下特性:

  • 壓縮基於列存儲的數據以提高存儲效率(僅存儲數據字節數這一項就可以減少內存開銷)和查詢效率(在查詢過程中從CPU內存到GPU內存傳輸的數據更少)

  • 通過主鍵去重實時執行Upsert操作,可在幾秒鐘內實現高數據準確性和接近實時的數據時效性

  • GPU驅動的查詢處理過程能夠高度並行化地處理數據,提供低查詢延遲(亞秒到秒)

列式存儲(Columnar Storage)

向量(Vector)

AresDB按照列式格式存儲所有數據,每列的值存儲爲列式值向量,每列值的有效性或無效性用一個二進制位表示,存儲在單獨的空向量中。

實時存儲(Live Store)

AresDB將未壓縮和未排序的列式數據(實時向量)儲存在實時存儲中,實時存儲中記錄的數據被實時分發到多個Batch的配置容量中,導入時創建新的Batch,記錄歸檔後清除舊的Batch。我們用主鍵索引來定位重複和更新的記錄,下面的圖3展示了我們組織實時記錄並通過主鍵值定位這些記錄的過程:

image

圖3:我們使用主鍵值來定位每個記錄的Batch和Batch的位置

每個Batch中每列的值都以列向量的形式存儲,每個值向量中值的有效性或無效性被存儲爲一個獨立的null向量,每個值的有效性以一個二進制位表示。在下面圖4的示例中,city_id列包含五個值:

image

圖4:我們在數據表中存儲未壓縮列的值(實際值)和null向量(有效性)

歸檔存儲(Archive Store)

對於已排序和已壓縮的成熟列數據(歸檔向量),AresDB會通過事實表的方式將其儲存在歸檔存儲中,歸檔存儲中的記錄也被分成了多個Batch。與實時Batch不同的是,歸檔Batch包含特殊的日期記錄,這些日期用協調世界時(UTC)表示,並使用與Unix初始時間的日期差作爲Batch的ID。

每條記錄都按照用戶配置的列順序排序,如圖5所示,我們先按city_id列排序,然後再按status列排。

image

圖5:我們先根據city_id列將所有行排序,然後按status列排序,最後使用遊程編碼壓縮每一列。每一列在被排序和壓縮後都會有一個計數向量。

設置按用戶配置的列排序順序的目的是:

  • 提前排序低基數的列以最大化壓縮效果,最大化壓縮能夠提高存儲效率(減少存儲數據所需的字節)和查詢效率(減少從CPU傳輸到GPU內存的字節)。

  • 支持對常見的等式過濾(如city_id=12)進行基於範圍的預過濾,預過濾能夠最小化從CPU內存到GPU內存需要傳輸的字節來降低開銷,從而最大化查詢效率。

某一列只有在用戶配置的排序順序中出現纔會被壓縮,我們不會嘗試壓縮高基數列,因爲壓縮高基數列所節省的存儲量可以忽略不計。

在排序之後,會用各種遊程編碼壓縮每個合格列的數據,除了值向量和null向量之外,我們還引入了計數向量來表示重複的相同值。

支持Upsert操作的實時導入

客戶端向AresDB導入數據,需要通過Ingestion HTTP API發起一個UpsertBatch的Post請求,UpsertBatch是一種自定義的序列化二進制格式,這種格式可以保證數據是隨機訪問的,同時也能最大限度地減少空間開銷。

當AresDB接收到一個導入數據的UpsertBatch請求時,首先會將它寫入重做日誌(Redo Log)的末尾,用於從異常中恢復。然後,AresDB會識別出事實表中的延遲記錄(late records),每當向實時存儲導入數據時都會跳過這些數據。如果記錄的事件時間早於歸檔的截止事件時間,則被認爲是“延遲”記錄。AresDB使用主鍵索引來定位Batch應該被放到哪個實時存儲中,如下面圖6所示,全新的記錄(記錄的主鍵值從未出現過)將被存入空白空間,而已有的記錄將直接更新:

image

圖6:在導入過程中,追加UpsertBatch到重做日誌後,將“延遲”記錄追加到回填隊列中,而在實時存儲中存入其他記錄。

歸檔(Archiving)

在導入數據時,要麼將新記錄追加到實時存儲中,要麼更新實時存儲中已有的記錄,抑或將數據追加到等待放入歸檔存儲的回填隊列中。

我們會定期運行一個名爲“歸檔”的計劃過程,將實時存儲中的新記錄(以前從未歸檔的記錄)合併到歸檔存儲中,歸檔只處理實時存儲中的記錄,其事件時間將位於舊的截止時間(上次歸檔過程的截止時間)和新的截止時間(基於表Schema中的歸檔延遲設置項中的新的截止時間)。

我們每天批量處理歸檔數據的時候,用記錄的事件時間來確定記錄應該被合併到哪個歸檔Batch。在合併過程中,歸檔不需要給主鍵值索引去重,因爲只有在新舊截止時間範圍內的記錄會被歸檔。

接下來的圖7展示了一條基於給定記錄時間的時間線:

image

圖7:通過事件時間和截止時間來確定哪些記錄是新的(實時的),哪些記錄是舊的(事件時間早歸檔截止時間)。

在這種情況下,兩次歸檔操作運行的間隔時間被稱作歸檔間隔(Archiving Interval),而在事件發生後直到可被歸檔前的這段持續時間被稱作歸檔延遲(Archiving Delay),二者都可以在AresDB的表Schema配置中定義。

回填(Backfill)

如圖7所示,事實表中的舊記錄(事件時間早於歸檔截止時間)被追加到回填隊列中,最終由回填進程處理。每當回填隊列的時間或大小達到閾值,就會觸發這個進程。與實時存儲的導入過程相比,回填操作是異步進行的,CPU和內存資源開銷相對更大,回填在以下場景中使用:

  • 處理偶爾很晚到達的數據

  • 藉助上游手工修復歷史數據

  • 爲最近添加的列填充歷史數據

與歸檔不同的是,回填是冪等的,並且需要基於主鍵值進行去重,被回填的數據最終對查詢可見。

回填隊列一直保留在內存中,並且預先配置了一定的空間。在大量回填載入期間,直到回填操作運行後清除了隊列纔會解除客戶端阻止運行的狀態。

查詢處理(Query Processing)

在當前的實現下,用戶需要使用Uber創建的Ares查詢語言(AQL)來對AresDB進行查詢。AQL是一種有效的時間序列分析查詢語言,不像其他類SQL語言一樣遵循SELECT FROM WHERE GROUP BY這種標準的SQL語法。相反,AQL是在結構化字段中指定的,可以與JSON、YAML和Go對象一起使用。例如,與

SELECT count(*) FROM trips GROUP BY city_id WHERE status = ‘completed’ AND request_at >= 1512000000

等效的JSON格式的AQL寫法如下:

{
 “table”: “trips”,
 “dimensions”: [
   {“sqlExpression”: “city_id”}
 ],
 “measures”: [
   {“sqlExpression”: “count(*)”}
 ],
;”>  “rowFilters”: [
   “status = ‘completed'”
 ],
 “timeFilter”: {
   “column”: “request_at”,
   “from”: “2 days ago”
 }
}

AQL採用JSON格式,爲儀表盤和決策系統開發者提供比SQL更好的程序化查詢體驗,因爲開發者可以用代碼輕鬆地編寫和操作查詢語句,並且無須擔心諸如SQL注入這樣的問題。JSON格式是Web瀏覽器、前端服務器和後端服務器這種經典架構的通用查詢格式,一直到數據庫(AresDB)都支持。此外,AQL還爲時間過濾和桶化(Bucketization)提供了方便的語法糖,並提供原生的時區支持。這門語言還支持隱式子查詢等功能,以避免常見的查詢錯誤並使後臺開發者可以方便地進行查詢分析和重寫。

儘管AQL提供了各種好處,但我們充分意識到大多數工程師更熟悉SQL,所以接下來,我們的研究方向就包括爲查詢操作提供SQL接口來增強AresDB的用戶體驗。

如下圖8描繪了AQL查詢的執行流程:

image

圖8:AresDB的查詢執行流程利用了我們自主開發的AQL查詢語言,可以快速高效地處理並檢索數據。

查詢編譯(Query Compilation)

AQL查詢會被編譯成內部的查詢上下文,過濾器、Dimension和Measurement(譯註:相當於關係型數據庫中的Table)中的表達式被解析成抽象語法樹(AST),以便後續通過GPU進行處理。

數據饋送(Data Feeding)

AresDB將歸檔數據發送至GPU進行並行處理前,會對數據進行預過濾以降低性能開銷。由於存檔數據是按照預先配置的列順序排列的,所以一些過濾器可能可以通過二分搜索定位相應的匹配範圍來利用此排序。特別是,所有首個X排序列上的相等過濾器和X+1排序列上的範圍過濾器(可選)都可以作爲預過濾處理,如圖9所示:

image

圖9:AresDB預過濾列數據,然後將其發送到GPU進行處理。

在預過濾後,只需將綠底色的值(滿足過濾條件的值)推送到GPU以進行並行處理,輸入數據被饋送到GPU,每次執行一個Batch(包括實時Batch和歸檔Batch)。

AresDB利用CUDA流(譯註:CUDA流表示GPU中的操作隊列)來饋送並執行流水數據。每次查詢交替使用兩個流,以便在處理過程中同步傳輸數據。下方圖10的時間線描繪了這一過程:

image

圖10:在AresDB中,兩個CUDA流交替傳輸和處理數據

查詢執行(Query Execution)

爲簡單起見,AresDB利用Thrust庫來實現查詢執行過程,該程序提供了經過微調的並行算法構建塊,以便在當前的查詢引擎中快速實現。

Thrust使用隨機訪問迭代器來訪問輸入和輸出向量數據,每個GPU線程將輸入迭代器取到其工作負載位置,讀取值並執行計算,然後將結果寫入到輸出迭代器上的對應位置。

AresDB遵循OOPK模型(每個內核一個操作符)來計算表達式。

下面的圖11是通過查詢編譯階段的維度表達式request_at – request_at % 86400生成的示例AST:

image

圖11:AresDB利用OOPK模型對表達式求值進行建模

在OOPK模型中,AresDB查詢引擎遍歷AST樹的每個葉子節點,併爲其父節點返回一個迭代器。在根節點也是葉子節點的情況下,直接在輸入迭代器上執行根操作。

在每個非根非葉節點(本示例中的取模運算),分配一個臨時的暫存空間向量來存儲request_at % 86400表達式產生的中間結果,利用Thrust在GPU上啓動一個內核函數來計算這個運算符的輸出。結果存儲在暫存空間迭代器中。

在根節點上,啓動內核函數的方式與非根非葉結點相同,並根據表達式的類型採取不同的輸出操作,具體如下:

  • 過濾操作,以減少輸入向量的基數

  • 將維度輸出寫入維度向量,以供後續聚合

  • 將度量輸出寫入度量向量,以供後續聚合

在表達式求值後,執行排序歸約操作以備最終聚合,在排序和歸約操作中,我們使用維度向量的值作爲排序和歸約的關鍵值,並使用度量向量的值作爲要聚合的值。通過這種方式,具有相同維度值的行將組合在一起並進行聚合。下面的圖12詳述了這個排序和歸約的過程:

image

圖12:在表達式求值之後,AresDB根據維度(鍵值)和度量(值)向量上的關鍵值對數據進行排序和歸約。

AresDB還支持以下高級查詢功能:

資源管理

AresDB作爲基於內存的數據庫,需要管理以下類型的內存使用:

分配者 管理模式
實時存儲向量(實時存儲列數據) C 跟蹤式
歸檔存儲向量(歸檔存儲列數據) C 管理式(加載和釋放)
主鍵索引(利用散列表刪除重複記錄) C 跟蹤式
回填隊列(存儲等待回填的“延遲”到達數據) Golang 跟蹤式
歸檔/回填過程臨時存儲(歸檔和回填過程彙總分配的臨時內存) C 跟蹤式
導入/查詢臨時存儲;進程開銷; 分配碎片化; Golang和C 靜態配置預估

當AresDB投入生產時,它會利用配置的總內存運算。此預算由所有六種內存類型共享,並且還應爲操作系統和其他進程留出充足的空間。此預算還包括靜態配置的開銷預估、服務器監控的實時數據存儲以及服務器可根據剩餘內存預算決定加載和釋放的歸檔數據。

下面的圖13描繪了AresDB主機的內存模型:

image

圖13:AresDB管理自己的內存使用情況,使其不超過配置的進程總預算。

AresDB允許用戶爲事實表在列級別配置預加載的日期和優先級,並且僅在預加載日期內預加載歸檔數據。非預加載的數據按需從磁盤中加載到內存。一旦內存佔滿,AresDB也可以將歸檔數據從主機內存中清除。AresDB的釋放策略基於預加載天數、列優先級、Batch日期和列的大小。

AresDB還管理多個GPU設備,建立設備資源模型(如GPU線程和設備內存),當處理查詢的時候跟蹤GPU內存的使用情況。AresDB通過設備管理器管理GPU設備,並將GPU設備資源劃分爲兩個維度:GPU線程和設備內存,當處理查詢時能夠跟蹤使用情況。在查詢編譯之後,AresDB允許用戶估計執行查詢所需的資源量。無論在哪臺設備上,只有滿足內存要求的設備才能夠啓動查詢,如果設備沒有足夠的內存,則查詢必須等待資源滿足後執行。目前,AresDB可以在同一GPU設備上同時運行一或多個查詢,只要該設備滿足所有資源要求。

在目前的實現中,AresDB不會在設備內存中緩存輸入數據,如果緩存下來,則可以在多個查詢中重用這些數據。AresDB的目標是支持針對不斷實時更新且難以正確緩存的數據集的查詢,我們打算在未來的迭代中實現具有數據緩存功能的GPU內存,這一步將有助於優化查詢性能。

使用案例:Uber的摘要儀表盤

在Uber,我們用AresDB構建儀表盤來實時提取業務洞察。AresDB扮演着存儲持續更新的新鮮原始數據和用GPU低功耗能力在幾秒鐘內計算針對這些數據的關鍵指標,從而用戶可以交互地使用儀表盤。例如,在數據存儲中具有較長壽命的匿名行程數據由多種服務進行更新,包括我們的調度、支付和評級系統。爲了有效利用形成數據,用戶將數據切分並劃分爲不同的維度,從而爲實時決策獲得洞察。

用AresDB打造的Uber摘要儀表盤是一個廣泛使用的分析儀表盤,公司的各個團隊會用它來檢索相關的產品指標,並實時響應來提高用戶體驗。

image

圖14:Uber摘要儀表盤按小時呈現的視圖用AresDB查看特定時間段的實時數據分析。

爲了構建上面的模擬儀表盤,我們對以下的表進行建模:

Trips(事實表)

trip_id request_at city_id status driver_id fare
1 1542058870 1 completed 2 8.5
2 1541977200 1 rejected 3 10.75

Cities(維度表)

city_id city_name timezone
1 San Francisco America/Los_Angeles
2 New York America/New_York

AresDB中的表Schema

要創建上述兩個建模表,我們首先需要在AresDB中按照以下Schema創建表:

Trips Cities
{ “name”: “trips”, “columns”: [   {     “name”: “request_at”,     “type”: “Uint32”,   },   {     “name”: “trip_id”,     “type”: “UUID”   },   {     “name”: “city_id”,     “type”: “Uint16”,   },   {     “name”: “status”,     “type”: “SmallEnum”,   },   {     “name”: “driver_id”,     “type”: “UUID”   },   {     “name”: “fare”,     “type”: “Float32”,   } ], “primaryKeyColumns”: [   1 ], “isFactTable”: true, “config”: {   “batchSize”: 2097152,   “archivingDelayMinutes”: 1440,   “archivingIntervalMinutes”: 180,  “recordRetentionInDays”: 30 }, “archivingSortColumns”: [2,3]} { “name”: “cities”, “columns”: [ {     “name”: “city_id”,    “type”: “Uint16”,   },   {     “name”: “city_name”,     “type”: “SmallEnum”   },   {     “name”: “timezone”,     “type”: “SmallEnum”,   } ], “primaryKeyColumns”: [   0 ], “isFactTable”: false, “config”: {   “batchSize”: 2097152 }}

如Schema中所述,Trips表被創建爲事實表,表示事實發生的行程事件,而Cities表被創建爲維度表,存儲有關城市的信息。

創建表之後,用戶可以利用AresDB客戶端庫從事件總線(如Apache Kafka)或流式或Batch平臺(如Apache Flink或Apache Spark)中獲取數據。

針對AresDB的示例查詢

在模擬儀表盤中,我們選擇兩個指標作爲示例:行程總價和活躍司機。在儀表盤中,用戶可以按城市指標過濾數據,例如:舊金山(San Francisco)。爲了在儀表盤上顯示過去24小時這兩個指標的時間序列,我們可以在AQL中運行以下查詢:

過去24小時中舊金山總行程費用(按小時) 過去24小時中舊金山活躍司機數(按小時)
{ “table”: “trips”, “joins”: [   {     “alias”: “cities”,     “name”: “cities”,     “conditions”: [       “cities.id = trips.city_id”     ]   } ], “dimensions”: [   {     “sqlExpression”: “request_at”,     “timeBucketizer”: “hour”   } ], “measures”: [   {     “sqlExpression”: “sum(fare)”   } ], “rowFilters”: [   “status = ‘completed’”,   “cities.city_name = ‘San Francisco’” ], “timeFilter”: {   “column”: “request_at”,   “from”: “24 hours ago” }, “timezone”: “America/Los_Angeles”} { “table”: “trips”, “joins”: [   {     “alias”: “cities”,     “name”: “cities”,     “conditions”: [       “cities.id = trips.city_id”     ]   } ], “dimensions”: [   {     “sqlExpression”: “request_at”,     “timeBucketizer”: “hour”   } ], “measures”: [   {     “sqlExpression”: “countDistinctHLL(driver_id)”   } ], “rowFilters”: [   “status = ‘completed’”,   “cities.city_name = ‘San Francisco’” ], “timeFilter”: {   “column”: “request_at”,   “from”: “24 hours ago” }, “timezone”: “America/Los_Angeles”}

查詢的示例結果:

上述模擬查詢將在以下時間序列結果中產生結果,這些結果可以很容易地繪製成時間序列圖,如下所示:

過去24小時中舊金山總行程費用(按小時) 過去24小時中舊金山活躍司機數(按小時)
{ “results”: [   {     “1547060400”: 1000.0,     “1547064000”: 1000.0,     “1547067600”: 1000.0,     “1547071200”: 1000.0,     “1547074800”: 1000.0,     …    } ]} { “results”: [   {     “1547060400”: 100,     “1547064000”: 100,     “1547067600”: 100,     “1547071200”: 100,     “1547074800”: 100,    …     } ]}

在上面的示例中,我們演示瞭如何利用AresDB在幾秒鐘內實時導入實時發生的原始事件,並立即針對數據發起任意的用戶查詢,從而在亞秒內計算指標。AresDB幫助工程師輕鬆構建數據產品來提取對企業至關重要的指標,員工或機器可以通過這些指標做出具有實時洞察能力的決策。

後續步驟

AresDB在Uber被廣泛使用,爲我們的實時數據分析儀表盤提供支持,使我們能夠針對業務的各個方面大規模製定數據驅動的決策。通過開源這個工具,我們希望社區中的其他人可以利用AresDB分析自己的數據。

未來,我們打算通過以下功能來賦能這個項目:

  • 分佈式設計:我們正在構建AresDB的分佈式設計,包括複製集(Replication)、分片(Sharding)管理以及Schema管理,從而提高AresDB的可伸縮性並降低運營成本。

  • 開發者支持和工具:自2018年11月開源AresDB以來,我們一直致力於構建更直觀的工具,重構代碼結構,豐富文檔,提高初次使用的體驗,使開發者能夠快速將AresDB集成到他們的分析工具棧中。

  • 擴展功能集:我們還計劃擴展查詢功能集,包括窗口函數(Window Function)和嵌套循環連接(Nested Loop Join)等功能,從而讓AresDB可以支持更多使用場景。

  • 查詢引擎優化:我們還將研究開發更先進的方法來優化查詢性能,例如:底層虛擬機(LLVM)和GPU內存緩存。

AresDB的開源協議是Apache,我們鼓勵你試用AresDB並加入我們的社區。

如果你對建立大規模實時數據分析技術感興趣,歡迎申請加入我們的團隊。

AresDB開源項目地址:https://github.com/uber/aresdb

鳴謝

特別感謝Kate Zhang、Jennifer Anderson、Nikhil Joshi、Abhi Khune、Shengyue Ji、Chinmay Soman、Xiang Fu、David Chen還有Li Ning,是你們讓這個項目取得了巨大的成功!

查看英文原文:

https://eng.uber.com/aresdb/

更多內容,請關注AI前線

image

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