面经解析
- 多益网络
- 1.数据库索引的类型包括:**唯一索引、主键索引、聚集索引**
- 2.hive内部表和外部表的区别和应用场景
- 3.堆、栈、方法区
- 4.行列存储数据库的区别
- 5.数据倾斜
- 4.如何解决**MapReduce**数据倾斜呢
- 5.如何解决**hive**造成的数据倾斜?
- 6.java中重载和重写
- 7.数据库事务及其特性
- 8.having和where的用法
- 9.为什么进行维度建模
- 10.hive优化通常怎么做
- 11.hive的复杂类型有哪些,有什么区别
- 12.group by和distinct对应MR执行过程有什么区别
- 13.java面向对象的三大特征
- 14.哈希 优点
- 15.二叉树 平衡二叉树 b树与b+树的区别
- 16.sql中on和where的区别
- 17.sql中的几个join
- 18.hadoop与sql区别
- 19. select,from,where ,group by ,having,order by ,limit的执行顺序
- 20.HDFS组成
- 21.排序问题的复杂度
2021.11.30 快手数据研发工程师一面
-
自我介绍
答:为什么选择做数据开发,比较合适,做事细心、认真。有些完美主义,其实这不是一件好事,然后喜欢做一件事情就能有一定的反馈成果,为什么不想要读博,一篇论文需要打磨很久才能到我想要的目的,那我在这个打磨得过程中,我看不到任何成果得反馈,我会着急。 -
对数据结构有没有了解
答:基本了解,本科学习过数据结构的课程。
链表和数组的区别
答:(1) 链表是链式存储结构,数组是顺序存储结构;
(2)链表通过指针连接元素与元素,在内存中不连续,而数组则是把所有元素按照顺序进行存储;
(3)链表的插入和删除元素容易,不需要移动元素,且较为容易实现长度扩充,但是寻找某个元素较为困难,而数组寻找某个元素较为简单,但插入与删除比较复杂,因为数组的最大长度在编程一开始就确定了,因此当达到最大长度时,扩充长度不如链表方便。
两者相同之处:都可以实现数据的顺序存储,构造的模型呈线性结构。
- 主要用什么技术栈,对JAVA了解吗
答:用python和java,对java了解 - 手撕算法(反转链表
(1)方法一:迭代
说明:
让后一个指针指向前一个,定义两个指针,pre和cur,最开始pre指向null,cur指向head
每次让cur的next指向pre,实现一次局部反转
局部反转之后,pre和cur同时往前移动一个位置
循环上述过程,直至cur到达链表尾部
java ListNode pre = null; ListNode cur = head; while(cur!=null&&cur.next!=null){ ListNode next = cur.next; cur.next = pre; pre = cur; cur = next; }
(2)方法二:头指针
说明:
原链表的头节点是反转之后链表的尾结点
定义指针cur,指向head
每次都让head下一个结点的next指向cur,实现一次局部反转
每次反转之后,cur和head的next指针同时往前移动一个位置
循环,直至cur到达链表的最后一个结点
ListNode cur = head;
while(cur!=null&&cur.next!=null){
ListNode next = cur.next.next;
cur.next.next = cur;
cur=head.next;
head.next=next;
}
(3)递归
说明:让当前结点的下一个结点的next指针指向当前节点
让当前结点的next指针指向null,实现从尾部开始的局部反转
当递归函数全部出栈后,链表反转完成
if(head==null ||head.next==null){
return head;
}
ListNode re = reverseList(head.next);
head.next.next = head;
head.next = null;
return re;
}
怎么判断链表是不是环形链表
答:运用两个双指针,快慢指针,快指针每次走两步,慢指针每次走一步,最终两个指针会相遇(在不是null的地方)
怎么找到环形链表的入口
答:相遇的点就是环形链表的入口
简单介绍一下hive
答:基于hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张表,并提供类SQL(HQL)查询功能。
数据仓库:存数据的地方,存的数据更大,仓库里面放的数据内容都是分门别类的,
hive能解决的问题,mapreduce一定能解决,反之不成立
(1)hive处理的数据存储在HDFS
(2)hive分析数据底层的实现是mapreduce
(3)执行程序运行在yarn上
可以看做是hadoop的客户端,一个入口,提交的是hql代码,实现的mapreduce操作
hive是怎么将sql转化成mr任务的
答: 数据仓库通过SQL进行统计分析,将SQL语言中常用的操作(select,where,group等)用MapReduce写成很多模板,所有的mapreduce模板封装在hive中;然后客户端根据用户需求编写相应的sql语句,到封装到hive中的mapreduce模板中找到所需要的,然后通过hive框架匹配出相应的mapreduce模板,运行mapreduce程序,生成相应的分析结果。最后将结果返回给客户端。
hive中写了一套逻辑,将sql转换成mr,hive相当于是hadoop的一个客户端,通过提交sql让hive解析,封装成mapreduce任务去hdfs读取数据,提交给yarn执行
针对sql语言如何写mapreduce模板
select deptid,count(*) from emp group by deptid
mapreduce的流程:inputformat读取数据,给map,map给reduce,reduce再给outputformat,key是deptid,value的值是count
首先,根据from字段决定inputformat读什么数据,from是表的名字,而inputformat是读取的文件夹
hive的元数据记录着表名和hdfs的对应关系
hive中四种排序的区别(sort by/ order by/ distributed by/ cluster by),你在什么场合下用过这些排序函数?
答案:
(1)order by是全局排序,只有一个reducer,改reducer个数也没有意义。number of reducers = 1。
(2)sort by是区内排序(每个recude内部),达不到全局排序的效果。可以自己定义reduce个数。分区没有规律(随机的,害怕数据倾斜),可以自定义
(3)distribute by是用来做分区的,和sort by连用的。往往是先distribute by(比如根据部门分区,然后在部门内部的薪水进行排序),为了给sort by使用的。
(4)cluster by,当distribute by与sort by字段一样,可以用cluster by代替,除了具有distribute by的功能外还兼具sort by的功能,但是排序只能是升序排序,不能指定排序规则为ASC或者DESC
- 实习的时候有遇到过什么困难,然后你是怎么解决的
- 除了hive还了解诸如spark 和flink这些吗
- 遇到数据倾斜怎么解决
- 你觉得数据研发的优势在哪
- 你对自己未来3-5年的规划是什么样的
- 一道sql,求出每个用户最大连续登录天数
链接: link.
user_id | log_in_date |
---|---|
u_01 | 2020-01-02 |
u_01 | 2020-01-04 |
u0001 | 2019-10-10 |
u0001 | 2019-10-11 |
u0001 | 2019-10-12 |
u0001 | 2019-10-13 |
u0001 | 2019-10-14 |
// 按时间降序排序
select
user_id,
log_in_date,
(row_number() over(partition by user_id order by log_in_date desc)) as 'rank'
from
user_log
--按时间升序排列
select
user_id,
log_in_date,
(row_number() over(partition by user_id order by log_in_date asc)) as 'rank'
from
user_log
//row_number()进行排序,分区排序
//用登录时间相减(相加)排序好的序列号
select
user_id,
DATE_ADD(log_in_date, INTERVAL row_number() over(partition by user_id order by log_in_date DESC ) day) AS 'add_time'
FROM user_log
//连续登录天数
SELECT user_id, add_time,count(add_time) AS '连续登录天数'
FROM
(
select
user_id,
DATE_ADD(log_in_date, INTERVAL row_number() over(partition by user_id order by log_in_date DESC ) day) AS 'add_time'
FROM user_log
)T
GROUP BY user_id, add_time
--最大连续登录天数
SELECT user_id,MAX(连续登录天数)
FROM
(
SELECT user_id, add_time,count(add_time) AS '连续登录天数'
FROM
(
select
user_id,
DATE_ADD(log_in_date, INTERVAL row_number() over(partition by user_id order by log_in_date DESC ) day) AS 'add_time'
FROM user_log
)T
GROUP BY user_id, add_time
)M
//连续登录天数为2的用户
SELECT user_id
FROM
(
SELECT user_id, add_time,count(add_time) AS '连续登录天数'
FROM
(
select
user_id,
DATE_ADD(log_in_date, INTERVAL row_number() over(partition by user_id order by log_in_date DESC ) day) AS 'add_time'
FROM user_log
)T
GROUP BY user_id, add_time
)M
where M.连续登录天数 = 2
//连续登录天数为2的用户法2
SELECT user_id
FROM
(
SELECT user_id,COUNT(sub_date) AS '连续登录次数'
FROM
(
SELECT
user_id,
log_in_date,
DATE_SUB(log_in_date,INTERVAL row_number () over (PARTITION BY user_id ORDER BY log_in_date ASC) DAY) AS 'sub_date'
FROM
user_log
)t
GROUP BY user_id,
sub_date
)m
WHERE 连续登录次数 = 2
- 有什么想问的吗
数据仓库
- 什么是数据仓库
首先,用于支持决策,面向分析型数据处理,它不同于企业现有的操作型数据库,其次,对多个异构的数据源有效集成,集成后按照主题进行重组,并包含历史数据,而且存放在数据仓库中的数据一般不再修改
数据仓库是一个面向主题的、集成的、相对稳定的、反应历史变化的数据集合,用于支持管理决策
数据仓库是为了便于多维分析和多角度展现而将数据按照特定的模式进行存储所建立起来的关系型数据库 - 数据库和数据仓库的区别
从三方面来对比,存在的目的、用途还有设计来说
(1)数据库是面向事务处理的,数据是由日常的业务产生的,常更新;数据仓库是面向主题的,数据来源多样,经过一定的规则转换得到,用来分析。
(2)数据库一般用来存储当前事务性数据,如交易数据;数据仓库一般存储的历史数据;
(3)数据库的设计一般是符合三范式的,有最大的精确度和最小的冗余度,有利于数据的插入;数据仓库的设计一般不符合三范式,有利于查询
12.6 58同城面经
1.对哪些大数据组件比较熟悉?(hive hadoop)
2.hadoop介绍一下
3.hdfs读写流程
4.mapreduce运行原理
5.hive的原理
6.hive里面的数据是怎么存储的,存储结构
7.行存储和列存储的好处
8.hadoop、hive的数据倾斜问题
9.n个有序数组,如何把他们排到一起
10.B+树和B-树的区别
- B树的阶:节点的最多子节点个数
- B+树的数据只在叶子节点中(稠密索引),且数据是有序的,非叶子节点是叶子节点的索引(稀疏索引)
多路查找树:
11.B+树移动会产生什么问题
12.hive中建模有哪些形式?实际场景如何去选择用哪种建模方式?
13.数据仓库的分层
数据运营层存放的是接入的原始数据,数据仓库层存放的是我们要重点设计的数据仓库中间层数据,数据应用层是面向业务定制的应用数据
- 为什么要做数据分层:(1)清晰数据结构:每一个数据分层都有它的作用域和职责,在使用表的时候能更方便地定位和理解(2)减少重复开发:规范数据分层,开发一些通用的中间层数据,能够减少极大的重复计算(3)统一数据口径:通过数据分层,提供统一的数据出口,统一对外输出的数据口径(4)复杂问题简单化:将一个复杂的任务分解成多个步骤来完成,每一层解决特定的问题
1.数据运营层ODS(operational data store): 存放的是接入的原始数据 ,是最接近数据源数据的一层,数据源中的数据,经过抽取、洗净、传输,也就是ETL之后,装入本层。因此本层数据大多数是按照源头业务系统的分类方式而分类的。一般来讲,为了考虑后续需要追溯数据问题,因此对于这一层不建议做过多的清洗工作,原封不动地接入原始数据即可,至于数据的去噪、去重、异常值处理等过程可以放在后面的DWD层来做
2.数据仓库层DW(data warehouse): 数据仓库层是做数据仓库时核心设计的一层,在这里,从ODS层中获得的数据按照主题建立各种数据模型。又细分为DWD(data warehouse detail)、DWM(data warehouse middle)和DWS(data warehouse service)
(1). 数据明细层:DWD(Data Warehouse Detail)
该层一般保持和ODS层一样的数据粒度,并且提供一定的数据质量保证。同时,为了提高数据明细层的易用性,该层会采用一些维度退化手法,将维度退化至事实表中,减少事实表和维表的关联。
(2). 数据中间层:DWM(Data WareHouse Middle)
该层会在DWD层的数据基础上,对数据做轻度的聚合操作,生成一系列的中间表,提升公共指标的复用性,减少重复加工。
(3). 数据服务层:DWS(Data WareHouse Servce)
又称数据集市或宽表。按照业务划分,如流量、订单、用户等,生成字段比较多的宽表,用于提供后续的业务查询,OLAP分析,数据分发等。
一般来讲,该层的数据表会相对比较少,一张表会涵盖比较多的业务内容,由于其字段较多,因此一般也会称该层的表为宽表。
直观来讲,就是对通用的核心维度进行聚合操作,算出相应的统计指标。
另外,在该层也会做一部分的数据聚合,将相同主题的数据汇集到一张表中,提高数据的可用性
3.数据应用层
在这里,主要是提供给数据产品和数据分析使用的数据,一般会存放在 ES、PostgreSql、Redis等系统中供线上系统使用,也可能会存在 Hive 或者 Druid 中供数据分析和数据挖掘使用。比如我们经常说的报表数据,一般就放在这里。
链接: 事实表维度表等数仓分层.
14.dwd层到dws层做了什么事情
多益网络
1.数据库索引的类型包括:唯一索引、主键索引、聚集索引
2.hive内部表和外部表的区别和应用场景
- Hive创建内部表时,会将数据移动到数据仓库指向的路径。创建外部表时,仅记录数据所在的路径,不对数据的位置做任何改变,在删除表的时候,内部表的元数据和数据会被一起删除,而外部表只删除元数据,不删除数据。这样外部表相对来说更加安全些,数据组织也更加灵活,方便共享源数据
- 一般,没有限制的时候用外部表:不会把数据加载到hive,减少数据传输,不会对HDFS的数据修改,不用担心数据被破坏,删除表时只删除表结构,不删除数据;做etl数据清洗的时候,通常选择内部表做中间表,这样清理时,会将HDFS上的文件同时删除。
3.堆、栈、方法区
- 堆:提供所有类的实例和数组对象存储区域,jvm只有一个堆区被所有线程共享,不存放基本类型和对象引用,只存放对象本身
- 栈:由栈帧组成,每个线程run的时候都会分配自己的栈内存空间,每个方法run时都会在自己的栈空间分配栈帧内存区,各个方法的局部变量存储在各自的栈帧内存区。每个线程包含一个栈区,栈中只保存基本数据类型的对象和自定义的对象引用,每个栈中的数据都是私有的,其他栈不能访问。
- 方法区:静态区,被所有的线程共享。包含所有的class和static变量,运行时常量池都分配在Java虚拟机的方法区中
4.行列存储数据库的区别
(1)传统行式数据库的特性:
- 数据按照行存储
- 如果没有索引的查询要使用大量的IO,一般的数据库都会建立索引,通过索引加快查询效率
- 建立索引和物化视图需要花费大量的时间和资源
- 面对查询需求,数据库必须被大量膨胀才能满足需求
(2)列式数据库
- 数据按列存储,每一列单独存放
- 数据即索引
- 只访问查询涉及的列,可以大量降低系统的IO
- 每一列由一个线程处理,即查询的并罚处理性能高
- 数据类型一致,数据特征相似,可以高效压缩,有利于存储和网络输出数据带宽的消耗。
(3)
- 行式存储倾向于结构固定,列式存储倾向于结构弱化
- 行式存储存储一行数据仅需要一个主键,列式存储存储一行数据需要多份主键
- 列式存储存储的都是业务数据,而列式存储除了业务数据之外,还需要存储列名。
- 行式存储像是一个java bean,所有的字段都提前定义好;列式存储更像是一个map,不提前定义,随意往里面加key/value。
5.数据倾斜
根本原因 :数据经过map后,由于不同key的数据量分布不均,在shuffle阶段通过partition将相同的key的数据打上发往同一个reducer的标记,然后开始spill写入磁盘,最后merge成最终map阶段输出文件。因此数据量很大的key将发往同一个reducer,超出了节点的计算能力,等待时间超出了可接收范围
表现 :
- 分析节点资源管理器,如果大部分节点已经执行完成,而个别节点长时间执行不完,很可能发生了数据倾斜
- 分析执行日志,作业在reduce阶段停留在99%,很长时间完成不了,很可能发生了数据倾斜。
1.导致倾斜的原因
- 业务逻辑
- 数据分布
具体原因
- group by逻辑
- distinct count
- 小表 join 大表
- 大表 join 小表
2.业务逻辑造成倾斜的原因
- group by的维度过小,某值的数量过多,这样处理某值的reduce非常耗时
- distinct ,特殊值过多,处理此特殊值的reduce耗时
- join,其中一个表较小,但是key集中,分发到某一个或几个reduce上的数据远高于平均值
- 大表和小表,但是分桶的判断字段0值或空值过多,这些空值都由一个reduce处理,非常慢
3.数据分布常见的数据倾斜
- 数据频率倾斜:某一个区域的数据量远远大于其他区域,唯一值非常少,极少数值有非常多的纪录值
- 数据大小倾斜:部分记录的大小远远大于平均值,唯一值比较多,这个字段的某些值远远多于其他值的记录数,但是它的占比小于
4.如何解决MapReduce数据倾斜呢
思路:
- 将reduce端的隐患在map端解决
- 对key进行操作,减缓reduce的压力
具体实现:
方法一: reduce端的隐患在map端解决
- combine:combine的目的是聚合并精简数据,加上combine相当于提前进行reduce,就会把一个mapper中的相同key进行了聚合,减少shufle过程中的数据量,以及reduce端的计算量。(但是如果导致数据倾斜的key大量分布在不同的mapper的时候,这种方法就不是很有效了)
- map端join ,join操作中,使用map join在map端就先join
- group,把key先进行一次reduce,之后再进行count或者distinct count
方法二: 对key操作,减缓reduce的压力
- 自定义partitioner,对key均匀分布
- 在map阶段将造成倾斜的key分组,例如aaa这个key,map时在aaa后面加上1234这四个数字之一,把key先分成四组,先进行一次运算,之后再恢复key进行最终运算
5.如何解决hive造成的数据倾斜?
首先,倾斜原因
- key分布不均匀
- 业务数据本身的特性
- 建表时考虑不周
- 某些sql语句本身就有数据倾斜
方法三: 参数调整
- 针对group by造成的倾斜:hive.map.aggr=true;
hive.groupby.skewindata=true;第一个语句中,map的输出结果集合会随机分布到reduce中,每个reduce做部分聚合操作,并输出结果,这样处理的结果是相同的,group by key又可能被分发到不同的reduce中,从而达到负载均衡的目的;第二个语句再根据预处理的数据结果按照group by key分布到reduce上,这个过程可以保证相同的group by key被分布到同一个reduce上,最后完成最终的聚合操作。 - 针对count distinct或者其他参数不当造成的数据倾斜:reduce个数太少,set mapred.reduce.tasks=800;可以用sum……group by代替count(distinct)
- 针对空值产生的数据倾斜:a.控制不参与关联;b.赋予空值新的key值
针对join产生的数据倾斜
a. 大表和小表
- 在多表关联情况下,将小表依次放到前面,
- 同时使用map join让小表缓存到内存。在map端完成join过程,这样就能省掉reduce端的工作,需要开启map-side join的设置属性:set hive.auto.convert.join=true
b.大表和大表的join产生的数据倾斜
- 将异常值赋一个随机值,以此来分散key,均匀分配给多个reduce去执行
- 如果key值都是有效值的情况下,需要设置以下几个参数来解决,set hive.exec.reducers.bytes.per.reducer=10000000000,也就是每个节点的reduce,如果join操作也产生了数据倾斜,那么就在hive中设定set hive.optimize.skewjoin=true;set hive.skewjoin.key=skew_key_threshold
6.java中重载和重写
重载:发生在本类或者其子类中,具有相同的方法名。参数列表不同,返回值类型可以相同也可以不同方法名相同,参数列表必须不一样,返回值类型可相同可不同 ,总结,重载就是针对不同情况写不同的方法,例如同一个类中的不同构造方法
重写:发生在父类和子类之间,方法名相同,参数相同,返回值类型相同,访问权限不能比父类中被重写的方法的访问权限低,比如父类是public,那么子类中该方法不能是protected,子类和父类在同一个包中,子类可以重写父类所有方法,除了声明为final和private,总结,具体的实现类对于父类的方法不满意,需要写一个满足自己要求的方法。
7.数据库事务及其特性
事务: 一种机制、一个操作序列,包含了一组数据库操作命令。事务把所有的命令作为一个整体一起向系统提交或撤销操作请求。事务是一个不可分割的工作逻辑单元。
- 原子性:事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生
- 一致性:如果事务执行之前数据库是一个完整的状态,那么事务结束后,无论事务是否成功,数据库仍然是一个完整的状态
- 隔离性:多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离
- 持久性:一个事务一旦被提交,对数据库的影响是持久的。
8.having和where的用法
- where是在对查询结果分组前,将不符合where条件的行去掉,即在分组之前过滤数据,条件中不能包含聚组函数,在group by语句之前,分组之前计算where语句
- having子句的作用是筛选满足条件的组,即在分组之后过滤数据,条件中经常包括聚组函数,在group by语句之后,分组之后计算having语句
- 1.where和having都可以使用的场景
select goods_price,goods_name from sw_goods where goods_price > 100
select goods_price,goods_name from sw_goods having goods_price > 100
having是从前面筛选的字段再筛选,而where是从数据表中的字段直接筛选。如果语句2没有select goods_price就会报错。having是先select,再从select中的筛选;而where是先筛选,再select- 2.只可以使用having,不可以使用where
select goods_category_id , avg(goods_price) as ag from sw_goods group by goods_category having ag > 1000
select goods_category_id , avg(goods_price) as ag from sw_goods where ag>1000 group by goods_category //报错!!因为from sw_goods 这张数据表里面没有ag这个字段
- where后面要接的是数据表里的字段,如果把ag换成avg(goods_price)也是报错的。而having是前面查询出什么,后面就可以接什么。
9.为什么进行维度建模
建模技术分为关系建模和维度建模
关系建模
- 将复杂的数据抽象为两个概念,关系和实体,并使用规范化的方式表示,关系建模较为松散、零碎、物理表数量多
- 关系建模严格遵循第三范式,数据冗余程度低,一致性容易得到保证,但是由于数据分布在众多的表中,查询会相对复杂,在大数据的场景下,查询效率相对较低
维度建模
- 以数据分析为出发点,不遵循三范式,故数据存在一定的冗余。维度模型面向业务,将业务用事实表和维度表呈现出来。表结构简单,故查询简单,查询效率较高。
10.hive优化通常怎么做
- fetch抓取,hive-default.xml.template文件中hive.fetch.task.conversion默认是more,该属性修改为more以后,在全局查找、字段查找、limit查找等都不走mapreduce
- 本地模式,对于数据量比较小的情况下,可以通过本地模式在单台机器上处理所有的任务,设置hive.exec.mode.local.auto的值为true
- 小表大表join,使用Group让小的维度表(1000条以下的记录条数)先进内存。在map端完成reduce。
- 大表Join大表,如果异常的key对应的是异常数据,在join之前将空key过滤;如果对应的不是异常数据,把key为空的字段赋一个随机的值,使得数据随即均匀分到不同的reducer上
- group by,在默认情况下,map阶段同一key数据发到一个reduce上,当一个key数据过大时会发生倾斜。并不是所有聚合都需要在reduce端完成,很多聚合可以先在map端进行部分聚合,最后在reduce端得到最终结果。(1)开启map端聚合参数设置
hive.map.aggr = true
;(2)在map端进行聚合操作的条目数目hive.groupby.mapaggr.checkinterval = 100000
;(3)有数据倾斜的时候进行负载均衡(默认是false)hive.groupby.skewindata = true
。设置为true时,会开启两个mr job,第一个job中,map的输出结果随机分布到reduce中,每个reduce做部分聚合操作,并输出结果,这样处理的结果是相同的key可能被分发到不同的reduce中,从而达到负载均衡的目的;第二个mr job根据预处理的结果按照group by key分布到reduce中,这个过程保证相同的key被分布到同一个reduce中,完成最终的聚合操作。 - Count(Distinct) 去重统计,一般COUNT DISTINCT使用先GROUP BY再COUNT的方式替换
- 行列过滤
- 合理设置map和reduce的个数
- 小文件合并,在map前合并
set hive.input.format= org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
在map结束后合并小文件SET hive.merge.mapfiles=true;
在整个任务结束后合并小文件SET hive.merge.mapredfiles=true;
- 复杂文件增加map数量,当input的文件都很大,任务逻辑复杂,map执行非常慢的时候,可以考虑增加Map数,来使得每个map处理的数据量减少,从而提高任务的执行效率。增加map的方法为:根据computeSliteSize(Math.max(minSize,Math.min(maxSize,blocksize)))=blocksize=128M公式,调整maxSize最大值。让maxSize最大值低于blocksize就可以增加map的个数
- 并行执行,设置参数hive.exec.parallel值为true
11.hive的复杂类型有哪些,有什么区别
- array:是由一系列相同数据类型的元素组成,这些元素可以通过下标来访问。下标从0开始。
size(字段):数组长度;array_contains(字段,“***”),查找数组中有“***”的的行 - map:包含key-value键值对,可以通过key来访问,key的类型一样;
map_keys(字段):有哪些key值;map_values(字段):有哪些value值;size(字段):查询map中有多少值 定义map<String,String> - struct:可以包含不同数据类型的元素,这些元素可以通过“点语法”得到所需要的元素定义struct< name:string,age:int>
链接: link.
链接: link.
12.group by和distinct对应MR执行过程有什么区别
- distinct会将所有的数据都shuffle到一个reducer里面,用一个reduce task来完成
- group by按某个字段分组,会起多个reducer,将数据分布到多台机器上执行
13.java面向对象的三大特征
- 封装性:私有属性,通过get,set方法进行访问
- 继承性:子类继承父类的属性和方法
- 多态性:子类继承父类,并对父类的方法重写
14.哈希 优点
哈希索引只存储了对应的哈希值和指向每个数据行的指针,因此索引的结构紧凑,让哈希索引查找的速度非常快
15.二叉树 平衡二叉树 b树与b+树的区别
二叉树
- 每个结点最多有两个子节点
- 叶子节点有0个子节点
二叉搜索树(二叉查找树)
- 每个节点最多有两个子树,左子树和右子树
- 若左子树不空,则左子树上所有节点的值均小于它的根节点的值
- 若右子树不空,则右子树上所有节点的值均大于它的根节点的值
- 左右子树也分别为二叉树
- 二叉树的根节点是确定不变的,因此当数据插入或者删除时,会造成二叉树朝着单项链表的方向发展,极端情况下为单向链表,时间复杂度变成O(n)
平衡二叉树
- 基于二叉树优化而来的
- 非叶子节点最多有两个子节点,且左边子节点小于当前节点值,右边子节点大于当前节点值
- 为保证查询性能增删节点时要保证左右两边节点层级相差不大于1
- 方法有左旋和右旋
- 具体实现有AVL、Treap、红黑树
相同点
- 基于分治思想
不同点
- 二叉查找树的根节点不可变,左右两边节点层级差没有限制
- 平衡二叉树左右两边结点层级相差不大于1,通过旋转实现根节点可变,达到自平衡。
b树(平衡多路查找树)
- 每个结点存储M/2到M个关键字
- 根结点至少有两个子节点
- 所有叶子结点在同一层,高度一致
- 非叶子节点存储指向关键字范围的子节点
- 所有关键字在整棵树中出现且只出现一次
- 非叶子节点可以命中
b+树
- 非叶子结点不存放数据,只存放关键字 ,结点中仅含有其树节点中最大或最小的关键字
- 在B-树基础上,为叶子结点增加链表指针,单链表
- 所有关键字都在叶子节点中出现
- 非叶子节点作为叶子节点的索引
- 到叶子结点才命中
区别
- B+树只有叶子结点会带有指向记录的指针(data)、而B树所有结点都有,且B树在内部节点出现的索引项不会再出现在叶子结点中
- B+树中所有叶子节点都是通过指针连接在一起,而B树不会
- B树的节点中存储了关键字、关键字记录的指针(data)、指向子树根节点的指针
- B+树的节点存储了关键字(索引)、指向子树根节点的指针
16.sql中on和where的区别
链接: link.
left join
- on是在生成临时表时使用的条件,不管on中的条件是否为真,都会返回左表中的记录(如果on中有and语句,该语句是对左表进行过滤的,那么不管真假都不起作用。如果是对右表过滤的,那么左表所有记录都返回,右表筛选以后再与左表连接返回)
- where是在临时表生成后进行过滤的,条件不为真整行全部过滤
inner join
- on和where效果一样
17.sql中的几个join
- inner join:交集
- left join:以左边的表为基准
- right join:以右边的表为基准
- full out join:外连接,并集
- cross join:笛卡尔集,左边和右边的每种可能组合都列出来,行数是表A行数×表B的行数
18.hadoop与sql区别
- sql针对的是结构化数据,Hadoop针对的是非结构化数据(用键值对)
- sql数据库使用声明式查询sql,hadoop是用函数式编程
- Hadoop为离线处理和大规模数据分析设计的,sql更适合对几个记录随机读写的在线事务处理模式
19. select,from,where ,group by ,having,order by ,limit的执行顺序
每个步骤都会产生一个虚拟表,该虚拟表作为下一个步骤的输入
-
1from阶段:指明查询的来源表
- 首先是join操作,生成虚拟表 VT1-J1
- 然后是on操作,对上个步骤生成的虚拟表筛选,生成VT1-J2
- 最后
-
2 where阶段:对生成的VT1中的行进行筛选生成VT2,此时数据还没有分组,所以不能在where中出现对统计的过滤
-
3 group by阶段:按照指定的列名列表,将VT2中的行分组,生成VT3,最后每个分组只有一行,会将null值分到同一个分组中
-
4 having阶段:对VT3的分组筛选,生成VT4,count(列名)不包括null,count(1)和count(*)包括null值在内的所有数量
-
5.select阶段:首先计算select中的表达式,生成VT5-1,如果有distinct,则删除VT5-1中的重复行
-
6 order by阶段:对VT5中的行进行排序生成VT6
-
7 limit阶段:取出指定行的记录,产生虚拟表VT7返回给用户
20.HDFS组成
- NameNode:设置副本策略、管理数据块、处理客户端读写请求
- DtaNode:存储实际的数据块、执行数据块的读写操作
- Client
- 文件切分,文件上传HDFS的时候,客户端将文件切分成一个一个的块,然后进行上传
- 与NameNode交互,获取文件的位置信息
- 与DataNode交互,读取或者写入数据
- 提供一些命令管理HDFS,比如NameNode格式化
- 可以通过一些命令访问HDFS,比如HDFS增删改查等
- Secondary NameNode:当NameNode挂掉的时候,不能马上替换NameNode并提供服务
- 辅助NameNode,分担工作量,定期合并Fsimage和Edits,并推送给NameNode
- 在紧急情况下,可以恢复NameNode
21.排序问题的复杂度
冒泡排序
- 依次比较两个相邻的数,小数在前面,大数在后面,因此第一趟最大的数在最后;第二趟不用比较最后一个数了,第二大的数据在最后,重复以上,外层循环n-1次
public static void BubbleSort(int [] arr){
int tmp;
for(int i = 0;i<arr.length;i++){
for(int j =0;j<arr.length-i-1;j++){
if(arr[j+1]<arr[j]){
tmp=arr[j+1];
arr[j+1]=arr[j];
arr[j]=tmp;
}
}
}
}
插入排序
- 构建有序序列,对于未排序数据,在已排序序列中从后往前扫描,找到相应的位置并插入,假设第一位的数字有序
private static void insertSort(int []arr){
int n = arr.length;
for(int i = 1;i<n;i++){
int j = i;
int target = arr[i];
while(j>0 && target<arr[j-1]){
arr[j] = arr[j-1];
j--;
}
arr[j] = target;
}
}
快速排序
- 分治法 ,首先选择一个关键字key,通过一趟排序将待排序列分成两个部分,其中一部分比key小,另一部分比key大,然后再对前后两部分分别采用这种方法排序,通过递归算法达到整个序列有序。先排序再递归
private static void quickSort(int[] arr,int leftIndex,int rightIndex){
if(leftIndex >= rightIndex){
return;
}
int left = leftIndex;
int right = rightIndex;
int key = arr[leftIndex];
while(left<right){
while(right>left && arr[right]>=key){
right--;
}
arr[left]=arr[right];
while(left<right && arr[left]<=key){
left++;
}
arr[right]=arr[left];
}
arr[left]=key;
quickSort(arr,leftIndex,left-1);
quickSort(arr,right+1;rightIndex);
}
归并排序
- 分治法,建立在归并操作上的排序,将已经有序的子序列合并,得到完全有序的序列;先使每个子序列有序,再使子序列间有序。先递归再合并
private static void mergeSort(int[]arr,int left,int right){
if(left>=right){
return;
}
int mid = (left+right)/2;
mergeSort(arr,left,mid);
mergeSort(arr,mid+1,right);
merge(arr,left,mid,right)
}
private static void merge(int[]arr,int left,int mid,int right){
int [] tmp = new int[arr.length];
int tIndex = left;
int cIndex = left;
int r1 = mid+1;
while(left<=mid && r1<=right){
if(arr[left]<=arr[r1]){
tmp[tIndex] = arr[left];
left++;
tIndex++:
}else{
tmp[tIndex]=arr[r1];
r1++;
tIndex++;
}
}
while(left<=mid){
tmp[tIndex]=arr[left];
left++;
tIndex++:
}
while(r1<=right){
tmp[tIndex]=arr[r1];
r1++;
tIndex++;
}
while(cIndex<=right){
arr[cIndex]=tmp[cIndex];
cIndex++;
}
}
堆排序
- 利用堆这种数据结构设计的一种排序算法
- 堆:每个结点的值都大于或小于其左右孩子节点的值,称为大顶堆
- 将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其于末尾元素进行交换,此时末尾就是最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值