ClickHouse 在京東能源管理平臺的應用實踐

作者:樊思國

ClickHouse 是一款面向大數據場景下的 OLAP 數據庫,相比於傳統的基於 Hadoop 生態圈的 OLAP 大數據分析系統,ClickHouse 具有極致的查詢性能、輕量級的架構設計及維護簡單等優勢。目前社區活躍度高,業界應用實踐日趨廣泛。

一、業務介紹

京東能源管理平臺是京東科技 IoT 產品部面向政企客戶推出的一款利用物聯網、大數據和 AI 技術實現用能企事業單位對能源大數據進行採集、監測、分析和告警的能耗分析產品,旨在幫助客戶實現節能減排,降低單位產品能耗。

能源指標包括用電量、用水量和用天然氣量,維度有時間維度(年、月、周、日、時)、廠家、車間、生產線類型、生產線、設備。針對這些指標和維度,提供了實時的數據多維分析與診斷服務。

二、技術選型

對於數據指標的多維度分析場景,上世紀業界就提出了 BI(商業智能)的概念。相較於 OLTP(聯機事務)系統,業界把此類面向 BI 的系統統稱爲 OLAP(聯機分析) 系統。伴隨着計算機軟件技術的發展、從單機工具的少量數據分析(如 Excel),到中等規模數據通過分析型關係數據庫構建(如微軟的 SSAS)的 OLAP,再到今日的大數據時代,海量數據的實時 OLAP 分析引擎,技術上的推陳出新,工具系統上百花齊放百家爭鳴,各有優勢,但大體上可以將它們從架構模式上劃分爲兩大類:

**1. MPP 架構。**MPP 架構特點是服務將接收到的查詢請求發送到每個計算節點,待計算節點計算完成後,通過一個節點將最終結果彙總在一起得到最終結果。典型實現如 Presto、Impala、SparkSQL、Drill 等。MPP 架構的特點是支持靈活的數據模型,要達到較高性能對內存開銷大。

**2. 預計算系統。**預計算的核心思想是利用空間換時間,通過深入業務理解,將需要查詢的數據指標和維度組合進行預處理,將計算好的結果存入數據庫並建立對應索引,實現查詢加速。典型實現如 Kylin、Druid。預計算系統特點是性能較高,但靈活性較差,一般對數據模型調整會涉及到歷史數據的重跑,維護困難。

從上表可知,目前業界還沒有一個 OLAP 引擎能夠同時兼顧性能和靈活性的要求,京東能源管理平臺在做技術選型的時候,綜合考慮了模型的靈活性、部署的難易程度、開發成本、可維護性以及是否適合雲端部署等因素,最終決定使用基於 MPP 架構的 ClickHouse 作爲我們的 OLAP 引擎。

三、ClickHouse 的應用

1、系統架構

京東能源管理平臺主要是對各種表計(水錶、電錶、天然氣表等)設備上報的計數進行多維度分析統計、AI 診斷和出具能耗報表等。表計的原始數據通常都是累計值,如電量度數就是一個從電錶安裝以來,所有耗電量的一個累計。因此,我們在數據接入前會引入一個差分器對數據進行預處理,使得進入 ClickHouse 的指標數據變成可直接累加的指標,方便利用 SQL 對接 ClickHouse 實現多維的查詢服務。架構圖如下:

說明:

2、ClickHouse 應用

通過上面的架構圖可以看出,能源平臺採用 ClickHouse 作爲 OLAP 引擎提供多維查詢服務。下面重點從數據的接入、存儲以及通用化接口設計方面談一談

ClickHouse 的應用:

ClickHouse 基於 kafka 引擎表的數據接入可以看做是一個典型的 ETL 過程,數據的抽取(Extract)是通過建立一張 kafka 引擎表,產生消費端訂閱 kafka topic 實現;數據的轉換(Transform)通過物化視圖實現;數據最終加載(Load)進 MergeTree 表,實現實際數據存儲。

創建 Kafka 表示例:

CREATE TABLE statistics_kafka ON CLUSTER '{cluster}' (
  timestamp UInt64,
  level String,
  message String
 ) ENGINE = Kafka SETTINGS kafka_broker_list = 'kafka.jd.com:9092',
                         kafka_topic_list = 'statistics',
                         kafka_group_name = 'gp-st',
                         kafka_format = 'JSONEachRow',
                         kafka_skip_broken_messages = 1, 
                         kafka_num_consumers = 3;

創建物化視圖示例:

CREATE MATERIALIZED VIEW statistics_view ON CLUSTER '{cluster}' TO statistics_replica AS
SELECT timestamp,
       level,
       message
FROM statistics_kafka;

創建 MergeTree 引擎表示例:

1CREATE TABLE statistics_replica ON CLUSTER '{cluster}'{
2    timestamp UInt64,
3    dt String,
4    deviceId String,
5    level String,
6     message String
7} ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/statistics_replica','{replica}')
8PARTITION BY dt
9ORDER BY (dt,deviceId,level);
  1. ClickHouse 表類型

    本地表:實際數據存儲的表,如上示例表 statistics_replica。

    分佈式表:一個邏輯上的表, 可以理解爲數據庫中的視圖, 一般查詢都查詢分佈式表. 分佈式表引擎會將我們的查詢請求路由本地表進行查詢, 然後進行彙總最終返回給用戶。創建分佈式表示例:

    1CREATE TABLE statistics ON CLUSTER '{cluster}' AS statistics_replica
    2ENGINE = Distributed(ck_cluster_1,test,events_local,rand());
  2. Replication 和 Sharding

    Replication 是 ClickHouse 提供的副本機制,對於 Replicated MergeTree 系列複製表,可以設置每個表有多份完全一樣的數據存放在不同的計算節點上,每一份數據都是完整的,並且稱爲一個副本。

    Shard:將表中的數據按照一定的規則拆分爲多個部分,每個部分的數據均存儲在不同的計算節點上,每個計算節點上的數據稱爲一個分片。

ClickHouse 基於 Replicated MergeTree 引擎與 Zookeeper 實現了複製表機制,在創建表時,可以決定表是否高可用。上一節的 statistics_replica 表,其中 / clickhouse/tables/{shard}/statistics_replica 表示 Zookeeper 中對應副本表的 node。當數據寫入 ReplicatedMergeTree 表時,過程如下:

  1. 某一個 ClickHouse 節點接收到數據寫入請求。

  2. 通過 interserver HTTP port 端口同步到其他實例。

  3. 更新 Zookeeper 集羣上的 node 信息。

3、OLAP 通用接口設計

ClickHouse 提供標準的 SQL 查詢引擎,通過 JDBC 引用程序可以實現多 ClickHouse 的基本操作。OLAP 的常規操作如上卷、下鑽和切片會涉及到多種維度自由組合、多種指標交叉剖析的過程,如果服務端採用 Mybatis 或 JPA 等常規 ORM 操作,工程師很容易根據不同的查詢場景要求設計出對應的接口,亦或是根據大量的分支操作設計出複雜的判定性接口,鑑於此,作者從 mdx 思想獲得啓示,設計一套對 OLAP 優化的通用多維服務查詢接口。

首先,一個典型的分析類 SQL 語句如下:

SELECT day_str,
       factory_name,
       workshop_name,
       prodline_name,
       device_id,
       SUM(w_total) AS total
FROM statistics
WHERE day_str BETWEEN '2020-10-01' AND '2020-12-31'
GROUP BY day_str,factory_name,workshop_name,prodline_name,device_id
ORDER BY day_str ASC;

如上語句,我們翻譯成業務語言爲『分別查詢 2020 年 4 季度全廠所有設備的耗電量』,從這裏我們可以清楚的知道這裏的維度是指『設備名稱』,指標爲『耗電量』,基於此,可以進一步歸類,維度通常出現在 SQL 語句的 SELECT、WHERE、GROUP BY 和 ORDER BY 後面,指標則通常出現在 SELECT 後面,也就是可以總結如下模式:

SELECT {維度},{指標}
FROM table_name
WHERE {維度}='xxx'
GROUP BY {維度}
ORDER BY {維度};

因此,我們可以設計如下通用接口方法:

//通用方法
List<Map<String,Object>> queryStatisticsResult(Query query);

//Query類
public class Query {
     private static final long serialVersionUID = 4904019884726531900L;
     /**
      * 維度
      */
    private List<String> dimensions;
    /**
     * 指標
     */
    private List<Measure> measures;
    /**
     * 過濾條件
     */
    private List<Filter> where;
}

//Measure類
public class Measure implements Serializable {

    private static final long serialVersionUID = -8556179136317748835L;
    /**
     * 指標名稱
     */
    @NonNull
    private String name;
    /**
     * 列名
     */
    @NonNull
    private String field;
    /**
     * 聚合類型
     */
    @NonNull
    private AggregationEnum expression;
}

//聚合枚舉
public enum AggregationEnum {
    SUM,AVG,COUNT,MIN,MAX,COUNT_DISTINCT,PERCENTILE;
}

四、總結

本文重點介紹了京東綜合能源管理平臺多維數據分析引擎的架構和設計,從數據接入、存儲和多維分析服務設計的角度,闡述了 ClickHouse 的一種典型應用場景。希望通過本文讓讀者在應對大數據實時 OLAP 領域,提供一種思路和方法。當然,限於篇幅和本人水平有限,沒有進一步展開闡述更多的可能性方案,隨着我們對於業務的深入,系統的迭代升級,適宜於將來更優方案勢必會步步推出,也請期待。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/MpuHpuRR7PpSqpZYS724hw