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


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

    自我介绍
    详细介绍项目
    学过哪些计算机基础课,数据库学过吗
    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)堆在运行时动态分配内存,存取速度慢。

  • 相关阅读:
    Redis入门到通关之数据结构解析-QuickList
    Java枚举类的使用
    具有通配符?的模式匹配算法(BF)
    Git 使用规范流程
    选择适合您的项目管理软件:哪个更好?
    Linux系统初识(使用centos7镜像 )--项目部署与简单命令、静态ip
    SVN介绍、安装及常用命令总结
    雅思口语 23九月换题季最新考题答案
    算法竞赛Java数据结构与算法类详解
    05【C语言 & 趣味算法】经典:兔子产子问题(即:Fibonacci数列)
  • 原文地址:https://blog.csdn.net/weixin_45545090/article/details/127109486