ClickHouse高频面试题

Posted 在路上_JD

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ClickHouse高频面试题相关的知识,希望对你有一定的参考价值。

ClickHouse高频面试题

1、简单介绍一下ClickHouse

ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。主要用于WEB流量分析。ClickHouse的全称是Click Stream,Data WareHouse,简称ClickHouse。
ClickHouse非常适用于商业智能领域,除此之外,它也能够被广泛应用于广告流量、Web、App流量、电信、金融、电子商务、信息安全、网络游戏、物联网等众多其他领域。

2、ClickHouse具有哪些特点

  • 支持完备的SQL操作

  • 列式存储与数据压缩

  • 向量化执行引擎

  • 关系型模型(与传统数据库类似)

  • 丰富的表引擎

  • 并行处理

  • 在线查询

  • 数据分片

3、ClickHouse作为一款高性能OLAP数据库,存在哪些不足

  • 不支持事务。

  • 不擅长根据主键按行粒度进行查询(虽然支持),故不应该把ClickHouse当作Key-Value数据库使用。

  • 不擅长按行删除数据(虽然支持)

4、ClickHouse有哪些表引擎

  • MergeTree系列
  • Log系列
  • Integration Engines
  • Special Engines

5、介绍下Log系列表引擎

应用场景

Log系列表引擎功能相对简单,主要用于快速写入小表(1百万行左右的表),然后全部读出的场景。即一次写入多次查询。
Log系列表引擎的特点

共性特点

  • 数据存储在磁盘上
  • 当写数据时,将数据追加到文件的末尾

不支持

  • 并发读写,当向表中写入数据时,针对这张表的查询会被阻塞,直至写入动作结束
  • 不支持索引
  • 不支持原子写:如果某些操作(异常的服务器关闭)中断了写操作,则可能会获得带有损坏数据的表
  • 不支持ALTER操作(这些操作会修改表设置或数据,比如delete、update等等)

6、 简单介绍下MergeTree系列引擎

在所有的表引擎中,最为核心的当属MergeTree系列表引擎,这些表引擎拥有最为强大的性能和最广泛的使用场合。对于非MergeTree系列的其他引擎而言,主要用于特殊用途,场景相对有限。而MergeTree系列表引擎是官方主推的存储引擎,支持几乎所有ClickHouse核心功能。

  • MergeTree表引擎
    MergeTree在写入一批数据时,数据总会以数据片段的形式写入磁盘,且数据片段不可修改。为了避免片段过多,ClickHouse会通过后台线程,定期合并这些数据片段,属于相同分区的数据片段会被合成一个新的片段。这种数据片段往复合并的特点,也正是合并树名称的由来。
    MergeTree作为家族系列最基础的表引擎,主要有以下特点:
    • 存储的数据按照主键排序:允许创建稀疏索引,从而加快数据查询速度
    • 支持分区,可以通过PRIMARY KEY语句指定分区字段。
    • 支持数据副本
    • 支持数据采样

7、简单介绍下外部集成表引擎

ClickHouse提供了许多与外部系统集成的方法,包括一些表引擎。这些表引擎与其他类型的表引擎类似,可以用于将外部数据导入到ClickHouse中,或者在ClickHouse中直接操作外部数据源。
例如直接读取HDFS的文件或者mysql数据库的表。这些表引擎只负责元数据管理和数据查询,而它们自身通常并不负责数据的写入,数据文件直接由外部系统提供。目前ClickHouse提供了下面的外部集成表引擎:

ODBC

通过指定odbc连接读取数据源

JDBC

通过指定jdbc连接读取数据源;

MySQL

将MySQL作为数据存储,直接查询其数据

HDFS

直接读取HDFS上的特定格式的数据文件;

Kafka

将Kafka数据导入ClickHouse

RabbitMQ

与Kafka类似

8、ClickHouse有哪些数据类型

数值类型

固定长度的整数类型又包括有符号和无符号的整数类型。

  • 浮点类型
    • 单精度浮点数
    • 双精度浮点数
  • Decimal类型
    有符号的定点数,可在加、减和乘法运算过程中保持精度。ClickHouse提供了Decimal32、Decimal64和Decimal128三种精度的定点数

字符串类型

  • String
    字符串可以是任意长度的。它可以包含任意的字节集,包含空字节。因此,字符串类型可以代替其他 DBMSs 中的VARCHAR、BLOB、CLOB 等类型。
  • FixedString
    固定长度的N字节字符串,一般在在一些明确字符串长度的场景下使用。

UUID

UUID是一种数据库常见的主键类型,在ClickHouse中直接把它作为一种数据类型。UUID共有32位,它的格式为8-4-4-4-12

日期类型

时间类型分为DateTime、DateTime64和Date三类。需要注意的是ClickHouse目前没有时间戳类型,也就是说,时间类型最高的精度是秒,所以如果需要处理毫秒、微秒精度的时间,则只能借助UInt类型实现。
- Date类型
用两个字节存储,表示从 1970-01-01 (无符号) 到当前的日期值。日期中没有存储时区信息。
- DateTime类型
用四个字节(无符号的)存储 Unix 时间戳。允许存储与日期类型相同的范围内的值。最小值为 0000-00-00 00:00:00。时间戳类型值精确到秒(没有闰秒)。时区使用启动客户端或服务器时的系统时区。

布尔类型

ClickHouse没有单独的类型来存储布尔值。可以使用UInt8 类型,取值限制为0或 1。

数组类型

Array(T),由 T 类型元素组成的数组。T 可以是任意类型,包含数组类型。但不推荐使用多维数组,ClickHouse对多维数组的支持有限。例如,不能在MergeTree表中存储多维数组。

枚举类型

枚举类型通常在定义常量时使用,ClickHouse提供了Enum8和Enum16两种枚举类型。

Tuple类型

Tuple(T1, T2, …),元组,与Array不同的是,Tuple中每个元素都有单独的类型,不能在表中存储元组(除了内存表)。它们可以用于临时列分组。在查询中,IN表达式和带特定参数的 lambda 函数可以来对临时列进行分组。

特殊数据类型

  • Nullable
    Nullable类型表示某个基础数据类型可以是Null值。其具体用法如下所示:
  • Domain
    Domain类型是特定实现的类型:
    IPv4是与UInt32类型保持二进制兼容的Domain类型,用于存储IPv4地址的值。它提供了更为紧凑的二进制存储的同时支持识别可读性更加友好的输入输出格式。
    IPv6是与FixedString(16)类型保持二进制兼容的Domain类型,用于存储IPv6地址的值。它提供了更为紧凑的二进制存储的同时支持识别可读性更加友好的输入输出格式。

9、ClickHouse为何如此之快

  1. 将硬件性能发挥到极致
    基于将硬件功效最大化的目的,ClickHouse会在内存中进行GROUP BY,并且使用HashTable装载数据。

  2. 算法方面精益求精
    在ClickHouse的底层实现中,经常会面对一些重复的场景,例如字符串子串查询、数组排序、使用HashTable等。对于不同的场景会用不同的算法。

  3. 勇于尝鲜,不行就换
    除了字符串之外,其余的场景也与它类似,ClickHouse会使用最合适、最快的算法。如果世面上出现了号称性能强大的新算法,ClickHouse团队会立即将其纳入并进行验证。如果效果不错,就保留使用;如果性能不尽人意,就将其抛弃。

  4. 特定场景,特殊优化
    针对同一个场景的不同状况,选择使用不同的实现方式,尽可能将性能最大化。关于这一点,其实在前面介绍字符串查询时,针对不同场景选择不同算法的思路就有体现了。类似的例子还有很多,例如去重计数uniqCombined函数,会根据数据量的不同选择不同的算法:当数据量较小的时候,会选择Array保存;当数据量中等的时候,会选择HashSet;而当数据量很大的时候,则使用HyperLogLog算法。
    对于数据结构比较清晰的场景,会通过代码生成技术实现循环展开,以减少循环次数。接着就是大家熟知的大杀器—向量化执行了。SIMD被广泛地应用于文本转换、数据过滤、数据解压和JSON转换等场景。相较于单纯地使用CPU,利用寄存器暴力优化也算是一种降维打击了。

  5. 持续测试,持续改进
    如果只是单纯地在上述细节上下功夫,还不足以构建出如此强大的ClickHouse,还需要拥有一个能够持续验证、持续改进的机制。由于Yandex的天然优势,ClickHouse经常会使用真实的数据进行测试,这一点很好地保证了测试场景的真实性。与此同时,ClickHouse也是我见过的发版速度最快的开源软件了,差不多每个月都能发布一个版本。没有一个可靠的持续集成环境,这一点是做不到的。正因为拥有这样的发版频率,ClickHouse才能够快速迭代、快速改进。

  6. 行存储和列存储
    分析场景中,我们一般会读大量的行而取少量的列,在列式存储结构下,我们只需要取对应的列数据就可以,不参与计算的列完全不会被扫描到,这会极大的降低磁盘 IO 的消耗。

  7. 数据压缩
    基于列式存储的结构,同一列中的数据属于同一类型,压缩效果会更加显著。列存储往有着高达十倍甚至更高的压缩比,节省了大量的存储空间,降低了存储成本。

  8. 向量化执行引擎
    SIMD(Single Instruction Multiple Data)即单条指令操作多条数据,它是通过数据并行以提高性能的一种方式,可以简单理解为在寄存器层面对程序中的数据做并行处理,Clickhouse 在能够提升计算效率的地方大量使用了 SIMD,通过使用 SIMD,基本上能带来几倍的性能提升,像阿里云的 PolarDB-X 也引入了向量化执行引擎,为表达式计算带来了几十倍的性能提升。

  9. 多线程与分布式
    分布式领域存在一条定律,计算移动比数据移动更加划算,这也是其核心所在,将数据的计算直接发放到数据所在的服务器,多机并行处理,再把最终的结果汇集在一起;另外 Clickhouse 也通过线程级别并行的方式为效率进一步提速,极致去利用服务器的资源。

  10. 多样化的表引擎

10、ClickHouse 常见的问题有哪些

1.重启 ClickHouse 服务的时间会比较长:主要是由于该节点数据分片过多导致加载缓慢,耐心等待即可。

2.数据插入报错 too many parts exception:主要是由于数据插入过于频繁,导致数据分片在后台 merge 缓慢,ClickHouse 启动自我保护机制,拒绝数据继续插入。此时可尝试增大插入数据的 batch_size (10 万) 并降低数据插入的频率(每秒 1 次)以缓解该问题。

3.复制表变为只读:主要是由于 ClickHouse 无法连接 ZooKeeper 集群或 ZooKeeper 上该复制表的元数据丢失导致的,此时新数据无法插入该表。若要解决该问题,首先要检查 ZooKeeper 的连接状况,如果连接失败,则需进一步检查网络状态以及 ZooKeeper 的状态,连接恢复后,复制表就可以继续插入数据了。如果连接正常而元数据丢失,此时可以将复制表转为非复制表然后再进行数据插入操作。

4.执行 JOIN 操作时内存超限:可能是由于 JOIN 前后的两个子查询中没有添加明确的过滤条件导致的,也有可能是由于 JOIN 的数据本身就很大,无法全部加载到内存。此时可以尝试增加过滤条件以减小数据量,或者适当修改配置文件中的内存限制,以装载更多的数据。

11、ClickHouse 的问题排查方法

1.检查 ClickHouse 运行状态,确保服务正常运行。
2.检查 ClickHouse 错误日志文件,寻找问题根源。
3.检查系统日志文件 (/var/log/messages) 中与 ClickHouse 相关的记录,查看是否是系统操作导致 ClickHouse 异常。
4.对于未知问题或 BUG,可以到官方 GitHub 仓库的 issue 下寻求帮助,需提供完整的问题描述和错误日志信息。

未完待续…

大数据面试题:MapReduce核心高频面试题

文章目录

MapReduce核心高频面试题

一、ReduceTask工作机制

二、请描述mapReduce有几种排序及排序发生的阶段?

1、排序的分类

2、自定义排序WritableComparable

3、排序发生的阶段

三、请描述mapReduce中shuffle阶段的工作流程,如何优化shuffle阶段?

四、请描述mapReduce中combiner的作用是什么,一般使用情景,哪些情况不需要,及和reduce的区别?

五、如果没有定义partitioner,那数据在被送达reducer前是如何被分区的?

六、MapReduce怎么实现 TopN?

七、有可能使 Hadoop 任务输出到多个目录中么?如果可以,怎么做?

八、简述hadoop实现join的几种方法及每种方法的实现

1、reduce side join

2、map join

九、请简述hadoop怎样实现二级排序

十、Hadoop中RecordReader的作用是什么?

十一、给你一个1G的数据文件。分别有id,name,mark,source四个字段,按照mark分组,id排序,手写一个MapReduce?其中有几个Mapper?

1、在map端对mark排序,在reduce端对id分组

2、几个mapper


MapReduce核心高频面试题

一、ReduceTask工作机制

1、Copy阶段:ReduceTask从各个MapTask上远程拷贝一片数据,并针对某一片数据,如果其大小超过一定阈值,则写到磁盘上,否则直接放到内存中。

2、Merge阶段:在远程拷贝数据的同时,ReduceTask启动了两个后台线程对内存和磁盘上的文件进行合并,以防止内存使用过多或磁盘上文件过多。

3、Sort阶段:按照MapReduce语义,用户编写reduce()函数输入数据是按key进行聚集的一组数据。为了将key相同的数据聚在一起,Hadoop采用了基于排序的策略。由于各个MapTask已经实现对自己的处理结果进行了局部排序,因此,ReduceTask只需对所有数据进行一次归并排序即可。

4、Reduce阶段:reduce()函数将计算结果写到HDFS上。

二、请描述mapReduce有几种排序及排序发生的阶段?

1、排序的分类

1.1、部分排序

MapReduce根据输入记录的键对数据集排序。保证输出的每个文件内部排序。

1.2、全排序

如何用Hadoop产生一个全局排序的文件?最简单的方法是使用一个分区。但该方法在处理大型文件时效率极低,因为一台机器必须处理所有输出文件,从而完全丧失了MapReduce所提供的并行架构。

替代方案:首先创建一系列排好序的文件;其次,串联这些文件;最后,生成一个全局排序的文件。主要思路是使用一个分区来描述输出的全局排序。例如:可以为待分析文件创建3个分区,在第一分区中,记录的单词首字母a-g,第二分区记录单词首字母h-n, 第三分区记录单词首字母o-z。

1.3、辅助排序(GroupingComparator分组)

Mapreduce框架在记录到达reducer之前按键对记录排序,但键所对应的值并没有被排序。甚至在不同的执行轮次中,这些值的排序也不固定,因为它们来自不同的map任务且这些map任务在不同轮次中完成时间各不相同。 一般来说,大多数MapReduce程序会避免让reduce函数依赖于值的排序。但是,有时也需要通过特定的方法对键进行排序和分组等以实现对值的排序。

1.4、二次排序

在自定义排序过程中,如果compareTo中的判断条件为两个即为二次排序。

2、自定义排序WritableComparable

bean对象实现WritableComparable接口重写compareTo方法,就可以实现排序

@Override
public int compareTo(FlowBean o) 
    // 倒序排列,从大到小
    return this.sumFlow > o.getSumFlow() ? -1 : 1;

3、排序发生的阶段

  • 一个是在map side发生在spill后partition前。
  • 一个是在reduce side发生在copy后 reduce前。

 

三、请描述mapReduce中shuffle阶段的工作流程,如何优化shuffle阶段?

分区,排序,溢写,拷贝到对应reduce机器上,增加combiner,压缩溢写的文件。

四、请描述mapReduce中combiner的作用是什么,一般使用情景,哪些情况不需要,及和reduce的区别?

1、Combiner的意义就是对每一个maptask的输出进行局部汇总,以减小网络传输量。

2、Combiner能够应用的前提是不能影响最终的业务逻辑,而且,Combiner的输出kv应该跟reducer的输入kv类型要对应起来。

3、Combiner和reducer的区别在于运行的位置。

  • Combiner是在每一个maptask所在的节点运行;
  • Reducer是接收全局所有Mapper的输出结果。

五、如果没有定义partitioner,那数据在被送达reducer前是如何被分区的?

如果没有自定义的 partitioning,则默认的 partition 算法,即根据每一条数据的 key的 hashcode 值摸运算(%)reduce 的数量,得到的数字就是“分区号”。

六、MapReduce怎么实现 TopN?

可以自定义groupingcomparator,或者在map端对数据进行排序;然后再reduce输出时,控制只输出前n个数,就达到了topn输出的目的。

七、有可能使 Hadoop 任务输出到多个目录中么?如果可以,怎么做?

1、可以输出到多个目录中,采用自定义OutputFormat。

2、实现步骤:

  • 自定义outputformat
  • 改写recordwriter,具体改写输出数据的方法write()

 

八、简述hadoop实现join的几种方法及每种方法的实现

1、reduce side join

Map端的主要工作:为来自不同表(文件)的key/value对打标签以区别不同来源的记录。然后用连接字段作为key,其余部分和新加的标志作为value,最后进行输出。

Reduce端的主要工作:在reduce端以连接字段作为key的分组已经完成,我们只需要在每一个分组当中将那些来源于不同文件的记录(在map阶段已经打标志)分开,最后进行合并就ok了。

2、map join

在map端缓存多张表,提前处理业务逻辑,这样增加map端业务,减少reduce端数据的压力,尽可能的减少数据倾斜。

具体办法:采用distributedcache

2.1、在mapper的setup阶段,将文件读取到缓存集合中。

2.2、在驱动函数中加载缓存。

job.addCacheFile(new URI("file:/e:/mapjoincache/pd.txt"));// 缓存普通文件到task运行节点

 

九、请简述hadoop怎样实现二级排序

对map端输出的key进行排序,实现的compareTo方法。 在compareTo方法中排序的条件有二个。

十、Hadoop中RecordReader的作用是什么?

1、以怎样的方式从分片中读取一条记录,每读取一条记录都会调用RecordReader类;

2、系统默认的RecordReader是LineRecordReader;

3、LineRecordReader是用每行的偏移量作为map的key,每行的内容作为map的value;

4、应用场景:自定义读取每一条记录的方式;自定义读入key的类型,如希望读取的key是文件的路径或名字而不是该行在文件中的偏移量。

十一、给你一个1G的数据文件。分别有id,name,mark,source四个字段,按照mark分组,id排序,手写一个MapReduce?其中有几个Mapper?

1、在map端对mark排序,在reduce端对id分组

@Override
public int compareTo(GroupBean o) 
    int result = this.mark.compareTo(o.mark);
    if (result == 0) 
        return Integer.compare(this.id,o.id);
    else
        return result;
@Override
public int compare(WritableComparable a, WritableComparable b) 
    GroupBean aBean = (GroupBean) a;
    GroupBean bBean = (GroupBean) b;
    int result;
    if (aBean.getMark() > bBean. getMark())  
        result = 1;
     else if (aBean. getMark() < bBean. getMark())  
        result = -1;
     else 
        result = 0;
    
    return result;

2、几个mapper

1024m/128m=8块


  • 📢博客主页:https://lansonli.blog.csdn.net
  • 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
  • 📢本文由 Lansonli 原创,首发于 CSDN博客🙉
  • 📢停下休息的时候不要忘了别人还在奔跑,希望大家抓紧时间学习,全力奔赴更美好的生活✨

以上是关于ClickHouse高频面试题的主要内容,如果未能解决你的问题,请参考以下文章

大数据面试题:ZooKeeper核心高频面试题

大数据面试题:MapReduce核心高频面试题

大数据面试题:Hadoop优化核心高频面试题

大数据面试题:Hadoop的联邦机制核心高频面试题

大数据面试题:Flume核心高频面试题

大数据面试题:Flume核心高频面试题