【面经】特斯拉大数据开发面经

欢迎点击此处关注公众号。

自我介绍
详细介绍项目
学过哪些计算机基础课,数据库学过吗
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 的执行时间。
数据倾斜怎么处理
数组的特点、优缺点
  1. 数组在内存中连续
  2. 数组在内存中顺序存储,可通过下标访问,访问效率高;
  3. 使用数组之前,必须事先固定数组长度,不支持动态改变数组大小;
  4. 数组元素增加时,有可能会数组越界;
  5. 数组元素减少时,会造成内存浪费;
  6. 数组增删时需要移动其它元素。
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)堆在运行时动态分配内存,存取速度慢。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值