1. 程式人生 > 實用技巧 >微博大資料即席查詢(OLAP)引擎實踐

微博大資料即席查詢(OLAP)引擎實踐

前言

適用於 即席查詢 場景的開源查詢引擎有很多,如:Elasticsearch、Druid、Presto、ClickHouse等;每種系統各有利弊,有的擅長檢索,有的擅長統計;實踐證明,All In One 是行不通的,最好的方式是選取若干個(考慮運維成本,建議 1 ~ 3 個),每個都對應著自身最具優勢的場景。

大多數的技術分享會從系統架構、功能擴充套件或效能優化角度進行討論,本文不涉及這些內容。本文以 指標多維統計查詢 為例,討論多個查詢引擎混合應用場景下的問題思考及相應的解決方案。

指標多維統計查詢

定義

什麼是 指標多維統計查詢 ?按指定的條件查詢指標值(一個或多個),條件包括:

  • 時間範圍(必選)
  • 時間粒度(必選)
  • 維度過濾條件(可選,類似SQL中的 Where
  • 維度分組條件(可選,類似SQL中的 GroupBy
  • 維度或指標排序條件(可選,類似SQL中的 OrderBy

注意,指標多維統計查詢 涉及4個關鍵元素:時間範圍時間粒度維度指標(詳情見後)。

示例

  • 查詢微博視訊最近7天範圍內,每天的PSR1(秒開率)是多少?
  • 查詢微博視訊最近1個月內,每天的移動4G場景下各個運營商的卡頓率是多少?

查詢引擎混合應用

多個查詢引擎混合應用的場景下,會遇到哪些問題呢?我們主要從 工程角度業務角度 來看下。

工程角度

每個查詢引擎都有自己特有的API(也包含 SQL,雖然開源社群大多致力於提供標準的SQL支援,但實際落地情況並不盡如人意),指標查詢需要 直接連線

查詢引擎並使用相應的API進行指標值計算,開發人員和分析人員需要在多個不同的API之間來回切換,學習及維護成本較高;查詢引擎版本升級或引入新的引擎會增加這些成本,系統整體的靈活性及可擴充套件性較差。

業務角度

多個查詢引擎混合使用的場景下,業務指標會分散至不同的引擎中,會遇到以下問題(複雜度依次增加):

  • 某個業務指標的查詢需要使用相應的查詢引擎;
  • 某個業務指標的查詢需要使用相應的查詢引擎,且計算規則較為複雜;
  • 某個業務指標需要依賴多個查詢引擎的聯合查詢;
  • 某個業務指標需要依賴多個查詢引擎的聯合查詢,且計算規則較為複雜;

可以看出,我們需要維護指標與查詢引擎的 對應關係,以及指標的 計算規則,實際中很大程度上依賴於 檔案

(Wiki)。資料應用服務中常見的資料視覺化、報表、資料介面,這些服務的實現模組均需要直接耦合這些對應關係及計算規則,而且需要重複多次。如果某個指標的對應關係或計算規則發生變化,相應的檔案及相關的服務 必須 全部更新,才能保證資料一致性。

指標眾多的場景下,上述提及的成本及複雜度會大幅增加,有過類似服務經驗的同學應該深有體會。

補充

對應關係 與 計算規則 可能比較抽象,舉一個具體的例子。假設 MySQL 資料庫某資料表 DEFAULT.TEST_TABLE 的中有 2 列:A 和 B,指標 M 的值可以通過SQL語句查詢得出,如:

SELECT
A / B AS M
FROM
DEFAULT.TEST_TABLE

資料應用服務(資料視覺化、報表或資料介面)查詢這個指標資料時,需要知道去什麼地方查詢,以及具體如何查詢;上述示例需要通過遠端方式連線MySQL,連線時使用到的協議型別(MySQL)、IP地址、埠號等資訊就是 對應關係;查詢時需要使用的SQL語句就是 計算規則

指標庫

針對指標眾多,指標計算規則及相關資訊不易維護的問題,我們提出 指標庫 用於規範化指標管理。什麼是 指標庫

對於某一個指標而言,可以從不同的時間範圍、時間粒度及維度視角進行統計查詢。換句話說,對於某一個指標而言,我們需要知道可以從哪些時間範圍、哪些時間粒度、哪些維度視角進行統計查詢,以及指標的計算規則是什麼。一般情況下,同屬於一個 業務 的指標會共享相同的時間範圍、時間粒度及維度項,我們將類似這樣的業務統稱為 主題。每一個主題對應著一個業務或業務中的某個細分場景,如:

  • 移動端視訊上傳效能
  • PC端視訊上傳效能
  • 移動端視訊劫持效能
  • CDN服務質量分析

 

每一個 主題 需要包含以下資訊:

  • 主題名稱

    顧名思義,業務名稱或業務中的某個細分場景名稱。

     

  • 時間範圍

    可支援的資料查詢時間範圍([起始時間,結束時間]);相當於資料的儲存週期,過期資料會被清除。

     

  • 時間粒度

    可支援的資料查詢最小時間粒度,相當於資料的聚合程度,如:1 秒,5 分鐘,1 小時 或 1 天 之類的。

    假設時間粒度是 5 分鐘,表示最小可以支援 每5分鐘 一個資料點的查詢請求,同時也可以支援 每1小時 或 每1天 之類的查詢請求,但不可以支援 每1秒 或 每1分鐘 之類的查詢請求。

     

  • 維度

    可以支援的資料查詢視角,通常會有多個,如:省份、運營商、裝置 之類的。以 運營商 為例,表示可以檢視指定運營商或各個運營商的資料。

     

  • 維度值

    維度可以支援的查詢值,通常會有多個。以維度 運營商 為例,維度值會有移動、聯通、電信之類的。

     

  • 指標

    可以支援查詢的指標,通常會有多個,如:秒開率、卡頓率 之類的。

     

  • 指標計算規則

    某個指標的計算公式。以指標 卡頓率 為例,計算公式:卡頓次數 / 總次數。

 

若干個 主題集合,我們稱之為 指標庫指標庫 的內容可以按照上述描述的格式記錄到專用的檔案中,統一更新發版,用於公司內部資料共享。

查詢語言

資料應用服務(資料視覺化、報表、資料介面)的核心邏輯是查詢指標資料,並不是使用 對應關係 和 計算規則 完成指標計算,且存在重複直接耦合的問題。為此,我們在 指標庫 的基礎之上,針對 指標多維統計查詢 場景設計了一種領域特定語言(DSL)。也就是說,資料應用服務查詢指標資料時直接使用統一的查詢語言,不再存在直接耦合 對應關係 和 計算規則 的情況,後續 對應關係(查詢引擎升級或遷移)或 計算規則 的 變更 對於資料應用服務也是 透明 的。

查詢語言是基於 JSON 實現的。

getTopics

查詢指標庫中有哪些主題(多個)。

 {
"type": "getTopics"
}

getDimentions

查詢指定主題中有哪些維度項(多個)。

 {
"type": "getDimentions",
"topic": "..."
}

其中,topic 為主題名稱。

getDimentionValues

查詢指定主題、指定維度中有哪些維度值(多個)。

 {
"type": "getDimentionValues",
"topic": "...",
"dimension": "..."
}

其中,topic 用於指定主題名稱, dimension 用於指定維度名稱。

getMetrics

查詢指定主題中有哪些指標項(多個)。

 {
"type": "getMetrics",
"topic": "..."
}

其中,topic 用於指定主題名稱。

query

查詢(指標資料),是查詢語言中最為複雜的型別。

 {
"type": "query",
"topic": "...",
"interval": {...},
"granularity": {...},
"metric": "...",
"where": {...},
"groups": [...],
"having": {...},
"orders": [...],
"limit": ...
}

topic

主題名稱(必填項)。

interval

時間範圍(必填項)。

 {
"start": "...",
"end": "..."
}

其中,start/end 均為閉區間,格式:yyyy-MM-dd HH:mm:ss。

granularity

時間粒度,預設為無窮大。

 {
"data": ...,
"unit": "..."
}

其中,data 為數值(整型),unit 為單位,如:s(秒)、m(分鐘)、h(小時)、d(天)、w(周)、M(月)、y(年)。

metric

指標名稱(必填項)。

where

維度過濾,對應於SQL語句中的 Where,預設為空,支援比較表示式與邏輯表示式。

比較表示式

比較表示式用於表示數值比較、包含或不包含,以及正則匹配。

eq/ne/gt/lt/ge/le
 {
"operator": "...",
"name": "...",
"value": "..."
}

其中,operator 為 eq(等於)、ne(不等於)、gt(大於)、lt(小於)、ge(大於或等於)或 le(小於或等於),name 為維度名稱,value 為維度值。

in
 {
"operator": "in",
"name": "...",
"values": [...]
}

其中,operator 為 in(包含),name 為維度名稱,values 為維度值列表(JSON陣列)。

regex
  {
"operator": "regex",
"name": "...",
"pattern": "..."
}

其中,operator 為 regex(正則匹配),name 為維度名稱,pattern 為正則表示式。

邏輯表示式

邏輯表示式用於表示一個或多個表示式之間的 與(And)、或(Or)、非(Not)關係。

and
 {
"operator": "and",
"filters": [...]
}

其中,operator 為 and(與),filters 為兩個或兩個以上的比較表示式或邏輯表示式。

or
 {
"operator": "or",
"filters": [...]
}

其中,operator 為 or(或),filters 為兩個或兩個以上的比較表示式或邏輯表示式。

not
 {
"operator": "not",
"filter": {...}
}

其中,operator 為 not(非),filter 為一個比較表示式或邏輯表示式。

groups

維度分組,類似於SQL語句中的 GroupBy,預設為空,可以指定多個維度項(JSON陣列)。

 [
"...",
...
]

having

分組過濾,類似於SQL語句中的 Having,預設為空,語法同 where。分組過濾表示式中的 name 可以是維度名稱,也可以是指標名稱。

orders

排序,類似於SQL語句中的 OrderBy,預設為空,可以指定多個排序項。

 [
{
"name": "...",
"sort": "..."
},
...
]

其中,name 為維度名稱或指標名稱,sort 為 asc(升序)或 desc(降序)。

limit

限制結果集行數,類似於SQL語句中的 Limit,預設為空。

示例參考

查詢指標

主題:CDN服務質量分析;

時間範圍:["2020-03-16 00:00:00", "2020-03-16 23:59:59"];

時間粒度:1小時;

指標:下載速度(download_speed_avg);

維度過濾:域名(domain)包含有字串“weibo”;

維度分組:運營商(isp);

分組過濾:下載速度(download_speed_avg)大於2000 Kb/s;

排序:下載速度降序(download_speed_avg);

結果集:最多100行;

 

查詢語言

 {
"type": "query",
"topic": "CDN服務質量分析",
"interval": {
"start": "2020-03-16 00:00:00",
"end": "2020-03-16 23:59:59"
},
"granularity": {
"data": 1,
"unit": "h"
},
"metric": "download_speed_avg",
"where": {
"operator": "regex",
"name": "domain",
"pattern": "^.*weibo.*$"
},
"groups": [
"isp"
],
"having": {
"operator": "gt",
"name": "download_speed_avg",
"value": 2000
},
"orders": [
{
"name": "download_speed_avg",
"sort": "desc"
}
],
"limit": 100
}

查詢代理

查詢語言只是一種描述性的協議規範,並不能完成實際的查詢過程。我們需要一種 服務,可以接收並執行這種查詢語言,過程如下:

  1. 解析查詢語言,獲取需要查詢的指標;
  2. 查詢指標對應的查詢引擎及計算規則;
  3. 使用查詢語言設定的查詢條件,連線查詢引擎,根據計算規則呼叫API,執行指標的實際計算過程;
  4. 返回結果。

這就是我們即將要重點介紹的 查詢代理

客戶端

AnalysisQl 本質是一套Java API,需要嵌入到Web或RPC容器中使用。

DefaultConnector connector = new DefaultConnector();

...

AnalysisQl analysisQl = new AnalysisQl(connector);

AnalysisQl 的實現是執行緒安全的,一個Web或RPC容器程式中建立一個 AnalysisQl 例項即可,例項建立完成即表示查詢代理服務啟動完成。

查詢語言的執行都需要通過 AnalysisQl.request 來完成,如下:

Response response = analysisQl.request(dsl);

相當於,AnalysisQl 是查詢代理的客戶端。

 

程式碼參考

https://github.com/weibodip/analysisql/blob/master/core/src/main/java/com/weibo/dip/analysisql/AnalysisQl.java

解析器

查詢語言是基於 JSON 實現的,不方便直接在Java語言中使用,需要將其 解析 成相應的 JavaBean,如下:

    Parser parser = new Parser(connector);

    Request request = parser.parse(dsl);

Parser 是解析器,Request 是查詢型別的頂層抽象類,查詢語言中的每一種查詢型別都有其對應的實現類:

  • GetTopicsRequest(getTopics)
  • GetDimensionsRequest(getDimentions)
  • GetDimensionValuesRequest(getDimentionValues)
  • GetMetricsRequest(getMetrics)
  • QueryRequest(query)

 

程式碼參考

https://github.com/weibodip/analysisql/blob/master/core/src/main/java/com/weibo/dip/analysisql/dsl/request/GetTopicsRequest.java

https://github.com/weibodip/analysisql/blob/master/core/src/main/java/com/weibo/dip/analysisql/dsl/request/GetDimensionsRequest.java

https://github.com/weibodip/analysisql/blob/master/core/src/main/java/com/weibo/dip/analysisql/dsl/request/GetDimensionValuesRequest.java

https://github.com/weibodip/analysisql/blob/master/core/src/main/java/com/weibo/dip/analysisql/dsl/request/GetMetricsRequest.java

https://github.com/weibodip/analysisql/blob/master/core/src/main/java/com/weibo/dip/analysisql/dsl/request/QueryRequest.java

其中,QueryRequest 關於 Filter(過濾)的實現較為有意思,有興趣的同學可以參考 https://github.com/weibodip/analysisql/tree/master/core/src/main/java/com/weibo/dip/analysisql/dsl/filter。

聯結器

聯結器用於定義查詢代理支援的介面協議,對應著查詢語言的查詢型別:

public interface Connector {
Response getTopics(GetTopicsRequest request); Response getDimensions(GetDimensionsRequest request); Response getDimensionValues(GetDimensionValuesRequest request); Response getMetrics(GetMetricsRequest request); Response query(QueryRequest request);
}

 

為什麼需要設計聯結器?

理論上,查詢語言是標準的,但查詢代理的實現可以是多種多樣的。類似於 Java JDBC,統一抽象資料庫的操作介面,連線資料庫時需要使用相應型別的驅動。我們使用聯結器,用於橋接查詢客戶端與查詢代理的具體實現,保證系統的可擴充套件性;同時提供系統預設的聯結器 DefaultConnector,滿足大部分場景的使用需求。

每一種查詢型別應呼叫聯結器的哪一個介面,是通過 Request.type 判斷的,如下:

   switch (request.getType()) {
case Request.GET_TOPICS:
return connector.getTopics((GetTopicsRequest) request); case Request.GET_DIMENSIONS:
return connector.getDimensions((GetDimensionsRequest) request); case Request.GET_DIMENSION_VALUES:
return connector.getDimensionValues((GetDimensionValuesRequest) request); case Request.GET_METRICS:
return connector.getMetrics((GetMetricsRequest) request); case Request.QUERY:
return connector.query((QueryRequest) request);
default:
throw new UnsupportedOperationException();
}

 

程式碼參考

https://github.com/weibodip/analysisql/blob/master/core/src/main/java/com/weibo/dip/analysisql/connector/Connector.java

https://github.com/weibodip/analysisql/blob/master/view/src/main/java/com/weibo/dip/analysis/view/DefaultConnector.java

資料檢視

資料檢視 View 是一個抽象類,對應於指標庫中的主題,每一個主題均需要提供相應的資料檢視實現類,並於查詢代理初始化之前註冊到聯結器中。實現時需要設定以下資訊:

protected String topic;

protected List<Dimension> dimensions;

protected List<Metric> metrics;

protected List<Table> tables;

protected PolicyRouter router;

 

topic:主題名稱;

dimensions:維度項,每一個 Dimension 表示一個維度;

metrics:指標項,每一個 Metric 表示一個維度;

tables:資料表,每一個 Table 表示一張資料表,詳細見後;

router:策略路由,詳情見後;

 

CDN服務質量分析 為例,資料檢視實現類如下:

public class CdnServiceQualityView extends DefaultView {

  /** Initialize a instance. */
public CdnServiceQualityView() {
super(
"CDN服務質量分析", // 主題名稱
...); addDimension("country", "國家"); // 新增維度項
addDimension("business", "業務");
addDimension("city", "城市"); addMetric("error_num", "錯誤量(萬)"); // 新增指標項
addMetric("request_num", "訪問量(億)");
addMetric("download_time_avg", "下載時間(s)"); addTable(new CdnServiceQualityTable(this)); // 新增資料表
addTable(new CdnNetflowBillingTable(this));
}
}

:DefaultView 是 View 的預設實現類。

CdnServiceQualityView 構建函式中直接完成主題、維度項、指標項及資料表的設定,也可以在 CdnServiceQualityView 例項建立完成之後,通過相應的例項方法(addDimension/addMetric/addTable)設定。

然後,註冊到聯結器:

connector.register(new CdnServiceQualityView());

聯結器中儲存著所有已註冊的資料主題:

  protected Map<String, Metadata> metadatas = new HashMap<>();

  public void register(Metadata metadata) {
metadatas.put(metadata.getTopic(), metadata);
}

:Metadata 是 View 的抽象父類。

聯結器中多數協議方法的實現本質就是根據主題名稱(topic)查詢到相應的資料主題例項,呼叫其例項方法完成查詢請求。

 

為什麼需要設計資料檢視?

 

假設資料表 A 以 5分鐘 的時間粒度,儲存著 m 個維度、n 個指標的資料,每5分鐘的資料量(即:資料行數)的最大值理論上取決於各個維度的維度值數目;如果 m 個維度的維度值數目依次為 N1、N2、...、Nm,那麼每5分鐘資料量為 N1 * N2 * ... * Nm(即多個維度的維度值數目的笛卡爾乘積);每日的資料最為 288 * N1 * N2 * ... * Nm。

我們列舉幾個常用的查詢:

  1. 查詢最近 1小時內,以 5分鐘 為時間粒度,某個維度或某幾個維度組合的指標資料;
  2. 查詢最近 7天內,以 1小時 為時間粒度,某個維度或某幾個維度組合的指標資料;
  3. 查詢最近 1月內,以 1天 為時間粒度,某個維度或某幾個維度組合的指標資料;

可以看出,資料表 A 是可以完全支撐上述查詢需求的。那麼,問題在於哪裡?問題在於查詢的 響應時間,響應時間很大程度上取決於查詢時需要掃描的 資料量。在我們的場景中,很多業務按資料表 A 的方式儲存資料,每天的資料量會達到數百億級別,較長時間範圍內(跨天/跨周/跨月)的指標資料查詢,響應是比較緩慢的。

我們經常使用的優化方案:

  • 建立資料表 B,以 5分鐘 為時間粒度,儲存資料表 A 中部分維度的指標資料;

    維度數目的減少,表示著資料表 B 相對於 資料表 A 的資料是減少的。以上述 查詢1 為例,如果需要查詢的維度正好存在於資料表 B,那麼使用資料表 B 相比於 資料表 A,需要掃描的資料量更少,響應時間更快。

     

  • 建立資料表 C,以 1天 為時間粒度,儲存資料表 A 聚合之合的指標資料;

    時間粒度的增大,表示著資料表 C 相對於 資料表 A 的資料量是減少的。以上述 查詢3 為例,使用資料表 C 相比於 資料表 A,需要掃描的資料量更少,響應時間更快。

 

擴充套件一下思路:可以根據實際的業務場景,在基礎資料表(如:資料表A)之上,有效組合不同的時間粒度、不同的維度組合額外創建出若干張資料表,用於優化查詢的響應時間。

也就是說,指標庫中的某一個主題可以對應著多張資料表,這些資料表可以有不同的時間粒度、不同的維度組合,甚至不同的指標項,儲存於不同的查詢引擎,或者被多個主題所共享。這樣的 自由組合 從工程角度看是非常靈活的;但從資料服務角度看,會帶來2個問題:

  • 多張資料表之間的資訊是 混亂 的,缺乏一致的資料口徑;
  • 查詢指標資料時應該使用哪張資料表?

因此,我們設計 資料檢視,顯示宣告包含的維度、指標資訊,以及相關的資料表(參考資料檢視定義)。資料檢視 如何解決查詢指標時使用的資料表問題,參見後文。

程式碼參考

https://github.com/weibodip/analysisql/blob/master/view/src/main/java/com/weibo/dip/analysis/view/View.java

資料表

如前文所述,同一個主題(資料檢視)中可以包含多張資料表,且這些資料表之間可能會有不同的時間粒度、維度項、指標項等,資料表(Table)的設計需要能夠明確顯示宣告這些資訊。

protected String topic;

protected List<Dimension> dimensions;

protected List<Metric> metrics;

protected Map<String, MetricCalculator> calculators;

private Granularity granularity;
private int period;
private int delay;

 

topic:資料表屬於的主題名稱;

dimensions:資料表支援的維度項;

metrics:資料表支援的指標項;

calculators:資料表支援的指標項對應的 指標計算器 (參見後文);

granularity:資料表儲存資料的時間粒度;

period:資料表儲存資料的週期,也就是資料表可以查詢的時間範圍;

delay:資料表資料的延遲時間,也就是說相對於當前時間,這個延遲範圍內的資料是查詢不到的;

CDN服務質量分析 的資料表為例,資料表實現類如下:

public class CdnServiceQualityTable extends Table {

  public CdnServiceQualityTable(View view) {
super(view, "clickhouse-cdn-all_cdn_staging", new Granularity(5, Unit.m), 103680, 36); // 設定時間粒度、儲存週期及延遲時間 addDimension("country"); // 宣告支援的維度項
addDimension("business");
addDimension("city"); addCalculator("error_num", new HubbleClickHouseCalculator("analysisql/cdn/cdn-error_num.sql")); // 宣告支援的指標項的同時,提供指標相應的指標計算器
addCalculator(
"request_num", new HubbleClickHouseCalculator("analysisql/cdn/cdn-request_num.sql"));
addCalculator(
"download_time_avg",
new HubbleClickHouseCalculator("analysisql/cdn/cdn-download_time_avg.sql"));
}
}

 

程式碼參考

https://github.com/weibodip/analysisql/blob/master/view/src/main/java/com/weibo/dip/analysis/view/Table.java

策略路由

資料檢視(View)中包含多張資料表(Table),查詢指標資料時,我們需要根據一定的 規則 從這些資料表中選取出 查詢代價最小(響應時間最快) 的資料表用於指標計算,規則 的具體實現就是 策略路由

過濾

過濾就是 排除 不能支援查詢的資料表,考慮以下4個因素:

  1. 指標

    資料表必須包含查詢的指標項;

  2. 維度

    資料表必須包含查詢涉及的維度項(多個);

  3. 時間粒度

    資料表資料儲存的時間粒度必須小於或等於查詢指定的時間粒度,否則無法聚合資料;

  4. 時間範圍

    資料表在查詢指定的時間範圍記憶體在資料(根據資料表儲存週期及延遲時間計算);

滿足上述4個條件的資料表即可以進入下一階段。

排序

排序規則:

  • 資料表的時間粒度越大,查詢代價越小;
  • 資料表的維度數目越少,查詢代價越小;

經過過濾、排序之後,位於第1個位置的資料表即是 最優 的資料表。目前,策略路由(PolicyRouter)的實現是直接內建於資料檢視,尚不支援自定義擴充套件。

 

程式碼參考

https://github.com/weibodip/analysisql/blob/master/view/src/main/java/com/weibo/dip/analysis/view/PolicyRouter.java

指標計算器

指標計算器(MetricCalculator)用於根據查詢請求完成指標的計算。也就是說,前文中提及的 對應關係 和 計算規則 均包含在指標計算器中,指標計算器需要連線查詢引擎,使用相應的API或SQL,根據計算規則完成指標的計算過程,並返回結果。資料表的各個指標均需要提供相應的指標計算器實現器,並註冊到資料表中。

public interface MetricCalculator {
List<Row> calculate(QueryRequest request) throws Exception;
}

考慮到常用的查詢引擎(如:MySQL、Presto、ClickHouse)大多支援以 SQL 的方式查詢資料,為加速指標計算器的實現效率,系統支援以 SQL模板 的方式定義指標的計算規則,並提供多種 模板引擎, 可將 查詢條件 與 SQL模板 整合轉換特定查詢引擎的SQL語句(不同查詢引擎的SQL實現存在一定差異)。

以 ClickHouse 為例,某指標計算規則可以這樣定義:

SELECT
$COLUMNS, SUM(netflow) * 8 / 1000 / 1000 / 1000 AS $METRIC
FROM
cdn.edge_scheduler_bandwith
WHERE
$WHERE
GROUP BY
$GROUPS
HAVING
$HAVING
AND isNaN($METRIC) == 0
AND isInfinite($METRIC) == 0
AND $METRIC >= 0.0
ORDER BY
$ORDERS
LIMIT
$LIMIT

 

$COLUMNS$WHERE$GROUPBY$HAVING$ORDERBY$LIMIT 均是 模板引擎 支援的自定義變數(不同的模板引擎支援的變數種類存在一定差異)。模板引擎 會將查詢語言中的過濾表示式、維度分組、分組過濾、排序及結果集行數轉換為這些自定義變更對應的值,並輸出完整的SQL語句。

指標計算器直接使用轉換之後的SQL語句,連線查詢引擎查詢資料即可。

綜上所述,查詢代理核心工作流程如下:

  1. 解析查詢語言;
  2. 使用主題名稱查詢資料檢視;
  3. 使用策略路由選取最優資料表;
  4. 使用指標名稱查詢最優資料表的指標計算器;
  5. 計算指標並返回結果;

資料應用服務僅需要知道查詢代理服務地址(域名)、埠號,使用查詢語言查詢需要的指標資料即可。

結語

本文介紹的指標庫、查詢語言(DSL)、查詢代理是我們團隊自主研發的OLAP服務,在微博視訊效能資料分析中取得很好地應用效果。通過技術優化的方式,在有限的計算資源範圍內得到不錯的效能表現,大幅降低資料介面、視覺化及監控服務的開發成本。 同時,我們團隊也在準備專案開源(https://github.com/weibodip/analysisql )的準備工作,有興趣的同學可關注交流。