zoukankan      html  css  js  c++  java
  • 【ClickHouse 技术系列】 ClickHouse 中的嵌套数据结构

    简介:本文翻译自 Altinity 针对 ClickHouse 的系列技术文章。面向联机分析处理(OLAP)的开源分析引擎 ClickHouse,因其优良的查询性能,PB级的数据规模,简单的架构,被国内外公司广泛采用。本系列技术文章,将详细展开介绍 ClickHouse。

    前言

    本文翻译自 Altinity 针对 ClickHouse 的系列技术文章。面向联机分析处理(OLAP)的开源分析引擎 ClickHouse,因其优良的查询性能,PB 级的数据规模,简单的架构,被国内外公司广泛采用。

    阿里云 EMR-OLAP 团队,基于开源 ClickHouse 进行了系列优化,提供了开源 OLAP 分析引擎 ClickHouse 的云上托管服务。EMR ClickHouse 完全兼容开源版本的产品特性,同时提供集群快速部署、集群管理、扩容、缩容和监控告警等云上产品功能,并且在开源的基础上优化了 ClickHouse 的读写性能,提升了 ClickHouse 与 EMR 其他组件快速集成的能力。访问 ClickHouse - E-MapReduce - 阿里云 了解详情。

    译者:何源(荆杭),阿里云计算平台事业部高级产品专家

    ClickHouse 中的嵌套数据结构

    在这篇博客文章中,我们将了解 ClickHouse for MySQL 中的嵌套数据结构,以及如何将其与 PMM 结合使用来查看查询。

    嵌套结构在关系数据库管理系统中并不常见。通常情况下,它只是平面表。有时,将非结构化信息存储在结构化数据库中会很方便。

    我们正在努力将 ClickHouse 调整为用于 Percona 监控和管理 (PMM) 的长期存储,尤其是存储有关查询的详细信息。我们试图解决的问题之一是,对导致特定查询失败的不同错误进行计数。

    例如,对于日期为 2017-08-17 的查询:

    "SELECT foo FROM bar WHERE id=?"

    被执行了 1000 次。其中 25 次失败的错误代码为“1212”,8 次失败的错误代码为“1250”。当然,在关系数据中进行存储的传统方法是创建一个表 "Date, QueryID, ErrorCode, ErrorCnt",然后对这个表执行 JOIN。遗憾的是,列式数据库在多个 Join 的情况下表现不佳,通常建议使用非规范化表。

    我们可以为每个可能的 ErrorCode 创建一个列,但这并不是最优解。可能有成千上万的列,而且大多数时候它们都是空的。

    在这种情况下,ClickHouse 提出了嵌套数据结构。对于我们的情况,这些可以定义为:

    CREATE TABLE queries
    (
        Period Date,
        QueryID UInt32,
        Fingerprint String,
        Errors Nested
        (
            ErrorCode String,
            ErrorCnt UInt32
        )
    )Engine=MergeTree(Period,QueryID,8192);

    这个解决方案有明显的问题:我们如何在这个表中插入数据?我们如何提取它?

    我们先从 INSERT 开始。插入可能如下所示:

    INSERT INTO queries VALUES ('2017-08-17',5,'SELECT foo FROM bar WHERE id=?',['1220','1230','1212'],[5,6,2])

    这意味着 2017-08-17 期间插入的查询出现了 5 次错误 1220,6 次错误 1230,2 次错误 1212。

    那么在不同的日期,它可能会产生不同的错误:

    INSERT INTO queries VALUES ('2017-08-18',5,'SELECT foo FROM bar WHERE id=?',['1220','1240','1258'],[3,2,1])

    让我们看一下 SELECT 数据的方法。非常基础的 SELECT:

    SELECT *
    FROM queries
    |_____Period_|_QueryID_|_Fingerprint_|_Errors.ErrorCode_______|_Errors.ErrorCnt_|
    | 2017-08-17 |       5 | SELECT foo  | ['1220','1230','1212'] | [5,6,2]         |
    | 2017-08-18 |       5 | SELECT foo  | ['1220','1240','1260'] | [3,16,12]       |
    |____________|_________|_____________|________________________|_________________|

    如果我们想使用更熟悉的表格输出,则可以使用 ARRAY JOIN 扩展:

    SELECT *
    FROM queries
    ARRAY JOIN Errors
    ┌─────Period─┬─QueryID─┬─Fingerprint─┬─Errors.ErrorCode─┬─Errors.ErrorCnt─┐
    │ 2017-08-17 │       5 │ SELECT foo  │ 1220             │            5    │
    │ 2017-08-17 │       5 │ SELECT foo  │ 1230             │            6    │
    │ 2017-08-17 │       5 │ SELECT foo  │ 1212             │            2    │
    │ 2017-08-18 │       5 │ SELECT foo  │ 1220             │            3    │
    │ 2017-08-18 │       5 │ SELECT foo  │ 1240             │           16    │
    │ 2017-08-18 │       5 │ SELECT foo  │ 1260             │           12    │
    └────────────┴─────────┴─────────────┴──────────────────┴─────────────────┘

    但是,通常我们希望看到多个期间的聚合,这可以通过传统的聚合函数来完成:

    SELECT 
        QueryID,
        Errors.ErrorCode,
        SUM(Errors.ErrorCnt)
    FROM queries
    ARRAY JOIN Errors
    GROUP BY 
        QueryID,
        Errors.ErrorCode
    ┌─QueryID─┬─Errors.ErrorCode─┬─SUM(Errors.ErrorCnt)─┐
    │       5 │ 1212             │                 2    │
    │       5 │ 1230             │                 6    │
    │       5 │ 1260             │                12    │
    │       5 │ 1240             │                16    │
    │       5 │ 1220             │                 8    │
    └─────────┴──────────────────┴──────────────────────┘

    如果我们别出心裁,每个 QueryID 只返回一行,我们也可以这么做:

    SELECT 
        QueryID, 
        groupArray((ecode, cnt))
    FROM 
    (
        SELECT 
            QueryID, 
            ecode, 
            sum(ecnt) AS cnt
        FROM queries 
        ARRAY JOIN 
            Errors.ErrorCode AS ecode, 
            Errors.ErrorCnt AS ecnt
        GROUP BY 
            QueryID, 
            ecode
    ) 
    GROUP BY QueryID
    ┌─QueryID─┬─groupArray(tuple(ecode, cnt))──────────────────────────────┐
    │       5 │ [('1230',6),('1212',2),('1260',12),('1220',8),('1240',16)] │
    └─────────┴────────────────────────────────────────────────────────────┘
    

    结论

    ClickHouse 提供了灵活的方式来存储数据,尽管它是一个列式数据库,但可以实现较低的结构化程度,并提供各种函数来提取和聚合数据。

    后续

    您已经了解了在 ClickHouse 中处理实时更新相关内容,本系列还包括其他内容:

    原文链接
    本文为阿里云原创内容,未经允许不得转载。 

  • 相关阅读:
    事件循环(Event Loop)promise、setTimeout、async的先后执行顺序
    计算机网络方面
    深拷贝与浅拷贝
    从输入url到页面加载发生了什么?
    手写jQuery插件
    vue与微信小程序的区别
    Webpack打包工具
    SpringCloud Feign的分析
    SpringCloud Ribbon的分析(二)
    SpringCloud Ribbon的分析
  • 原文地址:https://www.cnblogs.com/yunqishequ/p/15697005.html
Copyright © 2011-2022 走看看