• Clickhouse上用Order By保证绝对正确结果但代价是性能


    一些聚合函数的结果跟流入数据的顺序有关,CH文档明确说明这样的函数的结果是不确定的。这是为什么呢?让我们用explain pipeline来一探究竟。

    以一个很简单的查询为例:

    select any( step ) from events group by request_id;
    

    events表的定义如下:

    CREATE TABLE default.events
    (
        `ID` UInt64,
        `request_id` String,
        `step_id` Int64,
        `step` String
    )
    ENGINE = MergeTree
    ORDER BY ID
    

    该查询从events表里面读取数据步骤 step 和请求ID request_id ,按照request_id分组并取第一个step

    我们看一下这个查询的pipeline:

    localhost :) explain pipeline select any( `step`) from events group by request_id
    
    ┌─explain────────────────────────────────┐
    │ (Expression)                           │
    │ ExpressionTransform                    │
    │   (Aggregating)                        │
    │   Resize 32 → 1                        │
    │     AggregatingTransform × 32          │
    │       StrictResize 32 → 32             │
    │         (Expression)                   │
    │         ExpressionTransform × 32       │
    │           (SettingQuotaAndLimits)      │
    │             (ReadFromMergeTree)        │
    │             MergeTreeThread × 32 0 → 1 │
    └────────────────────────────────────────┘
    

    可以看出没有sorting步骤。这个查询在多核服务器中速度是相当快的,因为充分利用了多核,直到最后一步才归并成一个数据流由一个线程来处理。

    可是要注意 这个查询的结果每次都不一样,可以用加过滤条件的计数来测试,测试的SQL如下:

    select countIf(A='step1') from (select any( `step`) as A from (select * from events) group by request_id)
    

    结果是:2500579, 2500635,2500660。结果差距都不大,但都不是绝对正确的结果。这是因为多线程执行时并不能严格保证是按照engine=MergeTree 的表的存储顺序来处理数据的。如果能容忍误差就没问题,因为这个查询的效率是非常高的。

    但如果要追求绝对的正确结果。则需要显示地指定顺序,改造查询如下:

    select any( step ) from (select * from events order by ID) group by request_id;
    

    查询的pipeline变成这样:

    localhost :) explain pipeline select any( step ) from (select * from events order by ID) group by request_id;
    
    ┌─explain─────────────────────────────────┐
    │ (Expression)                            │
    │ ExpressionTransform                     │
    │   (Aggregating)                         │
    │   AggregatingTransform                  │
    │     (Expression)                        │
    │     ExpressionTransform                 │
    │       (Sorting)                         │
    │       MergingSortedTransform 36 → 1     │
    │         (Expression)                    │
    │         ExpressionTransform × 36        │
    │           (SettingQuotaAndLimits)       │
    │             (ReadFromMergeTree)         │
    │             MergeTreeInOrder × 36 0 → 1 │
    └─────────────────────────────────────────┘
    

    注意到pipeline中增加了重要的一步MergingSortedTransform 36 → 1 ,这一步保证了查询的正确性,但是将多个线程的数据流归集到一起,排序后继续由一个线程完成剩下的处理步骤,效率上受到很大的影响。测试结果表示:加了ORDER BY 子句的查询能够得到一致的正确结果,但效率差了至少10倍。越是核数多的服务器,其差距越大。

  • 相关阅读:
    数据分析普遍存在的方法及理论有哪些?-数据分析师
    R语言 我要如何开始R语言_数据分析师
    R语言 我要如何开始R语言_数据分析师
    Root(hdu5777+扩展欧几里得+原根)
    CF#256(Div.2) A. Rewards
    HDU-4031-Attack(树状数组)
    POJ 3253 Fence Repair(哈夫曼树)
    Python的Django框架中的Context使用
    C++中面向对象的理解
    统计输入的单词中有几个长度大于n的,n是自己指定的,用函数对象实现
  • 原文地址:https://www.cnblogs.com/chengxin1985/p/16055559.html
Copyright © 2020-2023  润新知