欢迎点击此处关注公众号。
自我介绍
详细介绍项目
学过哪些计算机基础课,数据库学过吗
ClickHouse 的引擎知道哪些
ClickHouse 的引擎分数据库引擎和数据表引擎。
数据库引擎举例:
- Ordinary:默认数据库引擎,可以使用任意表引擎。
- MySQL:用于将远程的 MySQL 服务器中的表映射到 ClickHouse 中,并 允许对表进行 INSERT 插入和 SELECT 查询, 方便在 ClickHouse 与 MySQL 之间进行数据交换。这里不会将 MySQL 的数据同步到 ClickHouse 中, ClickHouse 就像一个壳子,可以将
MySQL 的表映射成 ClickHouse 表,使用 ClickHouse 查询 MySQL 中的数据,在 MySQL中进行的 CRUD 操作,可以同时映射到 ClickHouse 中。
表引擎:ClickHouse设计实现中的一大特色,数据表拥有何种特性、数据以何种形式被存储以及如何被加载。
- MergeTree系列:生产环境中常用。有主键索引、数据分区、数据副本、数据采样、删除和修改等功能。
- ReplacingMergeTree有了去重功能
- SummingMergeTree有了汇总求和功能
- AggregatingMergeTree有聚合功能
- CollapsingMergeTree有折叠删除功能
- VersionedCollapsingMergeTree有版本折叠功能
- GraphiteMergeTree有压缩汇总功能
- 在这些的基础上还可以叠加Replicated和Distributed。
- TinyLog表引擎:以列文件的形式保存在磁盘上,不支持索引,并且没有并发控制。一般适用于保存少量数据的小表。
- Memory表引擎:内存引擎,数据以未压缩的原始形式直接保存在内存当中,服务器重启数据就会消失。读写操作不会相互阻塞,不支持索引。简单查询下有非常非常高的性能表现(超过10G/s)。
在所有的表引擎中,最为核心的当属MergeTree系列表引擎,这些表引擎拥有最为强大的性能和最广泛的使用场合。对于非MergeTree系列的其他引擎而言,主要用于特殊用途,场景相对有限。而MergeTree系列表引擎是官方主推的存储引擎,支持几乎所有ClickHouse核心功能。
MergeTree在写入一批数据时,数据总会以数据片段的形式写入磁盘,且数据片段不可修改。为了避免片段过多,ClickHouse会通过后台线程,定期合并这些数据片段,属于相同分区的数据片段会被合成一个新的片段。这种数据片段往复合并的特点,也正是合并树名称的由来。
MergeTree作为家族系列最基础的表引擎,主要有以下特点:
- 存储的数据按照主键排序:允许创建稀疏索引,从而加快数据查询速度
- 支持分区,可以通过PRIMARY KEY语句指定分区字段。
- 支持数据副本
- 支持数据采样
- 在MergeTree中主键并不用于去重,而是用于索引,加快查询速度
ClickHouse 的本地表和分布式表
- 分布式表:一个逻辑上的表,可理解为数据库中的view,一般查询都是分布式表,分布式表的引擎会将读请求路由到本地表进行查询,然后汇总输出。这里强调一点:分布式表本身不存储数据,它只是提供了一个可以分布式访问数据的框架。
- 本地表:实在存储数据的表。
分区的好处
分区表实际上是在表的目录下再以分区命名,建子目录。
作用:进行分区裁剪,避免全表扫描,减少 MapReduce 处理的数据量,提高效率。
分区和分桶的区别
分区:分区表是指按照数据表的某列或某些列分为多个区,区从形式上可以理解为文件夹。
分桶:相对分区进行更细粒度的划分。指定分桶表的某一列,让该列数据按照哈希取模的方式随机、均匀地分发到各个桶文件中。因为分桶操作需要根据某一列具体数据来进行哈希取模操作,故指定的分桶列必须基于表中的某一列(字段)。因为分桶改变了数据的存储方式,它会把哈希取模相同或者在某一区间的数据行放在同一个桶文件中。如此一来便可提高查询效率,如:我们要对两张在同一列上进行了分桶操作的表进行 JOIN 操作的时候,只需要对保存相同列值的桶进行JOIN操作即可。
两个大表 join 如何优化
分桶表 + map join。
对两张在同一列上进行了分桶操作的表进行 JOIN 操作的时候,只需要对保存相同列值的桶进行 JOIN 操作即可。
Parquet 的优点
列式存储。优点:
- 可以提升其查询性能,查询的时候不需要扫描全部的数据,而只需要读取每次查询涉及的列,这样可以将I/O消耗降低N倍,另外可以保存每一列的统计信息(min、max、sum等),实现部分的谓词下推。
- 由于每一列的成员都是同构的,可以针对不同的数据类型使用更高效的数据压缩算法,进一步减小I/O。
- 由于每一列的成员的同构性,可以使用更加适合CPU pipeline的编码方式,减小 CPU 的缓存失效。
行动算子
1)reduce:将RDD中的数据做聚合操作,先聚合分区内元素,再聚合分区间元素。
2)collect:将RDD中的元素以数组的形式收集到Driver端。
3)first:返回RDD中的第一个元素。
4)take:取出RDD的前n个元素。
5)aggregate:先通过分区内的逻辑聚合分区内的元素,再通过分区间的逻辑聚合分区间元素和初始值。
6)countByKey:统计RDD中同一个key出现的次数。
7)foreach:遍历RDD,将函数 f 应用于每一个元素。
8)saveAsTextFile:将RDD以文本文件的格式存储到文件系统中。
spark惰性计算的好处
在 Spark 的 RDD 算子中,Transformations 算子都属于惰性求值操作,仅参与 DAG 计算图的构建、指明计算逻辑,并不会被立即调度、执行。
惰性求值的特点是当且仅当数据需要被物化(Materialized)时才会触发计算的执行,RDD 的 Actions 算子提供各种数据物化操作,其主要职责在于触发整个 DAG 计算链条的执行。
当且仅当 Actions 算子触发计算时, DAG 从头至尾的所有算子(前面用于构建 DAG 的 Transformations 算子)才会按照依赖关系的先后顺序依次被调度、执行。
好处:可以优化后再执行。
Spark 怎么划分 Stage
宽依赖。
宽窄依赖的区别
shuffle。
shuffle 是什么
map 和 reduce 之间混洗的过程。为了让来自相同 Key 的所有数据都在同一个 reduce 中处理, 需要执行一个 all-to-all 的操作, 需要在不同的节点(不同的分区)之间拷贝数据,必须跨分区聚集相同 Key 的所有数据, 这个过程叫做 Shuffle。
RDD 和 DataFrame 的区别
在 Spark 中,DataFrame 是一种以 RDD 为基础的分布式数据集,类似于传统数据库中的二维表格。DataFrame 与 RDD 的主要区别在于,前者带有 schema 元信息,即 DataFrame 所表示的二维表数据集的每一列都带有名称和类型。这使得Spark SQL得以洞察更多的结构信息,从而对藏于DataFrame背后的数据源以及作用于DataFrame之上的变换进行了针对性的优化,最终达到大幅提升运行时效率的目标。
反观RDD,由于无从得知所存数据元素的具体内部结构,Spark Core只能在stage层面进行简单、通用的流水线优化。
Hive 提交任务设置哪些参数
- 基础资源:driver 的 memory、cores,excutor 的 memory、cores。
- 动态 executor 申请:dynamicAllocation。
- 自适应执行引擎:adaptive。
- 开启 parquet 切分,合并小文件。
- 推测执行。
- shuffle 落地 hdfs。
Hive 内部表外部表的区别
创建表:
- Hive创建内部表时,会将数据移动到数据仓库指向的路径,hive管理数据的生命周期;
- Hive创建外部表时,仅记录数据所在的路径,不对数据的位置做任何改变。
删除表:
- Hive删除内部表时,内部表的元数据和数据会一起被删除。同时对于一些hive操作不适应于外部表,比如单个查询语句创建表并向表中插入数据。
- Hive删除外部表时,不删除数据。这样外部表相对来说更加安全些,数据组织也更加灵活,方便共享源数据。创建外部表时,甚至不需要知道外部数据是否存在,可以把创建数据推迟到创建表之后才进行。
选择:内部表与外部表没有太大区别。如果所有的数据都由hive处理,则创建内部表;如果数据的处理由hive和其他工具一起处理,则创建外部表。
怎么通过 Web UI 看是否数据倾斜
数据倾斜只会发生在shuffle过程中。
通过观察spark UI的界面,定位数据倾斜发生在第几个stage中。
可以在Spark Web UI上看一下当前这个stage各个task分配的数据量,从而进一步确定是不是task分配的数据不均匀导致了数据倾斜。
- 1段提交代码是1个Application。
- 1个action算子是1个job 。
- 1个job中,以宽依赖为分割线,划分成不同stage,stage编号从0开始 。
- 1个stage中,划分出参数指定数量的task,注意观察Locality Level和Duration列 。Duration 就是 task 的执行时间。
数据倾斜怎么处理
数组的特点、优缺点
- 数组在内存中连续;
- 数组在内存中顺序存储,可通过下标访问,访问效率高;
- 使用数组之前,必须事先固定数组长度,不支持动态改变数组大小;
- 数组元素增加时,有可能会数组越界;
- 数组元素减少时,会造成内存浪费;
- 数组增删时需要移动其它元素。
HashSet 的底层原理
基于HashMap实现的。所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。
如何实现一个 LRU
class LRUCache {
class Node {
int key;
int value;
Node pre;
Node next;
public Node(){};
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}
private int size;
private int capacity;
private HashMap<Integer, Node> map;
Node head;
Node tail;
public LRUCache(int capacity) {
size = 0;
this.capacity = capacity;
map = new HashMap<>();
head = new Node();
tail = new Node();
head.next = tail;
tail.pre = head;
}
public int get(int key) {
Node node = map.get(key);
if (node == null) {
return -1;
}
moveToTail(node);
return node.value;
}
public void put(int key, int value) {
Node node = map.get(key);
if (node != null) {
node.value = value;
moveToTail(node);
} else {
node = new Node(key, value);
addToTail(node);
}
}
private void addToTail(Node node) {
map.put(node.key, node);
node.pre = tail.pre;
node.next = tail;
tail.pre.next = node;
tail.pre = node;
size++;
if (size > capacity) {
removeNode(head.next);
}
}
private void moveToTail(Node node) {
removeNode(node);
addToTail(node);
}
private void removeNode(Node node) {
node.next.pre = node.pre;
node.pre.next = node.next;
map.remove(node.key);
size--;
}
}
如何实现浏览器前进后退
两个栈来实现,stack1 和 stack2。我们把首次浏览的页面依次压入栈 stack1 。点击后退按钮时,从 stack1 中 pop 栈顶元素,并将其 push 到 stack2。而点击前进按钮时,从 stack2 中 pop 栈顶元素,并将其 push 到 stack1。
Java 的 int 和 Interger 的区别,哪个效率高
- int是基本数据类型,Integer是引用数据类型;
- int默认值是0,Integer默认值是null;
- int类型直接存储数值,Integer需要实例化对象,指向对象的地址。
效率:
- int是基本数据类型,基本数据类型在内存中存放的位置是栈。
- Integer是对象的引用,对象存放在堆中。
- 这就引出了堆与栈的对比。
栈:
1)栈的存取速度比堆快,仅次于直接位于CPU的寄存器。
2)栈中的数据的大小和生存周期是确定的。
3)栈中的数据可以共享。
堆:
1)堆可以动态的分配内存大小,生存期也不必告诉编译器。
2)堆在运行时动态分配内存,存取速度慢。