clickhouse技术细节整理

  • 数据量

    • clickhouse 面对海量数据,比如单表过百亿可以使用集群(复制 + 分片)
    • 如果数据量比较小,比如单表 10-20 亿使用单机就足以满足查询需求
    • https://segmentfault.com/a/1190000023702890
  • 向量化

    • https://www.jianshu.com/p/fc384f18ff1a

    • ClickHouse在计算层做了非常细致的工作,竭尽所能榨干硬件能力,提升查询速度。它实现了单机多核并行、分布式计算、向量化执行与SIMD指令、代码生成等多种重要技术

    • 多核并行

      • ClickHouse将数据划分为多个partition,每个partition再进一步划分为多个index granularity,然后通过多个CPU核心分别处理其中的一部分来实现并行数据处理。

        在这种设计下,单条Query就能利用整机所有CPU。极致的并行处理能力,极大的降低了查询延时。

    • 分布式计算

      • 除了优秀的单机并行处理能力,ClickHouse还提供了可线性拓展的分布式计算能力。ClickHouse会自动将查询拆解为多个task下发到集群中,然后进行多机并行处理,最后把结果汇聚到一起。
    • 向量化执行与SIMD

      • ClickHouse不仅将数据按列存储,而且按列进行计算。传统OLTP数据库通常采用按行计算,原因是事务处理中以点查为主,SQL计算量小,实现这些技术的收益不够明显。但是在分析场景下,单个SQL所涉及计算量可能极大,将每行作为一个基本单元进行处理会带来严重的性能损耗:
      • •对每一行数据都要调用相应的函数,函数调用开销占比高;
        •存储层按列存储数据,在内存中也按列组织,但是计算层按行处理,无法充分利用CPU cache的预读能力,造成CPU Cache miss严重;
        •按行处理,无法利用高效的SIMD指令;
      • ClickHouse实现了向量执行引擎(Vectorized execution engine),对内存中的列式数据,一个batch调用一次SIMD指令(而非每一行调用一次),不仅减少了函数调用次数、降低了cache miss,而且可以充分发挥SIMD指令的并行能力,大幅缩短了计算耗时。向量执行引擎,通常能够带来数倍的性能提升。
    • SIMD

      • 即 single instruction multiple data 英文首字母缩写,单指令流多数据流,也就是说一次运算指令可以执行多个数据流,一个简单的例子就是向量的加减。
      • SSE 与 SMID 关系
        • SSE(为Streaming SIMD Extensions的缩写)是由 Intel公司在1999年推出Pentium III处理器时,同时推出的新指令集。如同其名称所表示的,SSE是一种SIMD指令集。SSE有8个128位寄存器,XMM0 ~XMM7。可以用来存放四个32位的单精确度浮点数。可以看出,SSE 是一套专门为 SIMD(单指令多数据)架构设计的指令集。通过它,用户可以同时在多个数据片段上执行运算,实现数据并行(aka:矢量处理)。
        • SSE2是SSE指令的升级版,寄存器与指令格式都和SSE一致,不同之处在于其能够处理双精度浮点数等更多数据类。SSE3增加了13条新的指令
      • C++使用SIMD编程的3种方法
        • 编译器优化 即使用C/C++编写程序之后,带有SIMD优化选项编译,在CPU支持的情况下,编译器按照自己的规则去优化。
        • 使用intrinsic指令 参考Intel手册,针对SIMD指令,可以在编程时直接使用其内置的某些库函数,编译的时候在cpu和编译器的支持下会生成对应的SIMD指令。比如:double _mm_cvtsd_f64 (__m128d a) 该函数编译时就会翻译成指令:movsd
        • 嵌入式汇编 内联汇编直接在程序中嵌入对应的SIMD指令。
      • 计算机硬件支持与编译器支持
        • 要能够使用 Intel 的 SIMD 指令集,不仅需要当前 Intel 处理器的硬件支持,还需要编译器的支持。
        • $ grep -q sse4_2 /proc/cpuinfo && echo “SSE 4.2 supported” || echo “SSE 4.2 not supported”
        • 如果你的机器支持SSE4.2,那么,将打印:
        • SSE 4.2 supported
    • 使用SIMD考量

      • 利用优点: 频繁调用的基础函数,大量的可并行计算
      • 尽量避免: SSE指令集对分支处理能力非常的差,而且从128位的数据中提取某些元素数据的代价又非常的大,因此不适合有复杂逻辑的运算。
    • How Clickhouse USE SIMD

      • 大家在搜索CLICKHOUSE为什么快的文章中,都提到了CH使用到的技术列式存储,压缩,向量引擎。
      • CH在所有能够提高CPU计算效率的地方,都大量的使用了SIMD。
  • 分布式表-Distributed表引擎

    • join和in
      • https://blog.csdn.net/qq_23160237/article/details/105683601
      • 1、查询不全,由于分片的数据不均,会出现查询数据不全的问题,所以JOIN表和 IN子句 也要使用 _all 分布式表;
      • 2、查询放大,由于JOIN表 和 IN子句 也是 _all 分布式表,所以每个分片又会向其他远端的分片发起分布式查询,最终的查询次数是 N 的平方(N=分片数量);
      • 3、解决思路,使用 GLOBAL IN 和 GLOBAL JOIN 可以避免查询放大的问题。
    • 对于跨表查询
      • 可以使用跨表本地化的策略
      • 在ClickHouse集群中跨表进行Select查询时,采用Global IN/Global Join语句性能较为低下。分析原因,是在此类操作会生成临时表,并跨设备同步该表,导致查询速度慢。
      • 解决方案:采用一致性hash,将相同主键数据写入同一个数据分片,在本地local表完成跨表联合查询,数据均来自于本地存储,从而提高查询速度。
      • 这种优化方案也有一定的潜在问题,目前ClickHouse尚不提供数据的Reshard能力,当Shard所存储主键数据量持续增加,达到磁盘容量上限需要分拆时,目前只能根据原始数据再次重建CK集群,有较高的成本。
  • 分布式join

    • https://zhuanlan.zhihu.com/p/377506070

    • ClickHouse单机JOIN实现

      • ClickHouse 单机JOIN操作默认采用HASH JOIN算法,可选MERGE JOIN算法。其中,MERGE JOIN算法数据会溢出到磁盘,性能相比前者较差
      • SELECT <expr_list>
        FROM <left_table>
        [GLOBAL] [INNER|LEFT|RIGHT|FULL|CROSS] [OUTER|SEMI|ANTI|ANY|ASOF] JOIN <right_table>
        (ON <expr_list>)|(USING <column_list>) …
      • ClickHouse 的 HASH JOIN算法实现比较简单:
        • 从right_table 读取该表全量数据,在内存中构建HASH MAP;
        • 从left_table 分批读取数据,根据JOIN KEY到HASH MAP中进行查找,如果命中,则该数据作为JOIN的输出;
        • 从这个实现中可以看出,如果right_table的数据量超过单机可用内存空间的限制,则JOIN操作无法完成。通常,两表JOIN时,将较小表作为right_table.
    • 分布式JOIN实现

      • ClickHouse 是去中心化架构,非常容易水平扩展集群。当以集群模式提供服务时候,分布式JOIN查询就无法避免。这里的分布式JOIN通常指,JOIN查询中涉及到的left_table 与 right_table 是分布式表。

      • 分布式JOIN实现机制无非如下几种:

        • Broadcast JOIN
          Shuffle Join
          Colocate JOIN
      • ClickHouse集群并未实现完整意义上的Shuffle JOIN,实现了类Broadcast JOIN,通过事先完成数据重分布,能够实现Colocate JOIN。

      • ClickHouse 的分布式JOIN查询可以分为两类,带GLOBAL关键字的,和不带GLOBAL关键字的情况。

        • 普通JOIN实现
          • 如果右表为分布式表,则集群中每个节点会去执行分布式查询。这里就会存在一个非常严重的读放大现象。假设集群有N个节点,右表查询会在集群中执行N*N次。
          • ClickHouse 普通分布式JOIN查询是一个简单版的Shuffle JOIN的实现,或者说是一个不完整的实现。不完整的地方在于,并未按JOIN KEY去Shuffle数据,而是每个节点全量拉去右表数据。这里实际上是存在着优化空间的。
          • 在生产环境中,查询放大对查询性能的影响是不可忽略的。
        • GLOBAL JOIN 实现
          • GLOBAL JOIN 可以看做一个不完整的Broadcast JOIN实现。如果JOIN的右表数据量较大,就会占用大量网络带宽,导致查询性能降低。
          • GLOBAL JOIN 将右表的查询在initiator节点上完成后,通过网络发送到其他节点,避免其他节点重复计算,从而避免查询放大。
      • 分布式JOIN最佳实践

        • 尽量减少JOIN右表数据量

          • ClickHouse根据JOIN的右表数据,构建HASH MAP,并将SQL中所需的列全部读入内存中。如果右表数据量过大,节点内存无法容纳后,无法完成计算。

            在实际中,我们通常将较小的表作为右表,并尽可能增加过滤条件,降低进入JOIN计算的数据量。

        • 利用GLOBAL JOIN 避免查询放大带来性能损失

          • 如果右表或者子查询的数据量可控,可以使用GLOBAL JOIN来避免读放大。需要注意的是,GLOBAL JOIN 会触发数据在节点之间传播,占用部分网络流量。如果数据量较大,同样会带来性能损失。
        • 数据预分布实现Colocate JOIN

          • 当JOIN涉及的表数据量都非常大时,读放大,或网络广播都带来巨大性能损失时,我们就需要采取另外一种方式来完成JOIN计算了。
          • 根据“相同JOIN KEY必定相同分片”原理,我们将涉及JOIN计算的表,按JOIN KEY在集群维度作分片。将分布式JOIN转为为节点的本地JOIN,极大减少了查询放大问题。
          • 由于数据以及预分区了,相同的JOIN KEY对应的数据一定在一起,不会跨节点存在,所以无需对右表做分布式查询,也能获得正确结果。
          • 这种优化方案也有一定的潜在问题,目前ClickHouse尚不提供数据的Reshard能力,当Shard所存储主键数据量持续增加,达到磁盘容量上限需要分拆时,目前只能根据原始数据再次重建CK集群,有较高的成本。
  • 22
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

htmljsp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值