Bootstrap

大数据超详细面试题汇总(附答案)

1.Linux

1.1 Linux查看内存、磁盘存储、io 读写、端口占用、进程等命令

1、查看内存:top
2、查看磁盘存储情况:df -h
3、查看磁盘IO读写情况:iotop(需要安装一下:yum install iotop)、iotop -o(直接查看输出比较高的磁盘读写程序)
4、查看端口占用情况:netstat -tunlp | grep 端口号
5、查看进程:ps aux

1.2 linux文件的查看

cat  [选项] 要查看的文件   cat  –n  a.txt
 	more 要查看的文件

操作 功能说明
空白键 (space) 代表向下翻一页;
Enter 代表向下翻『一行』;
q 代表立刻离开 more ,不再显示该文件内容。
Ctrl+F 向下滚动一屏
Ctrl+B 返回上一屏
= 输出当前行的行号
:f 输出文件名和当前行的行号

less 要查看的文件

操作 功能说明
空白键 向下翻动一页;
[pagedown] 向下翻动一页
[pageup] 向上翻动一页;
/字串 向下搜寻『字串』的功能;n:向下查找;N:向上查找;
?字串 向上搜寻『字串』的功能;n:向上查找;N:向下查找;
q 离开 less 这个程序;
Less 与 more 的区别: less 相对于more 更加强大,more是将整个文件加载之后再显示,less 是根据显示加载内容,对于显示大型文件具有较高的效率。

1.3 linux查看磁盘的分区,磁盘,内存使用情况

df: disk free 空余硬盘
free /top查看内存使用情况
fdisk –l 查看分区

1.4 linux查看当前系统进程状态

ps:process status 进程状态

1.5 linux压缩文件打包

1.gzip/gunzip 压缩
1.基本语法
gzip 文件 (功能描述:压缩文件,只能将文件压缩为*.gz文件)
gunzip 文件.gz (功能描述:解压缩文件命令)
2.经验技巧
(1)只能压缩文件不能压缩目录
(2)不保留原来的文件
2.zip/unzip 压缩
1.基本语法
zip [选项] XXX.zip 将要压缩的内容 (功能描述:压缩文件和目录的命令)
unzip [选项] XXX.zip (功能描述:解压缩文件)
3.tar 打包
1.基本语法
tar [选项] XXX.tar.gz 将要打包进去的内容 (功能描述:打包目录,压缩后的文件格式.tar.gz)
2.选项说明

选项 功能
-c 产生.tar打包文件
-v 显示详细信息
-f 指定压缩后的文件名
-z 打包同时压缩
-x 解包.tar文件

1.6Linux 中RPM软件包管理

1.查询命令(rpm -qa)
2.卸载命令(rpm -e)
3.安装命令(rpm -ivh)

1.7 linux进程状态基本语法

ps aux | grep xxx		(功能描述:查看系统中所有进程)
ps -ef | grep xxx		(功能描述:可以查看子父进程之间的关系)

2.选项说明
表1-35
选项 功能
-a 选择所有进程
-u 显示所有用户的所有进程
-x 显示没有终端的进程

1.8 linux网络统计信息和端口占用情况基本语法

netstat -anp |grep 进程号	(功能描述:查看该进程网络信息)
netstat -nlp	| grep 端口号	(功能描述:查看网络端口号占用情况)

2.选项说明
表1-45
选项 功能
-n 拒绝显示别名,能显示数字的全部转化成数字
-l 仅列出有在listen(监听)的服务状态
-p 表示显示哪个进程在调用

1.9 linux中 grep过滤查找 及 管道 ”|” 的使用

grep 过滤查找,可以匹配正则
grep 查找内容 源文件
选项 功能
-c 或 --count 计算符本样式的列数
-n 显示匹配行及行号。
-r 递归查找匹配的数据
-i 是否区分大小写,默认是区分大小写
-h 查询多文件时,不显示文件名
-H 查询多文件时,显示文件名称(默认值)
-l 查询多文件时,只显示匹配到数据的文件名,不显示内容
-L 查询多文件时,只显示未匹配到数据的文件名,不显示内容
-v 逆向查找,查找未匹配到的数据文件
-s 不显示错误信息
–color =auto 给匹配到的数据加上颜色标识

1.10 linux 搜索查找 find 、locate

find 将指定目录向下递归的遍历其各个子目录,将满足条件的显示在终端。
find [ 搜索范围 ] [ 选项 ]
选项 功能
-name<查询方式> 按照指定的文件名查找模式查找文件
-user<用户名> 查找属于指定用户名所有文件
-size<文件大小> 按照指定的文件大小查找文件。

locate 定位文件所在位置
locate指令利用事先建立的系统中所有文件名称及路径的locate数据库实现快速定位给定的文件。Locate指令无需遍历整个文件系统,查询速度较快。为了保证查询结果的准确度,管理员必须定期更新locate时刻。

1.11 Linux中文件处理

1.11.1 cut 文件切割工具
Cut 就是剪切数据,并将这些数据输出。
cut [选项参数] filename
选项参数 功能
-f 列号,提取第几列
-d 分隔符,按照指定分隔符分割列
1.切割第二三列
cut –d “” –f 2,3 a.txt
2.在文件中切割出 guan
cat a.txt | grep “guan” | cut –d “ ” -f guan
1.11.2 sed 流处理工具
sed 是一种流编辑器,一次处理一行数据,处理时,将当前行保存到临时缓冲区,处理完成之后输出到控制台,这样不断重复,直到数据被全部处理完。
sed [选项参数] ‘command’ filename
选项参数说明:
选项参数 功能
-n 使用安静(silent)模式,在sed处理中,所有的STDIN 的资料都会被打印在屏幕上,但如果加上 –n ,则只有经过sed 特殊处理的哪一行(或者动作)才会被列出来
-f 直接将sed 动作写入到一个文件内,-f filename 则可以执行该文件内的sed 动作指令
-e 直接在指令列模式上进行sed的动作编辑。
-i 直接修改读取的文件的内容,而不是输出
-r sed 的动作支援的是延伸型正规表示法的语法。(预设是基础正规表示法语法)

命令功能描述:
命令 功能描述
a (append) 新增,a的后面可以接字串,在下一行出现
d (delete) 删除
s(find repleace) 查找并替换
s/wo/ni/g g 表示 global,是否是全部替换,没有g 则默认是每行的第一个
I (insert) 插入,I 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);
1.将数据插入到第二行下面
Sed ‘2a mei nv ’ sed.txt
2.删除文件中所有包含 “wo” 的行
Sed ‘/wo/d’ sed.txt
3.将所有的wo 替换为 ni
sed ‘s/wo/ni/g’ sed.txt
4.替换换行符,添加转义字符
echo “http://bbb” | sed ‘s/http://bbb/http://aaa/g’
1.11.3 awk 流处理工具
一个强大的文本分析工具,把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行分析处理。
1.基本用法
awk [选项参数] ‘pattern1{action1} pattern2{action2}…’ filename
pattern:表示AWK在数据中查找的内容,就是匹配模式
action:在找到匹配内容时所执行的一系列命令
2.选项参数说明
表1-55
选项参数 功能
-F 指定输入文件折分隔符
-v 赋值一个用户定义变量
(1)搜索passwd文件以root关键字开头的所有行,并输出该行的第7列。
[hadoop@hadoop102 datas]$ awk -F: '/^root/{print KaTeX parse error: Expected 'EOF', got '}' at position 2: 7}̲' passwd /bin/… awk -F: '/^root/{print $1",“KaTeX parse error: Expected 'EOF', got '}' at position 2: 7}̲' passwd root,… awk -F: '/^root/{print $1”,“KaTeX parse error: Expected 'EOF', got '}' at position 10: 7 “ end”}̲' passwd root,… awk -F : 'BEGIN{print “user, shell”} {print $1”,“KaTeX parse error: Expected 'EOF', got '}' at position 2: 7}̲ END{print "dah… awk -F : 'BEGIN{print “user, shell”} /^root/{print $1”,"KaTeX parse error: Expected 'EOF', got '}' at position 2: 7}̲ END{print "dah… awk -v i=1 -F: ‘{print $3+i}’ passwd
3.awk的内置变量
表1-56
变量 说明
FILENAME 文件名
NR 已读的记录数 (行号)
NF 浏览记录的域的个数(切割后,列的个数)
(1)文件的每行的行号以及每行的列数
$ awk -F: ‘{print “filename:” FILENAME “, linenumber:” NR “,columns:” NF}’ passwd
(2) 输出文件中空行所在的行号
$ awk ‘/^$/{print NR}’ password
1.11.4 sort 对文件内容进行排序
sort [选项] [参数]
选项 说明
-n 依照数值的大小排序
-r 以相反的顺序来排序
-t 设置排序时所用的分隔字符
-k 指定需要排序的列

参数: 指定待排序的文件列表
(1)按照“:” 分割后的第三列进行排序
sort -t : -nrk 3 sort.txt

1.12 Linux追加和重写

指令 > : 如果文件存在,将原来文件的内容覆盖;原文件不存在则创建文件,再添加信息。
指令 >>:不会覆盖原文件内容,将内容追加到文件的尾部。

1.13 Linux 任务后台运行

nohup (no hang up) 不挂起的意思
nohup 命令:如果正在运行一个进程,而且认为在退出账户之后该进程还在运行,可以使用 nohup 命令。该命令可以在退出账户、终端之后任然继续相应的程序。
指的是不挂断的运行,没有后台运行的功能,可以是程序永久的执行下去,和用户终端没有关系,即使是断开 SSH 连接也不会中断运行。
在缺省的情况下,该程序所有的输出都重定向到名为 nohup.out 文件中
&:指在后台运行,但是当用户推出(挂起)的时候,命令自动也跟着退出。
因此,巧妙的将二者结合起来。
nohub command &

  1. sh test.sh &
    将命令放到后台运行,关闭xshell , 对应的任务会随着停止。
  2. nohup sh test.sh
    将命令放到后台,关闭标准输入,终端不再能够接受标准输入,并将标准输出和错误输出重定向到当前目录下的 nohub.out 文件内,即使关闭Xshell,退出当前的Session,依然能正常运行。
  3. nohup sh test.sh &
    将命令放到后台,依然可以使用标准输入,终端可以接受任何的输入,将标准输出和错误输出重定向到当前目录下的 nohup.out 文件内,即使关闭Xshell,退出当前的Session,依然能正常运行。
    4.nohup command >myout.txt 2>&1 &
    0 standard input 标准输入(stdin)
    1 standard ouput 标准输出(stdout)
    2 standard error 错误输出(stderr)
    2>&1 是将错误输出重定向到标准输出,再将标准输出重定向到 myout.txt

2.Yarn

2.1 Yarn的定义

Yarn是一个资源调度平台,负责为运算程序提供服务器运算资源,相当于一个分布式的操作系统平台

2.2 Yarn的运行原理

1.客户端向RM中提交程序
2.RM向NM中分配一个container,并在该container中启动AM
3.AM向RM注册,这样用户可以直接通过RM查看停用程序的运行状态
4.AM向RM申请和领取资源,资源的协调通过异步完成
5.AM申请到资源后,便与对应的NM通信,要求他启动任务
6.NM为任务设置好运行环境(包括环境变量,jar包,二进制程序等)后,将任务启动命令写到一个脚本中,并通过运行该脚本启动任务
7.各个任务向AM汇报自己的状态和进度,以让AM随时掌握各个任务的运行状态,从而可以在任务失败时重新启动任务
8.应用程序运行完成后,AM向RM注销并关闭自己

2.3yarn 的组成架构

ResourceManager: 是全局的资源管理器 ,整个系统的资源管理和分配,包括 Scheduler 和 ApplicationManager
1.Schduler 调度器,根据定义的策略为程序(APPlication) 分配资源,纯资源调度器
2.ApplicationManager 应用程序管理器,管理整个应用系统中程序的提交,与调度器(schduler)协商资源已启动 applicatinMaster,监听 applicationMaster 运行状态,并在失败的时候重新启动applicationMaster

ApplicationMaster :用户提交的每个程序都有一个 AM ,其主要功能有 资源的申请,任务的进一步分配,与NM通信 启动、停止、监控任务 等
NodeManager :每个节点上资源和任务管理器
1.通过定时的心跳向 RM 反馈当前节点资源使用请过以及 Container 的运行状况
2.接收和处理 来自 AM 的Container 启动、停止等请求
Container Yarn 中资源的抽象,封装了 如内存,CPU,磁盘,网络等资源

2.4 yarn三种调度器及其原理

1.FIFO Scheduler 将所有的application放到队列中,先按照作业的优先级高低,再按照到达时间的先后,为每个app分配资源。一个app需要的资源被满足了,如果还剩下了资源并且满足第二个app需要的资源,那么就为第二个app分配资源。不适合共享集群,如果有大的app需要很多资源,那么其它app可能会一直等待.如果有一个很大的job1,它先提交,并且占据了全部的资源。那么job2提交时发现没有资源了,则等待job1执行结束,才能获得资源执行。
2.Capacity Scheduler 用于一个集群中运行多个application的情况,目标是最大化吞吐量和集群利用率。capacity scheduler 允许将整个集群的资源分成多个部分,每个组织使用其中的一部分,即每个组织有一个专门的队列,每个组织的队列还可以进一步划分成层次结构,从而允许组织内部的不同用户组使用
每个队列内部,按照FIFO的方式调度Application.
每个队列可以设置最大使用容量,队列之前没有优先级
3.Fair Scheduler 允许应用在一个集群中公平的共享资源。默认情况下,fair scheduler的公平调度只基于内存,也可以配置成基于memory和cpu。当集群中只有一个app提交时,他独占集群资源。当有新的app提交时,空闲的资源被新的app使用,这样最终每个app就会得到大约相同的资源。可以为不同的app设置优先级,决定每个app占用的资源百分比。fair scheduler可以让短的作业在合理的时间内完成,而不必一直等待长作业的完成
FIFO:单队列,先进先出
Capacity : 多队列(hac,etl 每个队列设置资源最低保障和资源使用上线),队列内部为FIFO
Fair: 多队列,每个队列设置资源最低保障和资源使用上线,队列内部任务资源均匀分配,在时间尺度上获得公平的资源

2.5Yarn 的操作命令

yarn app -list    ## 显示所有的app
yarn app -kill appid   ## 停掉某个程序
yarn node -list  ## 显示yarn集群环境所有的节点
yarn node -status <node_id>  ## 显示该节点详细信息
yarn logs -applicationId <appid> ## 显示某个job 日志
yarn queue -status <queueName> ## 显示队列的详细信息

3. Zookeeper

3.1 ZooKeeper工作机制

高可用的分布式数据管理和协调框架,并且能够很好的保证分布式环境中数据的一致性
ZooKeeper负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,ZooKeeper就将负责通知已经在ZooKeeper上注册的观察者做出相应的反映
ZooKeeper = 文件系统+通知机制
应用场景:统一命名服务,统一配置管理,统一集群管理,服务器节点动态上下线,软负载均衡等等

3.2.ZooKeeper特点

1.ZooKeeper:一个领导者(Leader),多个跟随者(Follow)
2.集群中只要有半数以上的节点存活,ZooKeeper集群就能正常服务
3.全局数据一致,每个Server(所有的节点都是一个 server)保存一份相同的数据副本,客户端无论连接到哪一个Server,数据都是一致的
4.更新请求顺序进行,来自同一个客户端的更新请求按其发送顺序依次进行
5.数据更新原子性,依次数据更新要么成功,要么失败
6.实时性,在一定时间范围内,客户端能读到最新数据

3.3 .Zookeeper 有哪些节点?临时节点永久节点用来做什么?

Zookeeper的数据模型可以看做是一棵树,每个节点称作一个Znode,每个Znode默认能够存储1MB的数据,每个Znode都可以通过其路径唯一标识
持久化目录节点:客户端与Zookeeper断开连接后,该节点依然存在
持久化顺序节点:客户端与Zookeeper断开连接后,该节点依然存在,只是zookeeper给该节点名称进行顺序编号
临时目录节点:客户端与Zookeeper断开连接后,该节点被删除
临时顺序节点:客户端与Zookeeper断开连接后,该节点被删除,只是zookeeper给该节点名称进行顺序编号

3.4 .Zookeeper 的ZAB选举算法

1)半数机制:集群中半数以上机器存活,集群可用。所以Zookeeper适合安装奇数台服务器。
2)Zookeeper虽然在配置文件中并没有指定Master和Slave。但是,Zookeeper工作时,是有一个节点为Leader,其他则为Follower,Leader是通过内部的选举机制临时产生的。
3)以一个简单的例子来说明整个选举的过程。
假设有五台服务器组成的Zookeeper集群,它们的id从1-5,同时它们都是最新启动的,也就是没有历史数据,在存放数据量这一点上,都是一样的。假设这些服务器依序启动,来看看会发生什么,如图5-8所示。

图5-8 Zookeeper的选举机制
(1)服务器1启动,此时只有它一台服务器启动了,它发出去的报文没有任何响应,所以它的选举状态一直是LOOKING状态。
(2)服务器2启动,它与最开始启动的服务器1进行通信,互相交换自己的选举结果,由于两者都没有历史数据,所以id值较大的服务器2胜出,但是由于没有达到超过半数以上的服务器都同意选举它(这个例子中的半数以上是3),所以服务器1、2还是继续保持LOOKING状态。
(3)服务器3启动,根据前面的理论分析,服务器3成为服务器1、2、3中的老大,而与上面不同的是,此时有三台服务器选举了它,所以它成为了这次选举的Leader。
(4)服务器4启动,根据前面的分析,理论上服务器4应该是服务器1、2、3、4中最大的,但是由于前面已经有半数以上的服务器选举了服务器3,所以它只能接收当小弟的命了。
(5)服务器5启动,同4一样当小弟。

3.5 Zookeeper监听器原理

1.首先要有一个main() 线程
2.在main() 线程中创建 Zookeeper客户端,这是会创建两个线程,一个负责网络连接通信(Connect),一个负责监听(listener)。
3.通过connect 线程将注册的 监听事件发送给zookeeper。
4.在zookeeper 的注册监听器列表中将注册的监听事件添加到列表中。
5.Zookeeper监听到有数据或者路径发生变化,就将消息发送给 listener线程。
6.listener线程内部调用了process() 线程。

3.6Zookeeper 写数据流程

3.7 Zookeeper的部署方式有哪几种?集群中有哪些角色?最少需要多少台机器?

(1)部署方式单机模式、集群模式
(2)角色:Leader和Follower
(3)集群最少需要机器数:3

3.8 zookeeper 客户端命令行

命令基本语法 功能描述
help 显示所有操作命令
ls path [watch] 使用 ls 命令来查看当前znode中所包含的内容
ls2 path [watch] 查看当前节点数据并能看到更新次数等数据
create 普通创建
-s 含有序列
-e 临时(重启或者超时消失)
get path [watch] 获得节点的值
set 设置节点的具体值
stat 查看节点状态
delete 删除节点
rmr 递归删除节点
history 显示历史命令
listquota path
setquota –n | -b val path 用来设置某个子节点的个数和其本身的数据长度
-n 设置个数 -b 设置数据长度 byte
setAcl path acl 设置ACL权限
getAcl path 获取ACL 权限
close 关闭连接
quit 退出

4. HDFS

4.1 hdfs定义

Hdfs 是一个分布式文件系统,用来存储文件,通过目录树来定位文件
Hdfs适合一次写入,多次写出的文件场景,且不支持文件的修改,适合用来做数据分析,并不适合用来做网盘应用

4.2 hdfs的优点

1.高容错性
数据自动保存多个副本,他通过增加副本的形式,提高容错性。当某一个副本丢失后,他可以自动恢复
2.适合处理大数据
数据规模:能够处理数据达到GB,TB,甚至PB级别的数据
文件规模:能够处理百万以上的文件数量,数量相当之大
3.可构建在廉价机器上,通过多副本机制,提高可靠性

4.3 hdfs的缺点

  1. 不适合低延迟数据的访问,比如毫秒级别的存储数据是做不到的
    2.无法高效的对大量小文件进行存储。存储大量小文件的话,会占用NameNode大量内存来存储文件目录和块信息。这是不可取的,因为NameNode的内存总是有限的, 小文件的存储的寻址时间会超过读取时间
    3不支持并发写入,文件随机修改。一个文件只能有一个写,不能由多个线程同时写
    仅支持数据追加,不支持数据随机修改

4. 4 HDFS读文件流程

1.客户端向NameNode发送读文件请求,NameNode返回文件的数据块信息,对于每一个数据块,元数据节点返回保存数据块的数据节点的地址
2.文件系统返回FSDataInputStream给客户端,用来读取数据
3.FSDataInputStream连接保存次文件第一个数据块的最近的数据节点,data从数据节点读到客户端
4.当此数据块读取完毕是,FSDataInputStream关闭和此数据节点的连接,然后连接此文件下一个数据块的最近的数据节点
5.当客户端读取数据完毕后,关闭FSDataInputStream
6.在读取数据的过程中,如果客户端在与数据节点通信出现错误,则尝试连接包含此数据块的下一个数据节点
7.失败的数据节点将被记录,以后不再连接

4.5 HDFS写文件流程

1.客户端向NameNode发送写文件请求 。
2.NameNode检查目标目录是否 exists,是否文件夹 isDir ,检查客户端是否有写权限 overwrite 配置。若通过检查,返回目标路径 。
3.通过检查,DFS根据 目标路径 创建一个 输出流对象(FSDataOutputStream), 客户端用于写数据。
4.客户端将NameNode返回的分配的可写的DataNode列表和Data数据一同发送给最近的第一个DataNode节点,第一个节点将数据块发送给第二个节点,第二个节点将数据块发送给第三个节点。
5.三个数据节点存储数据成功后会向客户端发送写入成功,客户端会通知NameNode写入完毕。
6.关闭输出流。

4.6 hdfs常用操作命令

[-appendToFile <localsrc> ... <dst>]
[-cat [-ignoreCrc] <src> ...]
[-checksum <src> ...]
[-chgrp [-R] GROUP PATH...]
[-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...]
[-chown [-R] [OWNER][:[GROUP]] PATH...]
[-copyFromLocal [-f] [-p] [-l] <localsrc> ... <dst>]
[-copyToLocal [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
[-count [-q] [-h] [-v] [-x] <path> ...]
[-cp [-f] [-p | -p[topax]] <src> ... <dst>]
[-createSnapshot <snapshotDir> [<snapshotName>]]
[-deleteSnapshot <snapshotDir> <snapshotName>]
[-df [-h] [<path> ...]]
[-du [-s] [-h] [-x] <path> ...]
[-expunge]
[-find <path> ... <expression> ...]
[-get [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
[-getfacl [-R] <path>]
[-getfattr [-R] {-n name | -d} [-e en] <path>]
[-getmerge [-nl] <src> <localdst>]
[-help [cmd ...]]
[-ls [-C] [-d] [-h] [-q] [-R] [-t] [-S] [-r] [-u] [<path> ...]]
[-mkdir [-p] <path> ...]
[-moveFromLocal <localsrc> ... <dst>]
[-moveToLocal <src> <localdst>]
[-mv <src> ... <dst>]
[-put [-f] [-p] [-l] <localsrc> ... <dst>]
[-renameSnapshot <snapshotDir> <oldName> <newName>]
[-rm [-f] [-r|-R] [-skipTrash] <src> ...]
[-rmdir [--ignore-fail-on-non-empty] <dir> ...]
[-setfacl [-R] [{-b|-k} {-m|-x <acl_spec>} <path>]|[--set <acl_spec> <path>]]
[-setfattr {-n name [-v value] | -x name} <path>]
[-setrep [-R] [-w] <rep> <path> ...]
[-stat [format] <path> ...]
[-tail [-f] <file>]
[-test -[defsz] <path>]
[-text [-ignoreCrc] <src> ...]
[-touchz <path> ...]
[-usage [cmd ...]]

4.7 HDFS中的NameNode和SecondaryNameNode

4.7.1 定义

NameNode : 管理HDFS的命名空间,配置副本策略,管理block的映射信息,处理客户端读写请求
Secondary : 并非NameNode 的热备,当NameNode 挂掉的时候,不能马上替换Namenode,而且提供周期检查和清理任务,帮助NN 合并editslog ,减少NN的启动时间

4.7.2 工作原理

1.fsimage是namenode内存中元数据序列化形成的文件
edits记录客户端更新元数据信息的每一步操作
2.第一次启动namenode格式化后,创建fsimage和edits文件,如果不是第一次启动,直接加载编辑日志和镜像文件到内存
3.客户端对元数据进行增删改的请求
4.namenode记录操作日志,更新滚动日志
5.namenode在内存中对数据进行增删改
6.secondarynamenode 询问namenode是否需要checkpoint
7.secondarynamenode请求执行checkpoink
8.namenode滚动正在写的edits文件
9.将滚动前的编辑日志和镜像文件拷贝到secondarynamenode
10.生成新的镜像文件fsiamge.checkpoint
11.拷贝fsimage.checkpoint到namenode
12.namenode将fsiamge.checkpoint重新命令成fsimage

4.8 hdfs中块的大小为什么设置成128M?

Hdfs中平均寻址时间为10ms
寻址时间是传输时间的1%时为最佳状态,所以最佳传输时间为1s
目前磁盘的传输速率普遍为100mb/s,因此100mb/s*1s=100mb,因此设置块大小为128mb
寻址时间 : 10ms
传输时间 : 10 ms *1000 = 1s
磁盘传输速率 :100MB/ s
每秒传输大小 : 100MB/s * 1s = 100MB
因此最终块的大小设置为 128 MB

4.9 hfds中块为什么不能设置太大,为什么也不能设置太小

如果块设置过大,传输时间过大超过寻址时间,导致处理数据是变得非常缓慢
一方面,从磁盘传输数据的时间会明显大于寻址时间,导致程序在处理这块数据时,变得非常慢。另一方面,mapreudce中的map任务通常一次只处理一个块中的数据,如果块过大,运行速度也会变慢
如果块设置过小,会增大寻址时间
一方面,存储大量小文件会占用NameNode大量内存来存储文件目录和块信息,而namenode的内存是有限的,不可取
另一方面,文件块过小,寻址时间增大,导致 程序一直在找block
4.9.1 hdfs 上对小文件处理
4.9.1.1 过多小文件的弊端:
(1) Namenode 主要对管理命名空间,NameNode 内部有两个文件FsImage (镜像文件元数据信息), Editlog(记录操作日志)。
Namenode 每次启动,就会加载以上两个文件到内存中,如果小文件过多,其元数据和Block信息就多,会占用 NameNode 大量的内存,但是NameNode 内存有限,应该避免更多的小文件。可以实现对小文件进行合并。
(2)其次会增大数据的寻址时间,索引文件过大,使得索引速度较慢。
(3)一个块是128M , 一个小文件占用一个Block,消耗 Block
4.9.1.2 小文件的处理
(1) 使用 hadoop 自带的归档
将一个目录下的所有的文件进行归档,将多个小文件进行打包。相当于linux 中的 tar
1需要启动YARN进程
[hadoop@hadoop102 hadoop-2.7.2]$ start-yarn.sh
2归档文件
把/user/hadoop/input目录里面的所有文件归档成一个叫input.har的归档文件,并把归档后文件存储到/user/hadoop/output路径下。
Archive(把什么归档,存档)
[hadoop@hadoop102 hadoop-2.7.2]$ bin/hadoop archive -archiveName input.har –p /user/hadoop/input /user/hadoop/output
3查看归档
[hadoop@hadoop102 hadoop-2.7.2]$ hadoop fs -lsr /user/hadoop/output/input.har
[hadoop@hadoop102 hadoop-2.7.2]$ hadoop fs -lsr har:///user/hadoop/output/input.har
4解归档文件
[hadoop@hadoop102 hadoop-2.7.2]$ hadoop fs -cp har:/// user/hadoop/output/input.har/* /user/hadoop
(2) Sequence File
Sequence File 是由一系列Key/Value 组成,如果 key为文件名,value 为文件内容,则可以将大批小文件合并为一个大文件。
(3) CombineFileInputFormat
MapTask 任务数主要由切片数决定,切片原理由InputFormat 决定。
有多个小文件是,可以采用 CombineFileInputFormat , 对小文件进行合并,无论是多少个小文件,最终都是一个 MapTask
(4) Hive中compress
Hive 中除了数据的存储格式优化 SequenceFile , Parquet ,ORCFile ,TextFile
Hadoop 中支持的压缩格式: gzip ,bzip2 ,snappy ,lzo
在map执行前合并小文件,减少map数:CombineHiveInputFormat具有对小文件进行合并的功能(系统默认的格式)
set hive.input.format= org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;

4.10 HDFS 集群安全模式

安全模式是一种保护机制,处于安全模式时,NameNode对外只提供读操作,不能增删改等操作。
1.NameNode 启动时,便会进入到安全模式。
2.NameNode首先将镜像文件载入内存,并执行 EditLog 中的操作,一旦内存中成功建立元数据镜像,则会创建一个新的 Fsimage和 空的editLog 文件,此时,NameNode开始监控DataNode 请求,这个过程一直处于安全模式。
3.检查数据的完整性, 假设设置的副本数是 5(dfs.replication =5) , 但是实际只存在3 个副本,那么比例就是 3/5 = 0.6。
在hdfs-default.xml中设置安全模式最小副本数 dfs.safemode.threshold.pct = 0.999f, 而副本率 0.6 明显小于 0.999,
那么系统会自动复制副本到其他的 DataNode上。如果系统中有8 个副本,但是设置的是5个副本,会删除掉多余的副本。
4.正常情况下,安全模式运行一段时间会自动退出的。
查看安全模式状态:hdfs dfsadmin –safemode get
进入安全模式:hdfs dfsadmin –safemode enter
离开安全模式:hdfs dfsadmin –safemode leave
等待安全模式:hdfs dfsadmin –safemode wait

4.11 HDFS HA 高可用

通过双NameNode消除单点故障
Hdfs HA 通过配置active/standby 两个NameNodes实现在集群中对NameNode的热备来解决上述问题。如果出现故障,如果机器崩溃或机器需要升级维护,这是可通过此种方法将NameNode很快的切换到另一台机器

1.元数据管理方式需要改变
内存中各自保存一份元数据
Edits日志只有active状态的NameNode节点可以做写操作
两个NameNode都可以读取Edits
共享的Edits放在一个共享存储中管理
2.需要一个状态管理功能模块
实现了一个zkfailover,常驻在每一个namenode所在的节点,每一个zkfailover负责监控自己所在NameNode节点,利用zk进行状态标识,当需要进行状态切换时,由zkfailover切换,切换时需要防止brain split现象的发生
3.必须保证两个NameNode之间可以ssh无密登录
4.隔离(Fence),即同一时刻只有一个NameNode对外服务

4.12Hdfs – HA

HA 高可用指 :同时有两个NameNode,当一个NameNode 宕机,则另外一个NameNode 可以及时的转为活跃状态。
需要依靠一个共享存储系统 ( Zookeeper ),两个NameNode都包含元数据信息Fsimage,而editlog 文件是存放在共享存储系统中,Active Namenode 将数据写入editsLog并保存到共享存储系统(Quorum Journal Node,Network File System,Zookeeper ),而 StandBy Namenode 会监听该系统,从该系统中读取 editlog 日志的数据,写入自己的使得与Active Namenode 数据保存一致,而StandBy NameNode 只有读权限。
实现一个 ZKFC(Zookeeper Failover Controller) ,常驻在每一个namenode节点中,每一个zkfailer负责控制自己所在的namenode节点,利用zk进行状态标识,当需要进行状态切换时,由zkfailer来负责切换,切换时需要防止溢出现象的发生

4.13 HDFS中的 HA ZKFC

ZKFailoverController(主备切换控制器)主要职责
分为两种, 依赖 zookeeper 的自动切换和不依赖zookeeper 的手动切换
1.健康检测:定期的向它监控的nn发送健康探测命令,从而来确定某个nn是否处于健康状态,如果机器宕机,心跳失败,那么zkfc就会标记它处于一个不健康的状态
2.会话管理:如果nn是健康的zkfc就会在zookeeper中保持一个打开的会话,如果namenode同时还是active状态的,那么zkfc还会在zookeeper中占有一个类型为短暂类型的znode,当这个nn挂掉时,这个znode将会被删除,然后备用的nn,会得到这把锁,升级为主nn,同时标记状态为active
3.当宕机的nn重新启动时,它会再次注册zookeeper,发现已经有znode锁了,便会自动变为standby状态,如此循环往复,保证高可需,需要注意,目前仅仅支持最多两个nn
4.Master选举:如上所述,通过在zookeeper中维持一个短暂类型的znode,来实现抢占式的锁机制,从而判断哪个nn为active状态

4.14HDFS NameNode 共享存储系统

(Network File system): 通过linux 共享的文件系统,属于操作系统的配置
QJN (Quoram Journal Node): hadoop 自身的东西,属于软件的配置
Zookeeper:
NameNode 之间共享数据,用的最多的 Quorum Journal Node
两个NameNode为了数据同步,会通过一组称作JournalNodes的独立进程进行相互通信。当active状态的NameNode的命名空间有任何修改时,会告知大部分的JournalNodes进程。standby状态的NameNode有能力读取JNs中的变更信息,并且一直监控edit log的变化,把变化应用于自己的命名空间。standby可以确保在集群出错时,命名空间状态已经完全同步了。

4.15 HDFS-HA自动故障转移工作

自动故障转移为HDFS部署了两个新组件,zookeeper和ZKFailerController(ZKFC),zookeeper是维护少量协调数据,通知客户端这些数据的改变和监视客户端故障的高可用服务。HA的自动故障转移依赖于zookeeper的以下功能:
1.故障检测:集群中的每个namenode在zookeeper中维护了一个持久会话,如果机器崩溃,zookeeper中的会话将终止,zookeeper会通知另一个namenode需要出发故障转移
2.现役namenode选择:zookeeper提供了一个简单的机制用于唯一的选择一个节点为active状态,如果目前现役namenode崩溃,另一个节点可能从zookeeper获得排外锁以表明它成为现役namenode
ZKFC是自动故障转移的另一个新组件,是zookeeper的客户端,也监视和管理namenode的运行状态。每个namenode节点也运行了一个zkfc进程,zkfc负责:
1.健康检测:zkfc使用一个健康检查命令定期的ping与之在相同主机的namenode,只要该namenode及时的回复健康状态,zkfc认为该namenode是健康的。如果该节点崩溃,冻结或进入不健康状态,健康检测器标识该节点为非健康的
2.Zookeeper会话管理:当本地namenode是健康的,zkfc保持一个在zookeeper中打开的会话,如果本地namenode处于active状态,zkfc也保持一个特殊的znode锁。该锁使用了zookeeper对短暂节点的支持,如果会话终止,锁节点将自动删除
3.基于zookeeper的选择:如果本地namenode是健康的,且zkfc发现没有其他的节点持有znode锁,它将为自己获取锁。如果成功,则它已经赢得了选择,并负责运行故障转移进程以使它的本地namenode为active。

4.16 HDFS Federation的原理结构

HDFS Federation 意味着集群中将会有多个namenode/namespace,多namespace的方式可以直接减轻单一namenode的压力
Hdfs deferation 是解决namenode单点问题的水平横向扩展
在 hdfs deferation 的情况下,只有元数据的管理与存放被分开了,但真实数据的存储还是共用的。
有多个namenode,每个namenode都有自己的命名空间(namespace),每个namenode 存贮不同的数据信息,N1(元数据),N2(日志信息),N3(其他)等,而且各个namenode 之间互不干扰,具有高并发机制
优势:
1.命名空间的扩展:随着集群使用时间的加长,hdfs上存放的数据也将会越来越多。这个时候如果还是将所有的数据都往一个namenode上存放,这个文件系统会显得非常庞大。这时候我们可以进行横向扩展,把一些大的目录分离出去,使得每个namenode下的数据看起来更加精简
2.性能的提升:当namenode所持有的数据量达到了一个非常大规模的量级的时候,这个时候namenode的处理效率可能会有影响,他可能比较容易的陷入一个繁忙的状态。而整个集群将会受限于一个单点namenode的处理效率,从而影响集群整体的吞吐量。这个时候多namenode机制显然可以减轻很多这部分的压力
3.资源的隔离:通过多个命名空间,我们可以将关键数据文件目录移到不同的namenode上,一次不让这些关键数据的读写操作受到其它普通文件读写操作的影响

5. MapReduce

5.1 MapReduce概述

Mapreduce是分布式运算程序的编程框架,它的核心功能是将用户编写的业务逻辑代码和自带默认组件组合成完整的分布式运算框架,并发运行在hadoop集群上

5.2 MapReduce优点

1.preduce易于编程,他简单的实现一些接口,就可以完成一个分布式程序
2.良好的扩展性,当你的计算资源不能得到满足的时候,你可以通过简单的增加机器来扩展他的计算能力
3.高容错性,其中一台机器挂掉了,它可以把这上面的计算任务转移到另一个节点上运行,不至于这个任务失败
4.适合PB级别以上海量数据的离线处理

5.3 Mapreduce的缺点

1.不擅长实时计算
2.不擅长流式计算
3.不擅长有向图计算

5.4 MapReduce的运行原理

输入,map,shuffle,reduce,输出
输入文件会被切分成多个块,每一个块都有一个maptask
Map阶段的输出结果会先写到内存缓冲区,然后由缓冲区写到磁盘上,当缓冲区达到一个阈值的时候就会往磁盘上写,在磁盘上写的时候会进行分区和排序
相同的分区的数据会进入同一个reduce,这一步中会从map中抓取某一分区的数据,在抓取的过程中伴随着排序合并
Reduce输出

5.5 Mapreduce 中对象的序列化

5.5.1 什么是序列化和反序列化
序列化: 就是将内存中的对象转化成字节序列(或其他的数据传输协议),以便于存储到磁盘(持久化)
和网络传输。
反序列化: 将收到的字节序列(或其他的数据传输协议,或磁盘的持久化数据),转化成内存中的对象。
5.5.2 为什么要序列化
一般,“活的”对象只生存在内存里,一旦关机断电,数据就会丢失。而且活的对象只能由本地的进程使用,不能被发送到网络上的另一台计算机上,然而序列化可以存储“活的”对象,可以将“活的”对象发送到远程计算机上。

5.6 MapReduce什么导致数据倾斜,怎么解决

数据倾斜就是数据的key的分化严重不均,造成一部分数据很多,一部分数据很少的局面

5.7MapReduce是怎么和yarn连接到一起的

MapReduce中的Driver相当于是YARN的客户端,将整个job任务提交到YARN集群上
提交的是封装MapReduce程序相关运行参数的job对象

5.8 MapReduce shuffle的运行原理

MapReduce中,map阶段处理的数据如何传递给reduce阶段,是MapReduce框架中最关键的一个流程,这个流程就叫shuffle。shuffle是洗牌,发牌,核心机制是:数据分区,排序,缓存。具体来说就是将MapTask输出的处理结果数据,分发给reduceTask,并在分发的过程中,对数据进行了分区和排序。
1.shuffle运行流程:(减少IO操作,一次性写入)
1.map task 收集我们的map()方法输出的kv对,放到内存缓冲区中
2.从内存缓冲区不断溢出本地磁盘文件,可能会溢出多个文件
3.多个溢出文件被合并成大的溢出文件
4.在溢出过程中,及合并的过程中,都要调用partitioner进行分组和针对key进行排序
5.reduce task根据自己的分区号,去各个map task 机器上取相应的结果分区数据
6.reduce task会取到同一个分区的来自不同map task的结果文件,reduce task会将这些文件再进行合并(归并排序)
7.合并成大文件后,shuffle过程也就结束了,后面进入reduce task 的逻辑运算过程(从文件中取出一个一个的键值对group,调用用户自己定义的reduce()方法
2.Shuffle的运行机制(Map方法之后,Reduce方法之前的数据处理过程称之为Shuffle)
1.map端输出文件进入环形缓冲区(80% 100M 为阈值),就溢出文件
2.分区
3.排序
4.combine 对多次溢出的文件进行归并排序,对每个mapTask的输出进行局部的汇总
5.groupingComparator 分组(辅助排序) 对reduce端的shuffle根据某一个或者某几个进行分组

5.9 MapReduce实现wordCount

5.10 MapReduce优化

MapReduce优化方法主要从六个方面考虑:
数据输入,Map阶段,Reduce阶段,IO传输,数据倾斜问题和常用的参数调优
数据输入:(1)合并小文件,在执行mapreduce之前进行小文件的合并,大量的小文件会产生大量的Map任务,增大Map任务的装在次数,而任务的装载比较耗时,会导致MR运行较慢(2)采用CombineTextInputFormat所谓输入,解决输入端大量小文件
Map阶段:1.减少溢写次数,从而减少磁盘IO
2.减少合并次数,增大merge的文件数目,减少Merge的次数,从而缩短MR处理时间
3.在map之后,不影响业务逻辑的前提下,先进行combine处理,减少磁盘IO
Reduce阶段:1.合理设置Map和Reduce数,均不能设置太少,会导致Task等待,延长处理时间,太多,会导致Map,Reduce竞争资源,造成处理超时等错误
2设置Map,Reduce共存,使Map运行一段时间后,Reduce也开始运行,减少Reduce的等待时间
3.规避使用Reduce,因为reduce在用于链接数据集的时候将会产生大量的网络消耗
IO传输:1.采用数据压缩的方式,减少网络io的时间。安装Snapy和LZO压缩编码器
2.使用SequenceFile二进制文件
数据倾斜:数据频率倾斜,某个区域的数据量要远远大于其他地区
数据大小倾斜:部分记录的大小远远大于平均值
解决方法:1.抽样和范围分区,可以通过对源数据进行抽样得到的结果来预设分区边界值
2.自定义分区
3.combine可以大量减少数据倾斜,在可能的情况下,combine的作用就是聚合精简数据集
4.采用Map join ,尽量避免Reduce Join

6. Hive

6.1 hive 定义

用于解决海量结构化日志的数据统计
.hive 是基于hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张表,并提供类sql查询功能
本质是将hql转化为mapreduce
数据仓库是面向主题的,数据集成,不可更新,随时间不断变化(不断增加新的变化)
搭建数据仓库,事实表,维度表,数据模型 (雪花模型, 星型模型)

ODS层 (Operation Data Store)[原始数据层]:存放原始数据,直接加载原始日志,数据。数据保存原貌不做处理
DWD层 (Data WareHouse Detail)[数据明细层]: 结构与粒度与原始表保存一致,对ODS 层数据进行清洗(去空格trim,脏数据,超出极限范围的数据)
DWS层 (Data WareHouse Service) [数据汇总层]:以DWD 为基础,进行轻度汇总
ADS [业务数据层] : 为各种统计报表提供数据

6.2 hive中sort by,order by,distribute by,cluster by各代表什么意思

1.Order by 会对输入做全局排序,因此只有一个reducer,会导致当数据规模较大时,需要较长的计算时间
2.Sort by 局部排序(区内排序),多个reducer ,是对 reducer 内部进行排序,因此全局不是有序的。会在数据进入reducer前进行排序
3.Distribute by 分区,(输入时便进行了分区)按照指定的字段对数据进行划分输出到不同的reducer中,结合sort by 使用 (分区排序)
4.Cluster by 除了具有distribute by 的功能还具有sort by 的功能,但排序只能是升序排序。
当distribute by 和sort by 字段相同时,可以使用cluster by 方式

select * from emp cluster by deptNo;
select * from emp distribute by deptNo sort by deptNo;

6.3 hive的运行原理

hive 通过给用户提供一系类的交互接口,接收到客户的指令(SQL),使用自己的Driver,,结合元数据(Metstore),将这些指令翻译成MapReduce,提交到Hadoop集群中执行,最后,将执行返回结果输出到用户交互接口
(将SQL语言中的select ,where ,group等很多语句用MapReduce写成模板,所有的模板分装到hive中,用户书写的SQL 语句与模板的mapreduce进行匹配,运行MapReduce,结果)
1.用户通过一些交互接口或者JDBC 进行SQL语句书写
2.任务提交给Driver
3. SQL Parser解析器 将hiveql转换为抽象语法树AST,
4.Physical Plan 编译器 然后将抽象语法树转换成查询块,将查询块转化为逻辑的查询计划,重写逻辑查询计划
5.Query Optimizer 优化器 对逻辑执行计划进行优化
6.Execution 执行器 将逻辑执行计划转化成物理执行计划(mapreduce)
7.取得并返回执行结果

6.4 hive的数据导入导出

Load data local inpath “xxxxxxx” overwrite into table 表名 partition(xx=”xx”)
1.将查询的结果导出到本地

hive (default)> insert overwrite local directory '/opt/module/datas/export/student'
            select * from student;

2.将查询的结果格式化导出到本地

hive(default)>insert overwrite local directory '/opt/module/datas/export/student1'
           ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'   select * from student;

3.将查询的结果导出到HDFS上(没有local)

hive (default)> insert overwrite directory '/user/hadoop/student2'
             ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' 
             select * from student;

6.5 Hive的排序方式

Group by 通常与聚集函数使用
1.RANK() 排序相同时会重复,总数不会变 ,跳跃排序的,两个第二名后面是第四名(1,2,2,4)
2.DENSE_RANK() 排序相同时会重复,总数会减少,连续排列,两个第二名仍然跟着第三名(1,2,2,3)
3.ROW_NUMBER() 会根据顺序计算,没有重复值的排序(即使两条记录相等,也是不重复的)(1,2,3,4)
4.LAG、LEAD 用于返回上下数据行的数据

-- lag 未设置行数时,默认是 1 
select orderid,orderdate,memberid
,lag(orderdate,1) over(partition by ownerid,memberid order by orderdate) as time1 
,lag(orderdate,2) over(partition by ownerid,memberid order by orderdate) as time2 
,lag(orderdate,3) over(partition by ownerid,memberid order by orderdate) as time3
from cnacdp_dw.dw_fact_transaciton_header_omni_channel_by_crm 
where ownerid = '84' and salestype ='S' order by memberid,orderdate limit 100;
orderid	orderdate	memberid	time1	time2	time3
84-11100003-1-1550	20220505	M8400000016	NULL	NULL	NULL
84-11100001-1-6123	20220730	M8400000016	20220505	NULL	NULL
84-11100005-1-5432	20230607	M8400000016	20220730	20220505	NULL
84-11100002-1-6437	20230612	M8400000016	20230607	20220730	20220505

5

6.6 hive 的分桶

1.创建分桶表

 `create table stu_buck(id int) clustered by(id) into 4 buckets row format delimited fields    Ternminaled by ‘\t’;`

2.创建对应的子表

create table stu(id int, name string) row format delimited fields terminated by '\t';

3.设置对应的属性

 set hive.enforce.bucketing=true;
 set mapreduce.job.reduces=-1;

4.通过子查询方式插入

Insert  into table stu_ buck select id from stu

6.7 hive内部表和外部表的区别

1.创建表:
外部表创建表的时候,不会移动数据到数据仓库目录中,只会记录表数据存放的路径
内部表会把数据移动到相应的目录下
2.删除表:
外部表在删除表的时候只会删除表的元数据信息,不会删除表数据
内部表删除时会将元数据信息和表数据同时删除
3.相互转换

Alter table student set tblpropertities(‘EXTERNAL’=’TRUE’);   or EXTERNAL=FALSE

6.8 hive有哪些复合数据类型

Map:提供了key-value存储
Struct:不同数据类型元素的集合
Array:同类型元素的集合
Uniontype:它代表一个可以具有属于你所选择的任何数据类型的值的列

6.9 hive分区的好处

可以更快的执行查询,当使用where子句查询时只扫描特定的子目录,而不是扫描整个
Hive的分区实质就是对文件进行分目录,大文件切分,where查询方便,提高查询效率

6.10 静态分区和动态分区的区别

静态分区:插入到分区必须手动指定插入到那个分区

insert overwrite table partition_table002 partition (dt='20150617', ht='00') select name, ip from partition_table001 where dt='20150617' and ht='00';

如果要插入一天24小时的数据,则需要执行以上语句24 次
启用动态分区,则可能只需要执行一次,动态分区会根据数据将数据插入到相应的分区。

动态分区:不需要指定分区,根据数据插入到相应的分区 ,以下子查询中查出来的 ht 会作为分区的值,对应 partition 后面的ht

insert overwrite table partition_table002 partition (dt, ht) select * from partition_table001 where dt='20150617';

设置参数:
set hive.exec.dynamic.partition=true; // 开启动态分区模式
set hive.exec.dynamic.partition.mode=nonstrict;
// 默认是 strict,即限制模式,限制不能所有的分区都是动态的,必须至少有一个有值即静态的, 且必须放在最前面。
// 设置为nonstrict 之后,所有的分区就都可以是动态的分区了

6.11 hive 分区跟分桶的区别

分区 :partition by 每个分区,相当于创建一个目录/子目录例如 biz_date=20240409,该分区下数据文件中不包含该分区字段
分桶: cluster by 是在分区或者分桶的基础上,对表进行组织。hive 对分桶所用的值进行 hash,hash 结果除以桶的个数取余进行分桶,保证了每个桶都有数据,但数据量不一样相等

6.11.1 文件上

分桶是对不同的文件(细粒度),分区是不同的目录(粗粒度)

6.11.2 数据类型上

分桶按照分桶字段随机切割数据,分区是非随机切割。
分桶按照hash值切割,相对比较均匀。
分区按照列的值切割,容易造成数据倾斜

6.12 hive 常用的函数

1. 系统自带函数
1.1 显示所有系统自带函数

hive > show functions;

1.2 显示该函数的使用

hive> desc function upper(函数名);

1.3 常用函数
get_json_object 从json字符串拿出来对应的value 值
regexp_replace(get_json_object(tt3.reference_info,"$$.goods"),'\\\\[|\\\\]','') 特殊符号替换
Case when                      根据条件匹配值,相当于 if else  
Collect_all ,collect_list , collect_set  将值进行汇总, 返回 Array< 类型 > ?
array_sort  数组类型排序
array_intersect 数组交集
array_contains 数组是否包含某个值
Concat ,concat_ws                将值进行拼接,将Array< 类型 > 转化为String类型
Over()                           窗口函数,根据串口的大小显示
1.4 自定义函数

/**

  • UDF(User-Defined-Function) 一进一出

  • UDAF(User-Defined Aggregation -Function)) 聚合函数,多进一出 例如 sum() count() 等

  • UDTF(User-Defined-Table-Generating Function)) 一进多出 例如 Explode(col) 对集合类型进行拆分
    */
    public class HiveFuncLow extends UDF {

    /**

      1. 创建函数继承 UDF ,实现 evaluate 函数
      1. 打jar 包上传至服务器
      1. 将 jar 包添加至 Hive|| add jar /opt/module/datas/udf.jar
      1. 创建临时函数与上传jar 相关联 || create temporary function lower as “com.utils.HiveFuncLow”
      1. 即可在HQL中使用自定义函数 || " select ename, mylower(ename) lowername from emp"
        */

    public String evaluate (final String s) {
    if (s == null || s.length() == 0 ){
    return null;
    }
    return s.toLowerCase();
    }

1.5 窗口函数

over()开窗函数前分排序函数和聚合函数两种
当为排序函数,如row_number(),rank()等时,over中的order by只起到窗口内排序作用。
当为聚合函数,如max,min,count等时,over中的order by不仅起到窗口内排序,还起到窗口内从当前行到之前所有行的聚合(多了一个范围)。

select orderid,memberno,orderdate,aftersalesamount
,sum(aftersalesamount) over(partition by orderid) as sum_aftersalesamount
,sum(aftersalesamount) over(partition by orderid order by aftersalesamount) as sum_aftersalesamount_order
,count(aftersalesamount) over(partition by orderid) as cnt_aftersalesamount_order
,count(aftersalesamount) over(partition by orderid order by aftersalesamount) as cnt_aftersalesamount_order
from cnacdp_dw.dw_fact_transaction_detail_omni_channel where ownerid = '84' and datasource = 'OMNI' and orderchannel = 'R' 
order by orderid,memberno,aftersalesamount  limit 100 ;
orderid	memberno	orderdate	aftersalesamount	sum_aftersalesamount	sum_aftersalesamount_order	cnt_aftersalesamount_order	cnt_aftersalesamount_order
84-11100000-1-10000	M8400030110	20221127	0.0	15800.0	0.0	2	1
84-11100000-1-10000	M8400030110	20221127	15800.0	15800.0	15800.0	2	2
84-11100000-1-10002	M8400030110	20221127	0.0	4600.0	0.0	5	1
84-11100000-1-10002	M8400030110	20221127	600.0	4600.0	1200.0	5	3
84-11100000-1-10002	M8400030110	20221127	600.0	4600.0	1200.0	5	3
84-11100000-1-10002	M8400030110	20221127	1400.0	4600.0	2600.0	5	4
84-11100000-1-10002	M8400030110	20221127	2000.0	4600.0	4600.0	5	5

6.13 hive 的数据存储格式

面向行 : 一行数据保存为一行,读取一行中的任何列都要把整行都读取出来,这种方式磁盘读取的时候开销比较大
面向列 :整个文件被切割称若干个列,每列的数据保存在一起。这种方式会占用更多的内存空间,需要将行数据缓存起来

6.13.1 行存储

1 TextFile
每一行是一条记录,每行是以 “ \n ” 结尾,
数据没有压缩,占用磁盘大,开销大
2 SequenceFile
是一种二进制压缩文件,使用方便,可压缩,可分割。
支持三种压缩 : NONE, RECORD(low efficient) , BLOCK (recommend)

6.13.2 列存储

1 ORCFile / RCFile
行列存储相结合的形式
首先,按行进行分块,确保一条记录在一个块上,避免读取多个块(Block)
其次, 块数据再进行列式存储,有利于数据压缩和快速的列存储
2 Parquet
是面向列的二进制文件,Parquet 对于大型数据查询高效,
对于扫描特定表格中的特定列的查询,较为有效。
Parquet 是用压缩Snappy( default ),gzip

6.13.3 总结

压缩比: ORC > Parquet > textFile(无压缩)
查询速度:几乎一致

6.13.3 hive 支持update 和 delete

Hive 0.14版本 新增了update 和 delete 功能,但是必须遵循ACID原则
实现 update 和 delete 必须遵守ACID 原则
1.必须是 ORC (Stored As ORCFile)
2.必须是 分桶表 (clusted by (col_name_1,col_name_2) into 8 buckets)
3.表的性质为 transactional(事务性)必须设为 true

6.14 hive 命令

创建表
Create table test() row format delimated fields terminated by ‘\t’
导入文本数据到测试表
load data local inpath ‘/opt/module/datas/test.txt’into table test
显示数据库
Show databases
切换当前数据库
use db_hive;
删除空数据库
drop database db_hive2;
根据查询结果创建表
Create table表名 as select xx,xx from 表名
根据已经存在的表结构创建表
Create table 表名 like 表名
查询表的类型
Desc formatted 表名
创建分区表
Create table 表名()partitioned by (xx,数据类型)
查询分区表中数据
Select * from 表名 where 分区名=“xxx”
增加分区
Alter table 表名 add partition(xx=”xx”),partiotion(xx=”xx”)
删除单个分区
Alter table 表名 drop partition(xx=”xx”)
查看分区表有多少分区
Show partitions 表名
查看分区表结构
Desc formatted 表名
重命名表
Alter table 旧表名 rename to 新表名
删除表
Drop table 表名
基本插入数据
Insert into|overwrite table 表名 partition(xx=’xx’) values(xx,xx)
基本模式插入(根据单张表查询结果)
Insert into|overwrite table 表名 partition(xx=’xx’) select xx,xx from student where….
将查询的结果导出到本地
Insert overwrite local directory ‘xxxxxx’ select * from xxx	
不进入 hive 执行命令 :  hive  -e  “sql”
执行 hiveSql 文件 :      hive  -f  “hive_sql.sql”

6.15 hive和数据库的比较

1.查询语句 HQL:SQL
2.数据存贮位置 HDFS/OSS :块设备或者本地文件系统
3.数据更新 读多写少,不建议修改:经常修改
4.索引 没有索引,暴力扫描整个表,由于MapReduce的并行机制,也较快 :有索引
5.执行 hadoop的mapreduce来执行的:自己的执行引擎
6.执行延迟 延迟较高,且mapduce也是延迟性较高,
7.可扩展性 与hadoop的可扩展性一致
8.数据规模 hive利用mapreduce 并发机制,因此可以处理大规模的数据:

6.16 hive 生产环境中为什么建议使用外部表?

1.外部表删除不会删除源数据,只删除表结构
2.因为外部表不会加载到hive上,减少数据传输,数据还能共享
3.hive不会修改原数据,所以无需担心数据的损坏

6.17 hive 中有哪些方式保存元数据,各有什么特点?

1.默认保存在derby内存数据库中,安装小,但是数据存在内存,不稳定
2.存在MySQL中,数据存贮模式可以自己查看,持久化好,查看方便

6.18 hive 数据倾斜,如何避免

数据倾斜原因
1.数据分布不均匀,某个key 对应的记录数非常多,在 join或者 group 时会导致该 reduce 任务特别大,执行缓慢
如何避免数据倾斜?
1.过滤掉脏数据,对于无意义的脏数据,直接过滤掉
2.对数据做一下预处理,尽量保证 join的时候,同一个 key 对应的记录数不太多。
3.增加reduce 的个数 ,如果数据中出现了多个大key ,增加reduce的个数,可以避免同一个key的任务落到一个reduce 中
4.使用mapJoin ,尽量避免使用reduceJoin

6.19 hive 优化

1)MapJoin
如果不指定MapJoin或者不符合MapJoin的条件,那么Hive解析器会将Join操作转换成Common Join),即:在Reduce阶段完成join。容易发生数据倾斜。可以用MapJoin把小表全部加载到内存在map端进行join,避免reducer处理。
2)行列过滤
列处理:在SELECT中,只拿需要的列,如果有,尽量使用分区过滤,少用SELECT *。
行处理:在分区剪裁中,当使用外关联时,如果将副表的过滤条件写在Where后面,那么就会先全表关联,之后再过滤。
3)采用分桶技术
4)采用分区技术
5)合理设置Map数
(1)通常情况下,作业会通过input的目录产生一个或者多个map任务。
主要的决定因素有:input的文件总个数,input的文件大小,集群设置的文件块大小。
(2)是不是map数越多越好?
答案是否定的。如果一个任务有很多小文件(远远小于块大小128m),则每个小文件也会被当做一个块,用一个map任务来完成,而一个map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的map数是受限的。
(3)是不是保证每个map处理接近128m的文件块,就高枕无忧了?
答案也是不一定。比如有一个127m的文件,正常会用一个map去完成,但这个文件只有一个或者两个小字段,却有几千万的记录,如果map处理的逻辑比较复杂,用一个map任务去做,肯定也比较耗时。
针对上面的问题2和3,我们需要采取两种方式来解决:即减少map数和增加map数;
6)小文件进行合并
在Map执行前合并小文件,减少Map数:CombineHiveInputFormat具有对小文件进行合并的功能(系统默认的格式)。HiveInputFormat没有对小文件合并功能。
7)合理设置Reduce数
Reduce个数并不是越多越好
(1)过多的启动和初始化Reduce也会消耗时间和资源;
(2)另外,有多少个Reduce,就会有多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题;
在设置Reduce个数的时候也需要考虑这两个原则:处理大数据量利用合适的Reduce数;使单个Reduce任务处理数据量大小要合适;
8)常用参数
// 输出合并小文件
SET hive.merge.mapfiles = true; – 默认true,在map-only任务结束时合并小文件
SET hive.merge.mapredfiles = true; – 默认false,在map-reduce任务结束时合并小文件
SET hive.merge.size.per.task = 268435456; – 默认256M
SET hive.merge.smallfiles.avgsize = 16777216; – 当输出文件的平均大小小于该值时,启动一个独立的map-reduce任务进行文件merge

6.20 union 和 union all 的区别

union : 取数据并集,不存在重复数据,默认按照某个字段排序
union all : 取数据交集,数据不做去重处理,不排序

6.21

7. Kafka

7.0 什么是消息队列

(1)点对点模式(一对一,消费者主动拉取数据,消息收到后消息清除)
点对点模型通常是一个基于拉取或者轮询的消息传送模型,这种模型从队列中请求信息,而不是将消息推送到客户端。这个模型的特点是发送到队列的消息被一个且只有一个接收者接收处理,即使有多个消息监听者也是如此。
(2)发布/订阅模式(一对多,数据生产后,推送给所有订阅者)
发布订阅模型则是一个基于推送的消息传送模型。发布订阅模型可以有多种不同的订阅者,临时订阅者只在主动监听主题时才接收消息,而持久订阅者则监听主题的所有消息,即使当前订阅者不可用,处于离线状态。

7.1 Kafka 数据传输的事务定义有哪三种?

有以下三种级别:
(1)最多一次:消息不会被重复发送,最多被传输一次,但也有可能一次也不传输。
(2)最少一次:消息不会被漏发送,最少被传输一次,但也有可能重复传输。
(3)精确地一次:不会漏传输也不会重复传输,每个消息都被传输且仅仅一次。

7.2 Kafka 判断一个节点是否存活有哪两个条件?

1.通常使用zookeeper 进行通信,zookeeper 通过心跳机制检查每个节点的连接是否正常
且zookeeper 还帮助选举Controller,Controller 主要是管理整个集群所有分区和副本的状态,选举leader ,如果leader 出现故障,选举新的leader
2. Kafka动态维护了一个同步状态的副本的集合(a set of in-sync replicas),简称ISR,在这个集合中的节点都是和leader保持高度一致的,任何一条消息必须被这个集合中的每个节点读取并追加到日志中了,才会通知外部这个消息已经被提交了。因此这个集合中的任何一个节点随时都可以被选为leader.ISR在ZooKeeper中维护。ISR中有f+1个节点,就可以允许在f个节点down掉的情况下不会丢失消息并正常提供服务。ISR的成员是动态的,如果一个节点被淘汰了,当它重新达到“同步中”的状态时,他可以重新加入ISR.这种leader的选择方式是非常快速的,适合kafka的应用场景
74.Kafka 数据存储在硬盘上的消息格式是什么?
消息由一个固定长度的头部和可变长度的字节数组组成。头部包含了一个版本号和 CRC32
校验码。
消息长度: 4 bytes (value: 1+4+n)
版本号: 1 byte
CRC 校验码: 4 bytes
具体的消息: n bytes

7.3 Kafka 的数据存储在那个分区?如何确定?

1.发送消息时指定分区号。
2.发送的消息是 Key:Value 格式的数据,则根据 Key的Hash函数对分区数取余分配 一个分区。
3.未指定分区,也没有Key ,将会以循环的方式分配一个分区。

7.4 Kafka 的数据存储格式

segment file组成:由2大部分组成,分别为index file和data file,此2个文件一一对应,成对出现,后缀“.index”和“.log”分别表示为segment索引文件、日志存储文件.
producer发message到某个topic,message会被均匀的分布到多个partition上(随机或根据用户指定的回调函数进行分布),kafka broker收到message往对应partition的最后一个segment上添加该消息,当某个segment上的消息条数达到配置值或消息发布时间超过阈值时,segment上的消息会被flush到磁盘,只有flush到磁盘上的消息consumer才能消费,segment达到一定的大小后将不会再往该segment写数据,broker会创建新的segment。
存储策略:
无论消息是否被消费,kafka都会保留所有消息。有两种策略可以删除旧数据:
1)基于时间:log.retention.hours=168
2)基于大小:log.retention.bytes=1073741824
需要注意的是,因为Kafka读取特定消息的时间复杂度为O(1),即与文件大小无关,所以这里删除过期文件与提高 Kafka 性能无关。

7.5 Kafka 的ACK 机制?ISR,AR,OR分别是什么?

0:生产者不会等待 broker 的 ack,这个延迟最低但是存储的保证最弱当 server 挂掉的时候
就会丢数据
1:服务端会等待 ack 值 leader 副本确认接收到消息后发送 ack 但是如果 leader 挂掉后他
不确保是否复制完成新 leader 也会导致数据丢失
-1:同样在 1 的基础上 服务端会等所有的 follower 的副本受到数据后才会受到 leader 发出
的 ack,这样数据不会丢失
ISR: Kafka动态维护了一个同步状态的副本的集合(a set of in-sync replicas),简称ISR,在这个集合中的节点都是和leader保持高度一致的,任何一条消息必须被这个集合中的每个节点读取并追加到日志中了,才会通知外部这个消息已经被提交了。因此这个集合中的任何一个节点随时都可以被选为leader.ISR在ZooKeeper中维护。ISR中有f+1个节点,就可以允许在f个节点down掉的情况下不会丢失消息并正常提供服务。ISR的成员是动态的,如果一个节点被淘汰了,当它重新达到“同步中”的状态时,他可以重新加入ISR.这种leader的选择方式是非常快速的,适合kafka的应用场景
AR: 所有的 follower 节点
OSR: 与 leader 数据落差太大
ISR(同步状态的副本集合) = AR(所有的节点) - OSR(与leader落差较大)

7.6 Kafka 的分区和副本机制

分区:为了实现扩展性,当数据量较大时,一个服务器放不下,这是考虑分区放在多个服务器上,主要解决了单台服务器存储容量有限的问题。kafka只保证按一个partition中的顺序将消息发给consumer,不保证一个topic的整体(多个partition间)的顺序;
kafka使用分区将topic的消息打散到多个分区分布保存在不同的broker上,实现了producer和consumer消息处理的高吞吐量。Kafka的producer和consumer都可以多线程地并行操作,而每个线程处理的是一个分区的数据。因此分区实际上是调优Kafka并行度的最小单元。对于producer而言,它实际上是用多个线程并发地向不同分区所在的broker发起Socket连接同时给这些分区发送消息;而consumer,同一个消费组内的所有consumer线程都被指定topic的某一个分区进行消费。所以说,如果一个topic分区越多,理论上整个集群所能达到的吞吐量就越大。
副本:每个Topic可以建立多个副本,即数据备份,防止数据丢失,最好副本放置在不同的机架上,如果该机架down 掉,数据也不会丢失

7.7 kafka 是如何保证数据不丢失的?

79.1保证生产者端不丢失
1) 消息生产分为同步模式和异步模式
2) 消息确认分为三个状态
a) 0:生产者只负责发送数据
b) 1:某个partition的leader收到数据给出响应
c) -1:某个partition的所有副本都收到数据后给出响应
3) 在同步模式下
a) 生产者等待10S,如果broker没有给出ack响应,就认为失败。
b) 生产者重试3次,如果还没有响应,就报错。
4) 在异步模式下
a) 先将数据保存在生产者端的buffer中。Buffer大小是2万条。
b) 满足数据阈值或者数量阈值其中的一个条件就可以发送数据。
c) 发送一批数据的大小是500条。
如果broker迟迟不给ack,而buffer又满了。
开发者可以设置是否直接清空buffer中的数据。
79.2 broker端消息不丢失
broker端的消息不丢失,其实就是用partition副本机制来保证。
Producer ack -1(all). 能够保证所有的副本都同步好了数据。其中一台机器挂了,并不影响数据的完整性。
79.3消费端消息不丢失
通过offset commit 来保证数据的不丢失,kafka自己记录了每次消费的offset数值,下次继续消费的时候,会接着上次的offset进行消费。
而offset的信息在kafka0.8版本之前保存在zookeeper中,在0.8版本之后保存到topic中,即使消费者在运行过程中挂掉了,再次启动的时候会找到offset的值,找到之前消费消息的位置,接着消费,由于offset的信息写入的时候并不是每条消息消费完成后都写入的,所以这种情况有可能会造成重复消费,但是不会丢失消息。

7.8 Kafka 的数据写入和数据消费过程?

1)producer先从zookeeper的 "/brokers/…/state"节点找到该partition的leader
2)producer将消息发送给该leader
3)leader将消息写入本地log
4)followers从leader pull消息,写入本地log后向leader发送ACK
5)leader收到所有ISR中的replication的ACK后,增加HW(high watermark,最后commit 的offset)并向producer发送ACK
7.8.1 Kafka 的数据写入
数据写入基本单位是 ProducerRecord()
同步发送:
使用send ,且会返回一个Future对象,通过 get 等待,即可知道数据是否发送成功。
优点:每次发送都等待返回响应是否正常。
缺点:同一时间只能有一个消息在发送,会造成很多消息无法直接发送,造成消息滞后,无法发挥最大效益同步发送,每次发送均等待响应
val value = producer.send(new ProducerRecord(topic,kafkaKey,kafkaValue)).get()
异步发送:
通过 send 发送消息,指定一个回调函数,服务器在返回响应时调用该函数
优点:同一时间可有多个消息同时发送,无需一个一个的等待返回响应,当服务器返回响应时会自动的调用编写的回调函数,对异常情况进行处理。
producer.send(new ProducerRecord(topic,kafkaKey,kafkaValue),new Callback {
override def onCompletion(recordMetadata: RecordMetadata, e: Exception): Unit = {
if (e != null){
e.printStackTrace(“Failed to send message with Exception :”+e)}}} )}
7.8.2 kafka 的数据消费
数据写入基本单位是 ConsumerRecord()
ConsumerRecords<String, String> records = this.consumer.poll(100)

同步提交:
commitSync() 提交由poll() 方法最新的偏移量,提交成功便马上返回,提交失败便抛出异常。但是在broker 提交请求做出回应之前,程序会一直处于阻塞,会限制应用程序的吞吐量。
异步提交:
commitAsync() 只管发送提交请求,无需等待broker 的响应。
提交最后一个偏移量,继续其他的事情,无需等待broker 响应。
同步提交与异步提交的区别
异步提交不会进行重试,同步提交会一直进行重试。

7.10 Kafka高吞吐低延迟的原因?

Kafka 是顺序写磁盘,每次都是写入操作系统页缓存(Page Cache),然后由操作系统决定什么时候将页缓存中的数据写入磁盘。
–操作系统缓存:页缓存是内存中分配的,kafka 不直接参与物理的I/O 操作哦,而交给操作系统来完成
–顺序读: Kafka 采用追加的方式,皮面了磁盘的随机读写操作。
–零拷贝: 使用sendFile 为代表的的零拷贝技术加强网络间的数据传输效率。

8. Hbase

8.1 Hbase 定义

非关系型数据库,可以进行随机访问和检索数据的存储平台,存储结构化和半结构化数据,不限制存储数据的类型,允许在同一列不同行中存储不同类型的数据,将HDFS作为底层的文件存储系统。

8.2 Hbase 的组成架构

Client:访问Hbase 的接口, 与Master 通信和RegionServer通信,客户端与Hmaster进行管理类操作通信,获取RegionServer获得通信后,直接与RegionServer进行数据读写类操作,且客户端获取到Region的信息之后缓存下来,用来加速后续数据的访问过程。
Zookeeper:
1.监控Master和RegionServer的状态,
Master的选举(同一时刻只有一个处于 active 状态)
RegionServer 是否上下线,开启系统容错
2.存储Hbase 表的元数据信息
Hmaster:是Hbase 集群的主服务器,负责监控集群中所有的RegionServer
1.管理用户对表的增删改查操作
2.RegionServer的负载均衡,Region的分布
3.RegionServer的故障转移,包含Hlog的拆分和Region的转移
4.Region的拆分、合并、转移
HregionServer:
1.响应客户端请求,将数据存储到HDFS
2.RegionServer 运行在DataNode服务器上,实现数据的本地性,便于将数据存储在HDFS上
其他组件:
1.Write-Ahead logs
HBase的修改记录,当对HBase读写数据的时候,数据不是直接写进磁盘,它会在内存中保留一段时间(时间以及数据量阈值可以设定)。但把数据保存在内存中可能有更高的概率引起数据丢失,为了解决这个问题,数据会先写在一个叫做Write-Ahead logfile的文件中,然后再写入内存中。所以在系统出现故障的时候,数据可以通过这个日志文件重建。
2.Region
Hbase表的分片,HBase表会根据RowKey值被切分成不同的region存储在RegionServer中,在一个RegionServer中可以有多个不同的region。
3.Store
HFile存储在Store中,一个Store对应HBase表中的一个列族。
4.MemStore
顾名思义,就是内存存储,位于内存中,用来保存当前的数据操作,所以当数据保存在WAL中之后,RegsionServer会在内存中存储键值对。
5.HFile
这是在磁盘上保存原始数据的实际的物理文件,是实际的存储文件。StoreFile是以Hfile的形式存储在HDFS的。
8.3 Hbase 数据存储
RowKey: 行键,相当于表的索引,可以是任意长度,在Hbase 内部,存储为字节数组。存储时,按照RowKey的字典顺序进行存储。设计
Column Family: Hbase 中的某个列必须属于某个列族,列族是表的schema的一部 分(而列不是),必须在使用表之前定义。列名都以列族作为前缀。例如 courses:history,courses:math都属于courses 这个列族。
Cell:由 {table , rowkey ,columnFamily :column , version [timestamp] } 唯一确定的单元,Cell中的数据是没有类型的,全部是字节形式存储。
Timestap: 不同版本的数据都有一个时间戳,有时间戳来确定数据的版本。
Namespace: 某个表必属于某个命名空间,没有指定则是默认default

8.4 Hbase 的读数据流程

  1. 寻找数据所在的RegionServer,客户端先访问 zookeeper , 发送元数据信息,从 META 表中获取 Region所在的RegionServer
  2. 寻找数据所在的Region,根据元数据信息的Namespace , RowKey ,在RegionServer中找到数据所在的 Region
  3. 先从 MemStore (内存)中查找数据,如果没有,再到 BlackCache(缓存)中查找数据,如果都没有,再去StoreFile(磁盘)中查找
  4. 如果是从StoreFile 上获取到的数据,不是直接返回给客户端,而是先写入到 BlackCache中,再返回给客户端

8.5 Hbase 的写数据流程

  1. Client 访问zookeeper ,获取行级锁,获取元数据所对应的Region。
    2.将 数据写入 Hlog(WAL/ Write Ahead Log)和 MemSotre(写缓冲区) 各一份。
    3.当 MemStore 达到阈值,将其写入到StoreFile 中,当 StoreFile 达到阈值合并为一个StoreFile 文件。
    4.当 StoreFile 达到阈值时,Master会负责Region会进行分裂,Master 将两个Region分配给两个 RegionServer。

8.6 Hbase 的SQL工具 Phoenix

phoenix 是构建在hbase上的一个sql层,可以使用标准的JDBC API s进行查询,并不一定非要要 hbase客户端的 API s来创建表,插入数据和对 hbase数据进行查询。
phoenix 查询引擎会将sql查询转化为一个或者多个hbase扫描,并编排执行以生成标准的JDBC结果集。

1.Hbase 表默认主键是ROW,Phoenix 建表必须包含主键
单主键 (ROW STRIGN PRIMARY KEY )
如果表字段为 NOT NULL , 则必须为主键
联合主键CONSTRAINT PK PRIMARY KEY (ID,JOBNUM,LOGGNUM,VPLTNUM,PLTNUM)
2.Phoenix 对表的大小写敏感,小写必须用 “ ”
phoenix创建的表如果想删除,必须是在phoenix上进行删除,不要在hbase shell或者是hbase客户端API 删除
否则删除之后在phoenix依然可以看到该表,但是对该表执行操作的话,会抛出异常(因为底层数据依然存在HBASE,元数据被删除,则Phoenix上肯定没有数据,便会报错)
并且只有在Phoenix指定删除才能删除对应的、可能存在的二级索引表。

8.7 Hbase 的基本shell 命令

1.进入HBase客户端命令行
[hadoop@hadoop102 hbase]$ bin/hbase shell
2.查看帮助命令
hbase(main):001:0> help
3.查看当前数据库中有哪些表
hbase(main):002:0> list
1.创建表
hbase(main):002:0> create ‘student’,‘info’
2.插入数据到表 表名,rowkey,
hbase(main):003:0> put ‘student’,‘1001’,‘info:sex’,‘male’
hbase(main):004:0> put ‘student’,‘1001’,‘info:age’,‘18’
hbase(main):005:0> put ‘student’,‘1002’,‘info:name’,‘Janna’
hbase(main):006:0> put ‘student’,‘1002’,‘info:sex’,‘female’
hbase(main):007:0> put ‘student’,‘1002’,‘info:age’,‘20’
3.扫描查看表数据
hbase(main):008:0> scan ‘student’
hbase(main):009:0> scan ‘student’,{STARTROW => ‘1001’, STOPROW => ‘1001’}
hbase(main):010:0> scan ‘student’,{STARTROW => ‘1001’}
4.查看表结构
hbase(main):011:0> describe ‘student’
5.更新指定字段的数据
hbase(main):012:0> put ‘student’,‘1001’,‘info:name’,‘Nick’
hbase(main):013:0> put ‘student’,‘1001’,‘info:age’,‘100’
6.查看“指定行”或“指定列族:列”的数据
hbase(main):014:0> get ‘student’,‘1001’
hbase(main):015:0> get ‘student’,‘1001’,‘info:name’
7.统计表数据行数
hbase(main):021:0> count ‘student’
8.删除数据
删除某rowkey的全部数据:
hbase(main):016:0> deleteall ‘student’,‘1001’
删除某rowkey的某一列数据:
hbase(main):017:0> delete ‘student’,‘1002’,‘info:sex’
9.清空表数据
hbase(main):018:0> truncate ‘student’
提示:清空表的操作顺序为先disable,然后再truncate。
10.删除表
首先需要先让该表为disable状态:
hbase(main):019:0> disable ‘student’
然后才能drop这个表:
hbase(main):020:0> drop ‘student’
提示:如果直接drop表,会报错:ERROR: Table student is enabled. Disable it first.
11.变更表信息
将info列族中的数据存放3个版本:
hbase(main):022:0> alter ‘student’,{NAME=>‘info’,VERSIONS=>3}
hbase(main):022:0> get ‘student’,‘1001’,{COLUMN=>‘info:name’,VERSIONS=>3}

8.8 Hbase 的优化

7.1 高可用
在HBase中Hmaster负责监控RegionServer的生命周期,均衡RegionServer的负载,如果Hmaster挂掉了,那么整个HBase集群将陷入不健康的状态,并且此时的工作状态并不会维持太久。所以HBase支持对Hmaster的高可用配置。
1.关闭HBase集群(如果没有开启则跳过此步)
[hadoop@hadoop102 hbase]$ bin/stop-hbase.sh
2.在conf目录下创建backup-masters文件
[hadoop@hadoop102 hbase]$ touch conf/backup-masters
3.在backup-masters文件中配置高可用HMaster节点
[hadoop@hadoop102 hbase]$ echo hadoop103 > conf/backup-masters
4.将整个conf目录scp到其他节点
[hadoop@hadoop102 hbase]$ scp -r conf/ hadoop103:/opt/module/hbase/
[hadoop@hadoop102 hbase]$ scp -r conf/ hadoop104:/opt/module/hbase/
5.打开页面测试查看
http://hadooo102:16010
7.2 预分区
每一个region维护着startRow与endRowKey,如果加入的数据符合某个region维护的rowKey范围,则该数据交给这个region维护。那么依照这个原则,我们可以将数据所要投放的分区提前大致的规划好,以提高HBase性能。
1.手动设定预分区
hbase> create ‘staff1’,‘info’,‘partition1’,SPLITS => [‘1000’,‘2000’,‘3000’,‘4000’]
2.生成16进制序列预分区
create ‘staff2’,‘info’,‘partition2’,{NUMREGIONS => 15, SPLITALGO => ‘HexStringSplit’}
3.按照文件中设置的规则预分区
创建splits.txt文件内容如下:
aaaa
bbbb
cccc
dddd
然后执行:
create ‘staff3’,‘partition3’,SPLITS_FILE => ‘splits.txt’
4.使用JavaAPI创建预分区
//自定义算法,产生一系列Hash散列值存储在二维数组中
byte[][] splitKeys = 某个散列值函数
//创建HBaseAdmin实例
HBaseAdmin hAdmin = new HBaseAdmin(HBaseConfiguration.create());
//创建HTableDescriptor实例
HTableDescriptor tableDesc = new HTableDescriptor(tableName);
//通过HTableDescriptor实例和散列值二维数组创建带有预分区的HBase表
hAdmin.createTable(tableDesc, splitKeys);
7.3 RowKey设计
一条数据的唯一标识就是rowkey,那么这条数据存储于哪个分区,取决于rowkey处于哪个一个预分区的区间内,设计rowkey的主要目的 ,就是让数据均匀的分布于所有的region中,在一定程度上防止数据倾斜。接下来我们就谈一谈rowkey常用的设计方案。
1.生成随机数、hash、散列值
比如:
原本rowKey为1001的,SHA1后变成:dd01903921ea24941c26a48f2cec24e0bb0e8cc7
原本rowKey为3001的,SHA1后变成:49042c54de64a1e9bf0b33e00245660ef92dc7bd
原本rowKey为5001的,SHA1后变成:7b61dec07e02c188790670af43e717f0f46e8913
在做此操作之前,一般我们会选择从数据集中抽取样本,来决定什么样的rowKey来Hash后作为每个分区的临界值。
2.字符串反转
20170524000001转成10000042507102
20170524000002转成20000042507102
这样也可以在一定程度上散列逐步put进来的数据。
3.字符串拼接
20170524000001_a12e
20170524000001_93i7
7.4 内存优化
HBase操作过程中需要大量的内存开销,毕竟Table是可以缓存在内存中的,一般会分配整个可用内存的70%给HBase的Java堆。但是不建议分配非常大的堆内存,因为GC过程持续太久会导致RegionServer处于长期不可用状态,一般16~48G内存就可以了,如果因为框架占用内存过高导致系统内存不足,框架一样会被系统服务拖死。
7.5 基础优化
1.允许在HDFS的文件中追加内容
hdfs-site.xml、hbase-site.xml
属性:dfs.support.append
解释:开启HDFS追加同步,可以优秀的配合HBase的数据同步和持久化。默认值为true。
2.优化DataNode允许的最大文件打开数
hdfs-site.xml
属性:dfs.datanode.max.transfer.threads
解释:HBase一般都会同一时间操作大量的文件,根据集群的数量和规模以及数据动作,设置为4096或者更高。默认值:4096
3.优化延迟高的数据操作的等待时间
hdfs-site.xml
属性:dfs.image.transfer.timeout
解释:如果对于某一次数据操作来讲,延迟非常高,socket需要等待更长的时间,建议把该值设置为更大的值(默认60000毫秒),以确保socket不会被timeout掉。
4.优化数据的写入效率
mapred-site.xml
属性:
mapreduce.map.output.compress
mapreduce.map.output.compress.codec
解释:开启这两个数据可以大大提高文件的写入效率,减少写入时间。第一个属性值修改为true,第二个属性值修改为:org.apache.hadoop.io.compress.GzipCodec或者其他压缩方式。
5.设置RPC监听数量
hbase-site.xml
属性:hbase.regionserver.handler.count
解释:默认值为30,用于指定RPC监听的数量,可以根据客户端的请求数进行调整,读写请求较多时,增加此值。
6.优化HStore文件大小
hbase-site.xml
属性:hbase.hregion.max.filesize
解释:默认值10737418240(10GB),如果需要运行HBase的MR任务,可以减小此值,因为一个region对应一个map任务,如果单个region过大,会导致map任务执行时间过长。该值的意思就是,如果HFile的大小达到这个数值,则这个region会被切分为两个Hfile。
7.优化hbase客户端缓存
hbase-site.xml
属性:hbase.client.write.buffer
解释:用于指定HBase客户端缓存,增大该值可以减少RPC调用次数,但是会消耗更多内存,反之则反之。一般我们需要设定一定的缓存大小,以达到减少RPC次数的目的。
8.指定scan.next扫描HBase所获取的行数
hbase-site.xml
属性:hbase.client.scanner.caching
解释:用于指定scan.next方法获取的默认行数,值越大,消耗内存越大。
9.flush、compact、split机制
当MemStore达到阈值,将Memstore中的数据Flush进Storefile;compact机制则是把flush出来的小文件合并成大的Storefile文件。split则是当Region达到阈值,会把过大的Region一分为二。
涉及属性:
即:128M就是Memstore的默认阈值
hbase.hregion.memstore.flush.size:134217728
即:这个参数的作用是当单个HRegion内所有的Memstore大小总和超过指定值时,flush该HRegion的所有memstore。RegionServer的flush是通过将请求添加一个队列,模拟生产消费模型来异步处理的。那这里就有一个问题,当队列来不及消费,产生大量积压请求时,可能会导致内存陡增,最坏的情况是触发OOM。
hbase.regionserver.global.memstore.upperLimit:0.4
hbase.regionserver.global.memstore.lowerLimit:0.38
即:当MemStore使用内存总量达到hbase.regionserver.global.memstore.upperLimit指定值时,将会有多个MemStores flush到文件中,MemStore flush 顺序是按照大小降序执行的,直到刷新到MemStore使用内存略小于lowerLimit

9. Flume

9.1 Flume 定义

Flume 是一个高可靠,高可用,分布式的海量日志采集系统,可以实时采集数据的流式系统。

9.2 Flume 的组件有哪些?

Agent : 是一个Java应用程序,接受并生产数据并缓存数据,直至最终写入到其他Agent中或者是存储系统中。
Event : 数据传输的基本单元
Source : 数据源头,负责接收数据
Channel :是Source与 Sink之间的缓冲区,数据被暂存在channel
Sink :不断地轮询Channel中的事件且批量地移除它们,并将这些事件批量写入到存储或索引系统、或者被发送到另一个Flume Agent。Sink 是完全事务性的,在每次Channel 批量删除数据之前,每个Sink会用Channel启动一个事务,批量事件一旦写入成功或者传入下一个Agent,Sink就会利用 Channel 提交事务。事务一旦提交,Channel会将这些事务从自己的内部缓冲区中删除。
(开启事务,在确保数据由Sink 传输到 Channel之前,Channel 都不会将事件删除)

9.3 Flume 组件各有哪些类型?

一、Source类型
NetCat 、Avro 、Exec、 Spooling Directory、 Taildir
1.Netcat 有UDP 和 TCP两种协议模式,使用方法基本相同,通过监听端口号来传输数据,将监听的每行数据都转化为一个 event, 写入到Channel中
2.Avro 不同主机上的Agent通过网络传输数据可使用的Source,一般是接受 avro client的数据, 或和是上一级Agent的Avro Sink成对存在
3.Exec 执行 Unix / Linux 命令,传输数据 如 cat 、tail -F 、等实时性较高,但是一旦agent 出现问题的话,数据可能会丢失
4.spooldir 监听整个文件夹,如果有新增文件,将新增文件内容转换成Event传输数据,特点是 数据不会丢失,但实时性较低
缺点: 1. 不能对被监控的文件夹下的新增文件做出任何的修改
2. 新增到监控系统的文件名称必须是唯一的
2.Taildir 可以实时的监控一个或者多个文件中的新增内容,由于该数据是将数据的偏移量保存到一个指定的 json 文件中,因此在Agent挂掉的或者kill 的时候不会有数据的丢失。但不能再Windows上使用
3. Kafka Source 收集kafka 传输的数据
二、Channel 类型
1.Memory Channel:
是内存中的队列,程序宕机或者重启都会导致数据的丢失

  1. File Channel :
    将所有事件写入磁盘,程序宕机和重启都不会丢失数据。
    三、 Sink的类型
    常用的Sink有 Log sink 、HDFS sink、 Avro sink、 Kafka sink 当然也可以自定义Sink
    1.Logger Sink 指的是 INFO级别的日志记录到 log 日志中,常用于测试
    2.HDFS Sink 目前支持textFile 和SequenceFile 两种文件格式,支持压缩,并可以对数据进行分区,分通的操作
    3.Avro Sink 多个不同主机上的Agent 进行数据传输
    4.Kafka Sink 传输数据到kafka 中
    自定义Source,Sink

9.4 Flume Agent 内部原理

9.5 Flume 与 Kafka 区别

Flume :
1.适合下游数据消费者不多的情况 (一个消费者一个 channel)
2.适合数据安全性要求不高的操作 ( 数据在channel 中,没有备份,没有副本)
3.适合与Hadoop对接(Hbase, Hdfs)
适合生产和收集数据
Kafka :
1.适合下游消费者较多的情况(kafka 从磁盘读, 仅从leader 读)
2.适合数据安全性较高的操作,支持 replication(副本)
适合消费数据

9.6 Flume 采集过程中数据会丢失吗?如何避免数据丢失?

File Channel 不会丢数据
也可以设置 file channel , 将数据存储在File 中,其内部有完善的事务机制(ACID),数据传输自身有事务。Source 到 channel 是事务性的,Channel到Sink是事务性的, Sink 从Channel 中获取数据,会先开启一个事务,直到channel 中所有的Event 写入成功或者传入下一个Agent。
Memory Channel 会丢数据
Agent宕机导致数据在内存中丢失,Channel写入已满,导致Source 不再写入数据,导致未写入的数据丢失。

9.7 Flume 管道内存,flume 宕机了数据丢失怎么解决?

1)Flume 的 channel 分为很多种,可以将数据写入到文件
2)防止非首个 agent 宕机的方法数可以做集群或者主备

9.8 Flume 的事务机制

Flume的事务机制(类似数据库的事务机制):Flume使用两个独立的事务分别负责从Soucrce到Channel,以及从Channel到Sink的事件传递。比如spooling directory source 为文件的每一行创建一个事件,一旦事务中所有的事件全部传递到Channel且提交成功,那么Soucrce就将该文件标记为完成。同理,事务以类似的方式处理从Channel到Sink的传递过程,如果因为某种原因使得事件无法记录,那么事务将会回滚。且所有的事件都会保持到Channel中,等待重新传递。

9.9 Flume 调优

  1. Source
    增加Source个(使用Tair Dir Source时可增加FileGroups个数)可以增大Source的读取数据的能力。例如:当某一个目录产生的文件过多时需要将这个文件目录拆分成多个文件目录,同时配置好多个Source 以保证Source有足够的能力获取到新产生的数据。
    batchSize参数决定Source一次批量运输到Channel的event条数,适当调大这个参数可以提高Source搬运Event到Channel时的性能。
  2. Channel
    type 选择memory时Channel的性能最好,但是如果Flume进程意外挂掉可能会丢失数据。type选择file时Channel的容错性更好,但是性能上会比memory channel差。
    使用file Channel时dataDirs配置多个不同盘下的目录可以提高性能。
    Capacity 参数决定Channel可容纳最大的event条数。transactionCapacity 参数决定每次Source往channel里面写的最大event条数和每次Sink从channel里面读的最大event条数。transactionCapacity需要大于Source和Sink的batchSize参数。
  3. Sink
    增加Sink的个数可以增加Sink消费event的能力。Sink也不是越多越好够用就行,过多的Sink会占用系统资源,造成系统资源不必要的浪费。
    batchSize参数决定Sink一次批量从Channel读取的event条数,适当调大这个参数可以提高Sink从Channel搬出event的性能。

10.Spark

1. Spark的内置项目

Spark core:实现了spark的基本功能,包含任务调度,内存管理,错误恢复与存储系统交互等模块。Spark core中还包含了对弹性分布式数据集(RDD)的API定义
Spark sql:是spark用来操作结构化数据的程序包。通过spark sql,我们可以使用sql或者hive版本的sql来查询数据,spark支持多种数据源,比如hive表、parquet以及Jason
Spark streaming:是spark提供的对实时数据进行流式计算的组件。提供了用来操作数据流的API,并与spark core中的API高度匹配
Spark mllib:提供常见的机器学习(ML)功能的程序库,包括分类,回归,聚类,协同过滤等。
还提供了模型评估,数据导入等额外的支持功能

2.Spark的部署模式

资源管理器 :主要是分配资源,包含CPU ,内存 ,带宽等
  1)本地模式:跑在本地,不是集群,但可以以多线程运行。适用于测试 ,local[K] 启动k 个executor 线程执行任务
  2) standalone 模式:分布式部署模式, 使用spark自带的资源调度框架,自带完整的服务,资源管理和任务监控是spark 自己管理
  3) spark on yarn 模式:分布式集群部署模式,使用yarn集群调度资源,yarn-cluster(适用生产) 与 yarn-client (适用调试)
  4) mesos模式:推荐使用,与spark 存在血缘关系

3.Spark的执行过程

每个 application 会生成很多的作业 ,每个作业会被切分成不同的阶段
应用(application) --> 作业 (homework) --> 不同的阶段(stage)–>任务的集合

1.为应用构建起基本的运行环境,即由Driver创建一个SparkContext进行资源的申请、任务的分配和监控
2.Driver与Cluster Manager 通信申请资源,资源管理器为Executor分配资源,并启动Executor进程,这些进程分布在不同的节点上
3.SparkContext根据RDD的依赖关系构建DAG图,DAG图提交给DAGScheduler解析成Stage,然后把一个个TaskSet提交给底层调度器TaskScheduler处理。
Executor向SparkContext申请Task,TaskScheduler将Task发放给Executor运行并提供应用程序代码。
4.Task在Executor上运行把执行结果反馈给TaskScheduler,然后反馈给DAGScheduler,运行完毕后写入数据并释放所有资源。

4. Spark 名词解释

Driver :每个应用的任务控制节点
即Application的main 函数并创建SparkContext,创建SparkContext 的目的是为了准备 spark 应用程序的运行环境,在spark中SparkContext 负责与Cluster Manager 进行通信,进行资源申请、任务分配和监控等,当 Executor 部分运行结束后,Driver负责将SparkContext关闭。
Cluster Manager : 集群资源管理器 (Spark自带管理器 ,Yarn ,Mesos)
Worker Node : 运行作业任务的工作节点
SparkContext :代表与集群的一个连接
Executor :运行在工作节点(Worker Node) ,是一个进程,可以派生出很多的线程。
RDD 只读的弹性分布式数据集(高度受限的共享内存模型)
DAG 反映RDD之间不同的依赖关系
Spark 将数据加载到内存中,转化成 RDD ,最终 RDD 会将其转化成 有向无环图。
Application 用户编写的一个Spark应用程序(词频统计代码)
Job 每个application 会被分为多个Job ,每个 job 被划分为多个Stage。 一旦Driver中出现action,就会生成一个Job,所以Driver中有多少个Action就有多少个Job
Stage 切割:划分Stage 过程是根据操作是宽依赖还是窄依赖进行划分stage ,将连续的窄依赖划分为一个stage,当遇到宽依赖时(发生 shuffle 操作)进行切割生成一个新的Stage

5.Spark 优点,相比于MapReduce的优势

优点: 批处理,延迟低,组件多
相比于 mapReduce 优势:
1.MapReduce每次执行完 map 或者reduce 之后,数据都是保存在磁盘上(磁盘消耗IO过大,如果是对象,还需要对对象进行序列化和反序列化)
2.Spark 则是将中间结果数据缓存在内存中,反复迭代结果是在内存中
2.1DAG执行引擎,中介结果不落盘
2.2充分利用内存,减少磁盘 IO
2.3更适合迭代计算,迭代计算不需要每次都去磁盘读,可以缓存在内存中,减少IO

6.Spark RDD

RDD(Resilient Distributed Dataset)弹性的分布式数据集,代表一个不可变,可分区,里面元素可以进行并行计算的集合。每个RDD有多个分区,每个分区的数据存储在不同的Worker Node 节点上。
RDD支持两种操作,转化操作和行动操作。转化操作是返回一个新的RDD的操作,比如map和filter,而行动操作则是向驱动程序返回结果或把结果写入外部系统的操作,比如count和first.
Spark采用惰性计算模式,RDD只有第一次在一个行动操作中用到时,才会真正计算
6.1 RDD 创建
scala> val array=Array(1,2,3,4,5)
scala> val rdd = sc.parallelize(array,2) //设置两个分区
scala> val rdd = sc.makeRDD(array,2) //设置两个分区
scala> val data = sc.textFile(filepath,2) //设置两个分区
scala> data.paritions.size //显示这个RDD的分区数目
scala> data.repartition(1) //对这个RDD进行重新分区
6.2 RDD 分区
6.2.1 设置分区

  1. 启动时设置分区:
    /**RDD逻辑上是分区的,每个分区的数据抽象存在,计算的时候通过一个函数计算每个分区的数

    *分区的好处: 增加并行度,实现分布式计算,减少通讯开销
    *local(本地模式) 默认是本地机器CPU的数目
    *local[N] 设置为 N个
  • Mesos 模式,不设置的话,默认是8
    */
  • conf.setMaster(local[numPartition])
  1. 创建RDD时设置分区
    scala> val rdd = sc.parallelize(array,2) //设置两个分区
    scala> val rdd = sc.makeRDD(array,2) //设置两个分区
    scala> val data = sc.textFile(filepath,2) //设置两个分区
    分区的优先级 : 创建时设置 parallelize| makeRDD| textFile > conf.setMaster(local[numPartition])
    6.2.2 重分区
    Coalesce 与 repartition 的区别:
    Coalesce 是进行重分区,使用HashPartitioner ,第一个参数为重分区的数目,第二个为是否进行 shuffle,默认是 false
    Repartition 其实是 coalesce的第二个参数设置为 shuffle = true
    def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] = withScope {

coalesce(numPartitions, shuffle = true)
}

Scala> val size_04 = sc.parallelize(list,2).repartition(3).partitions.size
scala> val rdd_02 = sc.parallelize(list,2).coalesce(4)
6.3 RDD 缓存
6.3.1 cache 与persist 区别

/** RDD 缓存,RRDD之间具有较长的血缘关系,在一个应用程序中,若果经常或者多次使用同一个RDD,
* 可以将其缓存起来,那么下次调用该RDD时,只有在第一次使用的时候走血缘关系,下次使用的时候不必再根据血缘关系去得到分区的数据,会直接从缓存处取。
*
* cache() cache 内部调用了 persist(StorageLevel.MEMORY_ONLY)
* persist() 保存有两种 MEMORY_ONLY : 如果内存不足时,将Old给挤出去
* MEMORY_AND_DISK : 如果内存不足,会将其存到磁盘,不会将old 挤出去
* checkpoint 是将血缘关系较长的逻辑计算的中间结果缓存到HDFS上,而cache 是缓存在内存中
*
*
* 以上两种缓存方式不是调用缓存就立即被缓存了,而是触发后面的action时,将RDD缓存到内存中,并提供后续的使用
* 将数据以序列化的方式缓存在JVM的堆内存中
*
*/
val rdd_01 = sc.parallelize(list,2)
val rdd_0 = rdd_01.cache() //res0: rdd_01.type = ParallelCollectionRDD[0] at parallelize at :29

rdd_01.persist(StorageLevel.MEMORY_AND_DISK)

// 同一个RDD只能被缓存一个 StorageLevel ,再次对该RDD进行缓存,则抛出异常 java.lang.UnsupportedOperationException: Cannot change storage level of an RDD after it was already assigned a level
rdd_01.persist(StorageLevel.MEMORY_ONLY)
// 可以将该RDD 移除缓存,对其进行缓存一个 StorageLevel
rdd_01.unpersist()
6.3.2 cache 与 checkpoint区别
checkpoint 介绍
长时间的迭代和血缘关系,使得RDD之间血缘关系链会越来越长,一旦后续迭代出现错误,需要通过非常长的血缘关系去迭代,影响性能。
RDD支持checkpoint,将数据保存到持久化的存储中,这样就可以切断之前的血缘关系。
检查点(本质是通过将RDD写入磁盘做检查点,从而减少lineage过长而造成的容错成本过高的(血统关系)),因此不如在lineage的中间做检查点,如果之后有节点出现问题而丢失分区,从做检查点的RDD开始重做lineage,就会减少开销。
cache 与 checkpoint 的区别
Cache : 是将公用的护着重复使用的RDD按照持久化的级别进行缓存 在内存
Checkpoint: 将lineage非常长的逻辑计算的中间结果缓存到HDFS上,实现原理:
1.首先找到stage最后的finalRDD的,然后按照RDD的依赖关系回溯,找到使用checkpoint的RDD。
2.标记这个checkpoint的RDD
3.重新启动一个线程将checkpoint之前的RDD缓存到HDFS上
4.强烈推荐先将数据持久化到内存中(cache操作),否则直接使用checkpoint会开启一个计算,浪费资源。
缓存把RDD计算出来然后放在内存中,但是RDD的lineage,也不能丢掉。当某个节点某个executor宕机了,上面cache的RDD就会丢掉。需要通过lineage重新计算找回。
Checkpoint是将RDD保存在HDFS上,是多副本可靠的存贮,所以依赖链就可以丢掉了。
cache 与 checkpoint 的区别

  1. cache 只是将数据保存起来,不切断血缘依赖。而Checkpoint 检查点切断血缘依赖.
  2. cache 缓存是将数据缓存到内存、磁盘等地方,可靠性较低。Checkpoint将数据存储在HDFS等容错,高可用的文件系统,可靠性较高
  3. 建议对Checkpoint 的数据先进行一次cache 缓存,这样Checkpoint 只需要从cache 中读数据,而不需要重新计算一次
    6.4 RDD 共享变量
    在应用开发中,一个函数被传递给Spark操作(例如map和reduce),在一个远程集群上运行,它实际上操作的是这个函数用到的所有变量的独立拷贝。这些变量会被拷贝到每一台机器。通常看来,在任务之间中,读写共享变量显然不够高效。然而,Spark还是为两种常见的使用模式,提供了两种有限的共享变量:广播变量和累加器。
    6.4.1 广播变量
    广播变量(Broadcast Variables)
    – 广播变量缓存到各个节点的内存中,而不是每个 Task
    – 广播变量被创建后,能在集群中运行的任何函数调用
    – 广播变量是只读的,不能在被广播后修改
    – 对于大数据集的广播, Spark 尝试使用高效的广播算法来降低通信成本
    val broadcastVar = sc.broadcast(Array(1, 2, 3))方法参数中是要广播的变量
    6.4.2 累加器
    累加器只支持加法操作,可以高效地并行,用于实现计数器和变量求和。Spark 原生支持数值类型和标准可变集合的计数器,但用户可以添加新的类型。只有驱动程序才能获取累加器的值
    11.spark-submit的时候如何引入外部jar包:
    在通过spark-submit提交任务时,可以通过添加配置参数来指定
    –driver-class-path 外部jar包
    –jars 外部jar包

6.5 RDD常用操作

6.5.1 转换操作

1. Transformation

val lines=sc.textFile(file:///usr/local/spark/mycode/rdd/word.txt)

1. map map(func) 将每个元素传递给函数 func 中,并将返回结果返回为一个新的数据集

scala> val data=Array(1,2,3,4,5)
scala> val rdd1 = sc.parallelize(data)
scala> val rdd2=rdd1.map(x => x+10)
scala> val words=lines.map(line => line.split(“”)) //最后得到的是多个数组

1.2 mapPartitions 将待处理的数据以分区为单位发送到计算节点进行处理,

map 与mapPartitions 有什么不同?
Map 内部是调用了 mapPartition
def map[U: ClassTag](f: T => U): RDD[U] = withScope {

val cleanF = sc.clean(f)
new MapPartitionsRDD[U, T](this, (context, pid, iter) => iter.map(cleanF))
}

1.数据处理角度不同
Map 是一行一行数据的处理,类似于串行执行. mapPartitions 是以分区为单位进行批处理工作。
2.功能角度
Map 是对数据进行转换和改变,但是数据不会增加和减少
mapPartitions 需要传递一个迭代器,返回一个迭代器,没有要求的数据的个数保持不变,所以可以增加或者减少数据量
3.性能角度
Map 是类似于串行处理的,性能较低。
MapPartitions 是类似于批处理的,性能较高。
但MapPartitions会长时间占用内存,导致内存可能不够用,出现内存溢出,所以在内存有限的情况下,不推荐使用。

2. flatmap flatMap(func) 与map()相似,将map 的输出结果进一步的压平,每个输入元素可以映射到多个输出结果

flatMap 的执行过程: 首先是 map ,得到三个 Array数组,再将每个数组拍平,所有的数据混合到一起。9 scala> val words=lines.flatmap(line => line.split(“”))

3. filter filter(func)将每个元素传递给函数 func 中,并返回一个新的数据集

val linesWithSpark = lines.filter(line => line.contains(“spark”))

4. groupByKey groupByKey() 应用于 (k,v) 键值对的数据集,返回一个新的(k,Iterable)形式的数据集

rdd = (“hadoop”,1),(“spark”,1),(“hadoop”,1),(“hive”,1) rdd.groupByKey() //(“hadoop”,(1,1)) (“spark”,1) (“hive”,1)

5. reduceByKey reduceByKey() 应用于 (k,v) 键值对的数据集时,返回一个新的 (k,v) 形式的数据集,其中根据Key 对每个值再进行函数 func 进行操作,

rdd = (“hadoop”,1),(“spark”,1),(“hadoop”,1),(“hive”,1) rdd.reduceByKey((a,b => a+b)) //(“hadoop”,(2)) (“spark”,1) (“hive”,1)

6. groupByKey 与 reduceByKey 有什么区别

groupByKey 只会进行分组
reduceByKey 分组并将结果根据聚合函数进行汇总
rdd = (“hadoop”,1),(“spark”,1),(“hadoop”,1),(“hive”,1) val groupRdd=rdd.groupByKey() //(“hadoop”,(1,1)) (“spark”,1) (“hive”,1) groupRdd.map(t=>t._1,t._2.sum()) //(“hadoop”,(2)) (“spark”,1) (“hive”,1) rdd.reduceByKey((a,b => a+b)) //(“hadoop”,(2)) (“spark”,1) (“hive”,1)
6.5.2 行动操作

2. Action
1. count() 统计数据集中元素个数

rdd.count() //5

2. collect() 以数组的形式返回数据集中所有元素,否则可能散布在不同的机器上

rdd.collect() //Array[1,2,3,4,5]

3. first() 返回第一个元素

rdd.first() //1

4. take(n) 返回前 n个元素

rdd.take(2) //Array[1,2]

5. reduce(func) 通过函数 func (输入两个参数并返回一个值) 聚合数据集中的元素

rdd.reduce((a,b) => a+b) //1+2+3+4+5 =15

6. foreach(func) 将数据集中的每个元素传递到函数 func 中运行

rdd.foreach(elem => println(elem)) //1 2 3 4 5

7.collect() 将RDD 的数据可能分布在多台机器上,使用collect(收集) 将数据生成一个数组 Array()

Rdd.collect() // Array[tag] = Array(tag(tag,time,value), tag(tag_01,time_01,value_01))
6.5.3键值对RDD

3.键值对RDD/PairRDD

RDD 一般分为数值RDD和键值RDD
所有的RDD都是延迟加载的,只有遇到真正的Action才会进行计算(只有当发生一个要求返回结果给Driver的动作,这些转化才会执行)。
键值RDD 常见的操作有 reduceByKey() | groupByKey() | keys() | values() | combineByKey() | join() | mapValues(func) |sortByKey()

1. keys() 将 PairRDD 中的 key 返回

scala> rdd = (“hadoop”,1),(“spark”,1),(“hadoop”,1),(“hive”,1)
scala> rdd.keys().foreach(println) // hadoop,spark,hadoop,hive

2. values() 将 PairRDD 中的 value 返回

scala> rdd.values().foreach(println) // 1,1,1,1

3. sortByKey() 将 PairRDD 中的 value 按照 Key 进行排序 ,默认是按照升序排序,sortByKey(false) 按照降序排

sortBy() 可以按照 value 进行排序
scala> rdd =sc.parallelize(Array((“a”,1),(“c”,2),(“d”,5),(“b”,95),(“d”,2)))
scala> rdd.reduceByKey(+).sortByKey(flase).collect //Array((“d”,7),(“b”,95),(“c”,2))
scala> rdd.reduceByKey(+).sortBy(_._2,false).collect //Array((“b”,95),(“d”,7),(“c”,2))

4. mapValues(func) 将 PairRDD 中的 map不变,对 value 进行 函数

scala> rdd = (“hadoop”,1),(“spark”,1),(“hadoop”,1),(“hive”,1)
scala> rdd.mapValue(x=>x+1).foreach(println) // (“hadoop”,2),(“spark”,2),(“hadoop”,2),(“hive”,2)

5. join() 把几个RDD当中元素 key 相同的进行连接

scala> rdd = (“hadoop”,1),(“spark”,1),(“hadoop”,2),(“hive”,1)
scala> rdd2 = (“hadoop”,“new”)
scala> rdd.join(rdd2).foreach(println) // (“hadoop”,(1,new)),(“hadoop”,(2,new))

4.惰性机制

将 transformation 的轨迹记录下来,只有遇到 action 才会真正的去执行
只有在遇到action时,才会操作,之前都只是记录轨迹。

5.WordCount 实例

scala> val lines=sc.textFile(“file:///words.txt”)
scala> val wordcount=lines.flatMap(line=> line.split(“”)) | .map(word =>(word,1)) //或者 map(,1) | .reduceByKey((a,b) => a+b) //或者 reduceByKey(+_) scala> wordcount.collect() scala> wordcount.foreach(println)

6.PairRDD之间的转化操作实例

key : 图书名称
value : 某天图书的销量
求 每种图书每天的平均销量
scala> val rdd=sc.parallelize(Array((“spark”,2),(“hadoop”,3),(“hive”,1),(“spark”,1))) //(“BookName”,(sales,每天)) reduceByKey 之后 (“BookName”,(TotalSales,TotalDay)) 再进行mapValues() (“BookName”,everyDayAvgSales)
scala> rdd.mapValues(x=>(x,1)).reduceByKey((x,y)=>(x._1+y._1,x._2+y._2)).mapValues(x=>x._1 / x._2).collect()
6. 宽依赖和窄依赖区别
宽依赖: (一对多) 每一个分区的输入对应多个分区的输出 , shuffle 称为洗牌,shuffle 过程一定会将数据写入到磁盘,即使spark 是基于内存计算的。
窄依赖: (一对一) 每一个分区的输入对应一个分区的输出。
7.Spark Context 与 SparkSession
SparkCore sc: SparkContext 处理 RDD
SparkSQL spark: SQLContext、HiveContext、SparkSession 处理 DataFrame
SparkStreaming ssc: SparkStreamingContext 处理实时数据
8. Spark中的RDD、DataFrame 、DataSet
2.1不同点
SparkContext :sc : RDD
SparkSession : spark : DataFrame
RDD是一个弹性的分布式数据集,懒执行不可变的并且支持Lambda表达式
不支持Sparksql 操作, 一般和 spark mlib 同时使用
DataFrame相当于是二维的表格,是DataSet的特例,Data Frame=DataSet [Row],是只知道字段,但是不知道字段的类型 每一行的类型固定为ROW,只有通过解析才能获取各个字段的值,每一列的值没法直接访问
RDD 往往是相当于是被封装的对象
Data Frame 是相当于一个二维表格,货架,直接显示数据,而不需要在将对象打开查看数据。
DataSet 是强类型的,可以知道字段的类型,可以过滤类型错误。
2.2 相同点

  1. 都是Spark下的弹性数据集
  2. 三者都是惰性机制
  3. 三者都有partition的概念
  4. Dataset 和 DataFrame均支持 sparksql 操作,还支持一些简单的保存方式 csv , 可以带上表头
    还可以注册临时表/视窗
    2.3 三者的相互转换
1. DataSet / DataFrame 转 RDD

scala> val RDD01=testDF.rdd
scala> val RDD02=testDS.rdd
##2. RDD 转 DataFrame
import spark.implicits._
scala> val testDF = rdd.map {line => (line.1,line.2)}.toDF(“col1”,“col2”)
scala> val targetDF = sQLContext.createDataFrame(rdd, df_schema)
##3.RDD 转 DataSet
import spark.implicits.

scala> case class Coltest(col:String,col2:Int) extends Serializable
scala> val testDS = rdd.map{line=> (line.1,line.2)}.toDS
##4.DataSet 转 DataFrame
import spark.implicits.
val testDF = testDS.toDF
##5.DataFrame 转 DataSet
import spark.implicits.

scala> case class Coltest(col:String,col2:Int) extends Serializable
scala> val testDS = testDF.as[Coltest]
注意: 在使用一些特殊的操作时,一定要 import spark.implicits.
否则 toDF、toDS 无法使用。

9. Spark 的内存模型

9.1.1 堆内内存
"spark.executor.memory"指定的内存为JVM最大分配的堆内存(“-xmx”)
静态内存管理 :是指存储内存,执行内存和其他内存大小在spark 程序期间都是固定的,Storage占系统内存的 60% , Exeution占系统内存的 20%, 且二者完全独立
Storage: Storage内存用于缓存数据 存储broadcast,cache,perpist 数据
Execution: 用于存放shuffle ,join sort, aggregation 等计算过程中的临时数据
User Memory(用户内存):主要存储RDD转换操作时所需的数据,例如RDD依赖等信息
Reserved Memory(预留内存):系统预留内存,用户定义的数据结构或者Spark内部对象。
UsableMemory = SystemMemory – ReservedMemory
统一内存管理 : 是指存储内存和执行内存公用同一块内存空间,可以动态占用对方的空闲区域,由 Storage 与 Execution 两种内存所组成,二者各占统一内存的50%
Storage: 缓存RDD数据(Cache、Persist)和广播(BroadCast)
Execution :执行shuffle 时占用的内存称为执行内存,shuffle中产生的中间数据
由于动态占用机制的实现,当 shuffle 过程需要的内存过大时,会自动的占用 Storage的内存呢,无需手动进行调节。
9.1.2 堆外内存
Spark-1.6.0 之后加入了堆外内存,堆外内存就是JVM堆以外的内存,不在JVM内申请内存,不会被GC回收,可以减少频繁的full GC。但是需要自己编写申请和释放内存的逻辑。
默认情况下 ,堆外内存是关闭的,可通过 spark.memory.offheap.enable 参数启用
通过 spark.memory.offheap.size 设置堆外内存大小,单位和字节
如果堆外内存被启用,则这时Executor 的内存包括堆内内存和堆外内存,同理Storage 内存也一致。与堆内内存相比,堆外内存只包含Execution内存和Storage内存。

10. Spark 中的OOM(内存溢出)问题

 OOM问题通常就出现在execution内存中
 OOM问题的两种情况:
map执行中内存溢出
omap执行中内存溢出代表了所有map类型的操作,包括:flatMap,filter,mapPatitions等
shuffle后内存溢出
oshuffle后内存溢出的shuffle操作包括join,reduceByKey,repartition等操作
 内存溢出的解决办法:
map过程产生大量对象导致内存溢出:
o在不增加内存的情况下,可以通过减少每个Task的大小,以便达到每个Task即使产生大量的对象Executor的内存也能够装得下,具体做法可以在会产生大量对象的map操作之前调用repartition方法,分区成更小的块传入map
数据不平衡导致的内存溢出
o与map过程的相同,也是调用repartition进行重新分区
shuffle后的内存溢出:
oshuffle内存溢出的情况可以说都是shuffle后,单个文件过大导致的,如果是别的partitioner导致的shuffle内存溢出,就需要从partitioner的代码增加partitions的数量
standalone模式下资源分配不均匀导致内存溢出:
o在standalone的模式下如果配置了–total-executor-cores 和 --executor-memory 这两个参数,但是没有配置–executor-cores这个参数的话,就有可能导致,每个Executor的memory是一样的,但是cores的数量不同,那么在cores数量多的Executor中,由于能够同时执行多个Task,就容易导致内存溢出的情况。这种情况的解决方法就是同时配置–executor-cores或者spark.executor.cores参数,确保Executor资源分配均匀
在RDD***用对象能够减少OOM的情况

11. Spark中的Shuffle机制

一个任务对应一个分区,通常不会跨分区操作数据,除非遇到需要汇总所有数据的操作例如,reduceByKey(),Spark 需要从所有分区读取数据,并找到所有键的所有值,然后汇总在一起计算每个键的最终结果,这个过程就成为shuffle
Shuffle Write: 上一个stage的每一个 map task 就必须保证自己处理的当前分区的中的数据,相同的key 写入一个分区文件中,可能写入多个不同的分区文件中
Shuffle Read:reduce Task 会从上一个Stage的所有Task所在的机器找寻属于自己的那些分区文件,这样就可以保证每一个key 所对应的Value都会汇集在同一个节点上去处理和集合。
会导致Shuffle出现的算子:
涉及到重分区操作 repartition 、coalesce
所有涉及到ByKey 的操作 : groupByKey,ReduceByKey,CountByKey
联合操作: corGroup, Join

12.Spark中的 reduceByKey 与 groupByKey 哪个性能高?

ReduceByKey 是在传输之前首先在每个分区上对相同的key 的数据进行聚合,然后再传输之后在进行聚合
GroupByKey 是将分区中所有的键值对经过网络传输之后在进行聚合。
因此在传输大数据集时,ReduceByKey 性能高于 GroupByKey

13.Spark的容错机制

容错:容错指当一个系统在部分模块出现故障时,还能否持续的对外提供服务
数据检查点和 记录数据的更新

14.Spark的性能调优

1.在使用Spark 提交程序的时候,合理的配置资源
2.对多次使用的RDD进行持久化或者checkPoint
3.在ReduceByKey和groupByKey中进行合理的选择
4.对shuffle 阶段进行优化
5.广播共享数据
6.优化所使用的数据结构
7.使用序列化的持久化级别,将数据序列化之后再持久化,可以减小内存开销。

15.Spark 中的Partitioner(分区器)

只有key-Value 格式的数据才有分区器
HashPartitioner: 使用Object.hashCode 实现基于散列的分区,hashCode的概念是相同的对象应该具有相同的哈希码,基于hashCode ,HashPartitioner将具有相同hashCode的键分到同一个分区中,是默认分区。
RangePartitioner: 按范围将可排序记录划分为大致相等的范围,范围通过对传入的RDD的内容进行采样来确定,即RangePartitioner将根据Key 对记录进行排序,根据指定的值将记录划分为多个分区。
CustomPartitioner(自定义分区器): 通过扩展Spark中默认的Partitioner 类以及应该存储在该分区中的内容,然后通过PartitionBy() 在RDD上应用自定义分区逻辑

16.Spark yarn模式下的 cluster 与 client 的区别

Spark on YARN模式根据Driver在集群中的位置分为两种模式:一种是YARN-Client模式,另一种是YARN-Cluster(或称为YARN-Standalone模式)。
(1)SparkContext初始化不同,这也导致了Driver所在位置的不同,
Yarn-Cluster 模式下,Driver运行在AM(Application Master)中,它负责向YARN申请资源,并监督作业的运行状况,当用户提交作业之后,Client可以关掉,作业会继续运行在YARN上。
YARN—Client 模式下,Application Master 仅仅 向 YARN请求 Executor,Client会和请求的Container通信来调度他们的工作,也就是Client不能离开
(2)最后再来说应用场景,Yarn-Cluster适合生产环境,Yarn-Client适合交互和调试。

17.Spark 数据倾斜

数据倾斜:在执行任务时,RDD会被分为一系列分区,每个分区都是整个数据集的子集,当Spark调度并运行任务的时候,Spark会为每一个分区中的数据创建一个任务。大部分的任务处理的数据量差不多,但是有少部分的任务处理的数据量很大,因而Spark运行起来会十分的慢,从而产生数据倾斜
数据倾斜只出现在shuffle 过程,可能触发shuffle的算子:
groupByKey,ReduceByKey,Join,CogGroup,Distinct,aggregateByKey,Repartition,Coaleasc等
解决方法:
过滤少量导致数据倾斜的Key
对Key添加随机前缀,在执行操作后在去掉前缀
自定义Partitioner

18.spark 共享变量

18.1 广播变量
18.2 累加器

19.spark特点

快,与mapreduce相比,spark基于内存的运算要快100倍左右
易用,spark支持java、python和scala的api,还支持超过80种的算法,使用户可以快速构建不同的应用。而spark支持交互式的python和scala的shell,可以非常方便的在这些shell中使用spark解决验证问题
通用,spark提供统一的解决方案。Spark可以用于批处理,交互式查询,实时流处理,机器学习和图计算。这些不同类型的处理都可以在同一个应用中无缝使用。
兼容性,spark可以非常方便的与其他开源产品进行融合,可以使用yarn作为资源调度器,可以处理所有Hadoop支持的数据

20.Spark优化

21.spark 序列化

22. Spark中RDD,DataFrame,DateSet 区别

RDD:弹性的分布式数据集,代码中是一个抽象类,代表不可变,可分区,里面元素可并行计算的集合
DataFrame : 是以RDD为基础的分布式数据集,类似于数据库中的二维表格,DataFrame 与RDD的区别在于,前者有schema 信息,及DataFrame所表示的二维表格数据集的每一列都有名称和类型
Dataset :

11. Scala

  1. scala语言有什么特点? 什么是函数式编程?
  2. 函数式编程,很多变量不可变更,不存在枷锁,可以并行处理
  3. Java与 Scala无缝衔接,都是运行在JVM上
    3.类型推测(自动推测类型),不用指定类型
    4.并发和分布式
    5.特质 Trait (类似 java 中的 interface 和 abstract class )
    6.模式匹配,可以匹配case class
    7.高阶函数(函数的参数是函数,函数的返回是函数),可进行函数式编程)
    2.scala 中的集合 ?
  • 顶级父类/根集合: scala.collection

  • 不可变集合:scala.collection.immutable

  • 可变集合:scala.collection.mutable

  • 默认的是 不可变集合,定义可变集合 ,需要 import scala.collection.mutable

  • 可变集合可以修改,添加,移除

  • 不可变集合永远不会改变,但是可以模拟添加,移除和更新操作,返回的都是新集合
    经常使用的集合包括 Set , List , Map ,Tuple等
    3.scala 中 var ,val ,mutable ,immutable的区别?
    Var 与 Val :
    Var : 可变变量,定义时可以赋值,可以不赋值,且可以被多次赋值
    Val : 不可变变量,定义时必须赋值,且只能被赋值一次
    被编译后的 Val变量,添加了 final 关键字,所以相当于 java 中的 final
    Mutable 与 Immutable :
    Mutable : 可变的集合 ,可以增删改
    Immutable : 不可变集合 ,不会改变,但是可以模拟增删改操作,返回的都是新集合
    4.scala 中 object 与 class 区别?
    Object 相当于 Java中的静态,不需要new
    普通 class 需要new
    5.scala 中 class 与 case class 有什么区别 ?

    /** 1. case class 初始化的时候可以不用 new ,当然也可以加上,但是普通类就一定需要 new /
    val aa =new AA(“hdfs”)
    val dd =DD(“yarn”)
    /
    * 2. case class 直接实现了 toString ,而 普通类没有 /
    println(aa)
    // sdf.basic.VarValDefDiff$AA@880ec60
    println(dd)
    // DD(yarn)
    val aa2 = new AA(“hdfs”)
    val dd2 =DD(“yarn”)
    println(aa == aa2) //false
    // 二者均是新 new 的对象,且没有实现 hashCode和 equals 方法
    println(dd == dd2) // true
    println(dd.equals(dd2)) // true
    // case class 实现了 equal 和 hashCode , case class 的是同一个对象,hashCode相同,因此二者相等
    /
    * 4. case class 构造函数的参数是 public 级别的,则可以直接调用 /
    println(dd.name)
    /
    * 3. 认实现了equals 和hashCode /
    // aa.name error: value name is not a member of AA
    /
    * 5. case class 可以进行模式匹配 具体请见 sdf.basic,Match
    模式匹配是 case class 最主要的功能
    */

6.scala 中 trait与 abstract class的区别 ?
7.scala 中 Nil ,Null , None, Nothing 四个类型 ?
8.scala 中的闭包?
9.scala 柯里化?

;