–第二部分 kafka,scala,spark
day01 kafka
1. 和zk的关系
-
元数据存放到zk(节点)中, 应用了zk的统一命名的功能
- 集群节点信息: brokerId 每台机器的id
- topic信息: 在哪台机器上的哪个位置
- parititon主从信息: 每个partition多个副本中,谁是老大
-
partition_0 有多个副本, 需要选择出老大,应用了zk的分布式锁功能
-
kafka没有单独的界面, zk, 或者安装kafka-eagle
访问端口: http://node01:8048/ke
2. kafka简介
2.1 版本
- 依赖scala(2.11), kafka(1.0.0)的版本由scala版本&kafka版本组成
- 服务器的版本和客户端的版本要相同, 否则无法连接服务器
2.2 集群架构
- kafka cluseter:天生分布式消息存储,每一个机器是一个broker, 有borkerId, 存储在zk中
3. kafka原理
3.1 分片&副本
3.2 消息存储&查找机制
- 每一个分区会对应一个文件夹
- 每一个文件夹中有多个sagment段, 每个sagment段
- xxx.index 记录每条消息的在xxx.log中的位置(offset), 第一个为0000.index(起始偏移量为0)
- xxx.log 真正存储消息
- 一个sagment大约1G
- 查找时
- 会根据记录的offet 二分查找,找到对应的xxx.index,
- 根据xxx.index中记录的顺序查找到指定的消息
3.3 生产者分发策越
- 指定分区
- 指定key, 根据hashCode()
- 不指定key, 也不知道分区, 随机分配
3.4 消费者 消费消息时负载均衡
- 同一个消费者组中的一个消费者,同一时间, 只能消费同一个主题中的一个分区, 消费后会把消息的offet记录到zk或者kafka文件中
- 一个消费者不可能消费多个分区的消息
- 一个分区的消息也不可能被同一个组的多个消费者消费
- 最好的状态是1:1
3.5 消息不丢失
3.5.1 生产者
- ack反馈机制 -1 代表分区&副本都收到了消息, 0 代表分区副本老大收到了, 1 只发不反馈
- 面试问题
- 如果broker一直不给ack–>设定超时时间, 超过时长就默认发送失败,重新发送
- 一条一条反馈,浪费带宽, 生成者将消息放到缓存池中,缓存池积累到比如500条时, 集中写到服务器中, ack=-1, 缓存池再写下一个500条
- 如果缓存池满了, 但是服务器一直不给ack, 定期定量清缓存池
3.5.2 存储者
3.5.3 消费者
- 消费者 消费完消息记录offset (xxx.index), __consumer_offsets-25
- 问题(放到zk中会有重复消费的问题发生)
4. kafka应用案例
- topic串联
- producer01
- consumer01(producer02)
- consumer02
- 生产者和消费者版本不同?
- 生产者一个模块kafka-clients导入旧版本
- 消费者一个模块kafka-clients导入新版本pom依赖
- 和模块平级的src里面构建一个context类,是单例对象,用于存放旧版本生产者生成的消息
day02 hbase
0. hbase简介
0.1 数据库分类
- 关系型数据库
- 非关系型数据库 redis mgdb hbase
- 面向主题的是数据仓库, 面向事务的是数据库
0.2 hbase安装
0.3 hbase简介
1. 和zk&HDFS关系
- 元数据存放到zk中, 应用了zk的统一命名的功能(就是元数据信息)
- 因为配置时指定了老大, 所以没有应用zk的分布式锁功能
- hdfs就是hbase存储数据的磁盘,
- hmaster和hregison启动后, 会在zk上面创建对应的临时节点, 才会起到监听作用
2. hbase整体结构
2.1 层级结构
- client
- zk
- Hmaster
- HRegionServer
- HRegion–> 一个table有多个HRegion
- HStore–>对应一个列族, 一个HRegion有多个HStroe
- MemStroe(内存)–>flush到StroeFile
- StoreFile(磁盘)
- Hfile(128)–> HDFS客户端, 写入到hdfs中
- HLog(容错机制,副本机制,保证数据不丢失)
2.2 详细讲解HBase结构
2.2.1 client
- client和hmaster, client和hregisonServer之间使用rpc通信
- client和hmaster–>管理类操作
- client和hregisonServer–>读写类操作
2.2.2 zookeeper
- 保证多个hmaster启动情况下,只有一个hmaster运行. 分布式锁功能
- 存储 -root-表的location, 所有hregion的入口 ,hmaster地址
- 监控hregisonServer的状态, 将hregisonServer的上线和下线信息,实时通知给hmaster
- 存储hbase的schema
- 有哪些table,
- 每个表中有哪些column family
2.2.3 hmaster
- hmaster需要与zookeeper保持通信转态, 通过zookeeper知道hregsionServer的位置及hregisonServer的存活转态(zookeeper监控hregisonServer的存活). 管理集群
- 为hregisonServer分配regison(默认为10G一个regison)
- 赋值hregisonServer的赋值均衡.
- 发现失效的hregisonServer, 就重新分配到其他hregison的hregison中
- GFS上的垃圾文件回收
- 它会负责客户端创建表、删除表的请求。
2.2.4 hregisonServer(hbase最核心的模块)
- 维护hmaster分配给它的10G的hregion.
- 负责切分过大的hregison(StoreFile触发切分机制)
- 它会接受到客户端的读写表的请求
2.2.5 hregison
2.2.6 hfile
- hfile是hbase中keyValue数据的存储格式, 列名:列值
- hfile是hadoop的二进制格式文件, storeFile就是对hfile进行了封装,进行数据的存储.
2.2.6 hstore(hbase存储核心,memStore和storeFile组成)
- client写入数据,会先连hmaster, hmaster通过zk中的-root-和.meta得知机器的分配转态.找一台空闲的,鲜活的,离得近的hregionServer.
- 如果是重新建表,并分配一块新的hregion和hlog给这个表.
- 如果是往表里写数据会通过zk告知hregionServer往哪里写
- client从zk中找到hmaster告知的空闲的,鲜活的,离得近的hregionServer, 开始写数据.
- 数据会先写入到hlog和memStore中
- memStore满了后,会flush到一个StoreFile.
- storeFile达到一定阀值(比如64M), 触发compact,会合并成一个大的storeFile, 同时进行版本合并和数据删除
- 多个storeFile合并成一个StroeFile, 当这个StroeFile达到一定阀值(比如256M),会触发split操作, 切分为两个storeFile.
- split操作相当于把HRegion切分为两个HRegion, 旧的hregion下线, 新的storeFile分配到这两个hregion中, hmaster又把这两个hregion分配到相应的hregisonServer上(有了20G的两个hregison)
2.2.7 hlogs(副本,防止hbase宕机)
- 每个hregionServer都有一个hlog
- hlog是一个实现write ahead log的类, 用户操作写入memstore的同时, 也会写一份数据到hlog, hlog文件定期会滚动出新, 并删除旧的文件(已经持久化到storeFile中的数据)
- 当hregionServre宕机后, zookeeper会先监听到, 告知hmaster
- hmaster首先处理遗留的hlog文件, 将不同hregion的hlog数据拆分, 放到对应的hregion目录下,
- hmaster将失效的hregion重新分配.
- 新hregionServer加载旧hregion,发现hregion目录下有历史hlog需要处理, 会replay hlog中的数据到自己的memStore中, 内存满了flush到storeFiles.完成数据恢复.
3. hbase的读写流程
3.1 hbase的写入流程
- client通过zk, 找到对应的hregionServer
- 数据会先写入到hlog和memStore中
- memStore满了后,会flush到一个StoreFile.
- storeFile达到一定阀值(比如64M), 触发compact,会合并成一个大的storeFile, 同时进行版本合并和数据删除
- 多个storeFile合并成一个StroeFile, 当这个StroeFile达到一定阀值(比如256M),会触发split操作, 切分为两个storeFile.
- split操作相当于把HRegion切分为两个HRegion, 旧的hregion下线, 新的storeFile分配到这两个hregion中, hmaster又把这两个hregion分配到相应的hregisonServer上(有了20G的两个hregison)
3.2 hbase查询路由
- hbase中有两张特殊的表, -root-和.meta(元数据)
- .meta(书的小目录): 记录用户表的hregison信息, .meta可以有多个hregison
- -root-(书的大目录): 记录.meta表的hregison信息, -root-只有一个hregison
- zookeeper中记录了-root-表的元数据信息(书在哪儿?)
- 所以查找数据顺序
- 访问zookeeper
- 通过zookeeper存储的-root-的location(书在哪儿), 找到-root-表, 找到了大目录
- 通过大目录, 找到.meta(小目录, 也就是表的详细元数据信息)
- 通过小目录, 就能具体找到数据的位置了.
4. hbase的交互
- hbase shell
- create ‘user’, ‘base_info’ 必须指定列簇
- put ‘user’, ‘rowkey_10’,‘base_info:username’,‘张三’
- describe ‘user’
- scan ‘user’ 扫描表
- get ‘user’, ‘rowkey_16’ 查询
- get ‘user’,‘rowkey_16’,‘base_info’
- delete ‘user’, ‘rowkey_16’, ‘base_infor:username’
- disable ‘user’ drop ‘user’
- java操作
5. hbase的过滤器
5.1 过滤器类型
- rowFiter 基于主键
- familyFilter 基于列族
- QualifiterFilter 基于字段
- ValueFilter 基于列值
- bloom过滤器
5.2 Bloom过滤器
5.2.1 哈希函数
5.2.2 判定一个元素是否存在于集合中
5.2.3 原理
- 布隆过滤器(Bloom Filter)的核心实现是一个超大的位数组和几个哈希函数。假设位数组的长度为m,哈希函数的个数为k
- 假设一个集合中有三个元素, 那么就有三个哈希函数{}
- 每个元素通过三个哈希函数, 映射到位数组上, 标记为1
- 最终位数组上至多有9个1
- 但实际情况不是9个, 有可能不同的元素映射到位数组中的是同一个元素中, 这也是布隆过滤器存在误判的原因
- 判断一个元素是否存在在集合中, 这个元素通过上面三个哈希函数,映射都位数组上.如果三个点中有一个不为1, 那么这个元素肯定不存在于集合中.如果映射三个点都为1, 那么这个元素就很有可能存在于集合中.
- 但存在误判, 如果映射的三个点为1(假设是如下位数组下标为4,5,6), 但是这三个点真正是由不同元素映射出来的, 所以也不能判定这个点就一定存在于这个集合中
6. hbase的rowkey的设计原则
hbase是三维有序存储的.rowkey,column key, timeStamp三个维度可以对hbase中的数据进行快速定位,
6.1 三个设计原则
- 长度原则, 最大铲毒64kb(10-100bytes), 越短越好, 不要超过16个字节
- 散列原则 高位随机生成,地位放时间字段 避免造成热点问题
- 唯一原则
6.2 热点
- 大量的client直接访问集群中的一个或者极少数个节点
- 解决热点(让数据随机分散到各个region上)
- 加盐(rowkey前面增加随机数)
- 哈希
- 反转 手机号反转存放
day03 scala01
1. Any 所有类的超类(Object)
- 所有值类的基类, 是一个值, 不是一个对象
- 继承结构
- AnyVal
- 9个子类
- AnyUnit void
- Boolean
- AnyRef
2. HashMap是Map(接口)的实现类
- put(key)
- hashMap(key) 得到value真实对象
- get(key) 得到Option类型的对象
3. 集合不可变&可变
3.1 常用方法
- 不可变的名称短,属于immutable包下, 可变名称长,属于mutable包下
- 不可变中的方法–>添加元素, 只会生成新的集合或数组
- 0 +: list 头部加一个元素
- 等价于0::list
- :: 是右结合, 从右边执行
- list :+ 0 尾部加一个元素
- list ++ list0 两个集合合并, list在前, list0在后
- val first=lst1.head 获取头
- val tail=lst1.tail 尾部
- 不可变–> 会生成新的集合,也可以像StringBuilder一样追加元素
- 生成新的集合
- 0 +: list 头部添加一个元素, 生成新集合
- list :+ 0 尾部追加一个元素, 生成新的集合
- ++ 两个集合合并, 生成新的元素, 谁在前面,谁就添加到前面
- 不生成新的集合
- insert(下标,params xxx) 从某个下标开始 添加n个元素
- insertAll(下标,集合)
- += 尾部追加一个元素
- ++= 尾部追加一个集合或数组
- -= 直接移除某个元素
- remove(下标, params xx) 从某个下标开始, 移除N个元素
3.2 集合作为参数
-
Array[Int]-------ArrayBuffer
-
List[(String,String,String,String)]-----ListBuffer
-
Set[Double]-----HashSet
-
Map[String,Int] ------HashMap
apply()方法, 可以不用new集合,直接List(元素)
4. val/var/immutable&mutable
- val 类似于java中的final修饰的变量, ==>常量value String s
- .class文件中只有getter()方法
- 没有setter()方法
- var 变量variable
- getter()和setter()方法
- 如果修饰引用遍历, val person:Person , person指向的地址值可以变
- immutable 不可变的(值不可变) 常量池
- 体现在集合中, 在具体的包中
- 如果经过操作后, 只能形成新的集合(引用中的值不可以被改变)
- mutable 可变的(值可以改变后还能指向本引用) StringBuilder StringBuffer
- 经过操作后,可以形成新的集合, 也可以不形成新的集合(集合中引用的值发生了修改)
5. 方法&函数
-
方法
-
def add(x:Int,y:Int) = {
x + y
}
-
不可以作为参数传递
-
可以没有形参
-
可以通过add _转换为函数
-
函数
6. class和object
- class 可以new,
- 每个辅助构造器执行必须以主构造器或者其他辅助构造器的调用开始
- 因此子类的辅助constructor是一定不可能直接调用父类的constructor的;
- object
- 相当于java中的静态代码块, 是单例的
- 存放静态的field或者methon
- 有apply()方法, 伴生类(参数…)会调用这个方法, 创建一个伴生类对象
day04 scala02(继承&actor)
1. 继承
1.1 继承小记
责任链模式
- 创建对象时with了不相关的接口, 就会具有这个接口中的方法
- 不相关接口, 和对象所属的类 --> 肯定都要继承同一个父接口, 这个父接口有一个log()方法,方法体没有内容
- 不相关的接口重写了父接口中的方法, 方法体中有内容
- 创建的对象, 类中还有调用log()方法, 这时候就会具有不相关接口中的log()方法
- 虽然这个对象没有重写父接口的空方法,但不相关接口重写了, 就相当于我有这个方法了
模板设计模式
-
trait 链
- 一个类继承了多个子接口, 每个接口中有相同的方法
- 肯定有一个父接口(共同的方法), 其余子接口都继承了这个父接口, 然后重写了父接口的方法, 最后再supper.父接口共同方法
- 调用这个类的共同方法时, 会依次从右往左执行,最后执行到父接口中的方法
-
trait的构造机制
- class类必须方法最左边
- 先构造父类, 再构造父trait,再子trait
- 如果多个子trait继承同一个父trait,父trait只构造依次
- 最后子类
- 从左往右依次执行
-
协变、逆变、非变总结
Ø C[+T]:如果A是B的子类,那么C[A]是C[B]的子类。
Ø C[-T]:如果A是B的子类,那么C[B]是C[A]的子类。
Ø C[T]: 无论A和B是什么关系,C[A]和C[B]没有从属关系。
-
上界 下界
- U >: T 下界 U是T的父类或本身
- S <: T 上界 S是T的子类或本身
1.2 isInstanceOf & asInstanceOf
- a.isInstanceOf[A] a是A的子类或者本身对象放回true ==> 相对于java中的 a isInstanceof A
- a.asInstanceOf[A] 就把a转为A的对象了==> 相当于java中的 (A)a
1.3 getClass & classOf
-
p.getClass:利用反射得到p的字节码对象==>相当于java中的p.getClass()
-
classOf[P]:利用反射得到P类的字节码对象==>相当于java中的P.class()
-
java中获取字节码对象的3种方法
(1)Class.forName("类相对的全路径名称"),Class类中有forName()这个方法
Class clazz=Class.forName("com.itheima.demo01.Person");加载Person.class字节码进方法区,
同时创建类字节码对象
(2)类名.class()作为参数传递
Class clazz=Person.class,也是加载Person.class字节码进方法区,同时创建字节码对象
(3)实例对象名.getClass() Object类中有getClass()方法,第三种方法是获取字节码对象
Person p=new Person();
Class clazz=p.getClass();
2. 函数
- collection中的方法
- map((_,1)) 对数组/集合中的每一个元素作用一个函数, 然后返回原来类型的数组/集合
- flatten 对数组/集合 , 元素还是数组或集合, 把内存抽出来到同一个级别, 返回数组或者集合
- flatMap(_,split("\r\n")) 先map, 在flatten ==>最后返回的类型不变
- groupBy(_._1) 对数组或者集合分组拆分, 拆分成Map(key, Iterator[V])类型
- reduce
- reduceLeft() 从左往右计算
- reduceRight(_+_)
- fold
- foldLeft(0)(_+_._2) 从0开始, 每一个元素的从左往右累加_.2
- foldRight(0)(_+_)
- filter(_%2==0) 过滤
- 映射中的方法
- mapValues(_.size) map类型的集合调用这个函数, 对每个元素的value作用一个函数, 返回值赋值为value, ==> 再返回新的map集合
- mapKeys(_ % 2 == 0) 过滤k
- seq
- sorted
- sortBy(x=>x)排序,默认asc
- sortWith(_>_) 降序排列
- reverse 反转
3. 样例类&异步有返回值
-
样例类 case class ReturnMes(map:Map[String, Int])
-
react 方法中接收到消息返回结果 sender ! ReturnMes(map)
-
object中 发送消息后接收到结果, 得到为来的某一个结果
val future: Future[Any] = actor !! sendMes(array(0))
val result: Any = unit.apply()
-
如果sender 返回的结果是对象, Any 就是AnyRef 需要强转为ReturnMes(map)
val resultNew:ReturnMes = result.asInstanceOf[ReturnMes]
resultNew.map 就可以得到react方法中发送给的结果了
-
如果sender 返回的结果是数值类型, Any 就是AnyValue,不需要强转就可以直接打印
4. 线程知识&入口
4.1 开启线程的方式
public class Thread1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
public static void main(String[] args) {
Thread1 thread1 = new Thread1();
thread1.start();
}
}
public class Thread2 implements Runnable {
@Override
public void run() {
}
public static void main(String[] args) {
Thread thread = new Thread(new Thread2());
thread.start();
}
}
public class Thread3 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return null;
}
}
public class Thread4 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
ExecutorService executorService1 = Executors.newCachedThreadPool();
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
ExecutorService executorService2 = Executors.newSingleThreadExecutor();
ScheduledExecutorService scheduledExecutorService1 = Executors.newSingleThreadScheduledExecutor();
ExecutorService executorService3 = Executors.newWorkStealingPool();
}
}
4.2 入口
5. scala实现wordCount
day05 scala03 spark入门
0. spark利用akka实现通信
0.1 原理
- 主节点Master, 从节点Worker
- 每个节点底层都是Akka原理, 主从之间通信,其实就是actor之间通信
- 老大ActorSystem
- 老大负责创建actor
- worker 通信给 master, 需要使用master的引用, master给worker通信, 直接sender
0.2 RPC
RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议
1. scala编写spark入门程序
-
a --worker 发送注册信息到 masterSS
-
worker 发消息给 master, 需要调用context获取到master的引用对象, 使用引用对象发消息
master 发消息给 worker, 直接使用sender ! 就能发送
-
hashMap相关操作
-
a – b-- master 接收到注册消息, 返回worker注册成功的消息
-
c— worker接收到master反馈的注册成功的消息后,定时发送心跳给master, 报活
-
定时任务
-
java— Time TimeTask
-
linux —crontab
-
scala–Akka中, 需要先发送给自己中转一下 context.dispatcher
//定时单位需要导入此包
import scala.concurrent.duration._
//定时执行需要隐式转换
import context.dispatcher
context.system.scheduler.schedule(0 millis,SEND_HEART_BEAT_INTERVAL millis,self,SendHeartBeat(workerId))
-
c-- master接收心跳, 存储到hashMap中, 同时也存到listBuffer中
-
d–master定时检查worker是否还活着, 自己发给自己, 初始化方法中发送开启定时任务.同时对listBuffer中的worker按照memory降序排列
2. 高阶函数
- 柯里化(Currying) 接收多个参数的方法, 转换为接收一个参数的函数的过程==>返回值是一个函数,参数就是其余参数, 而且第一个参数的值会累计在此函数中
- 闭包, 就是函数的返回值依赖于函数之外的参数
- 函数:
- 函数是头等举足轻重的人物, 是一个有血肉的,有灵魂的实体
- 可以当做参数传递到方法中,也可以当做方法具有某种功能
3. 隐式转换
隐式转换就是, 让某一个类中具有另一个class(或者另一个object)的方法,或者参数值
-
隐式值&隐式方法
- 隐式值, 同一个类型的隐式值只能定义一次
- implicit 修饰隐式值或者方法
- 相应触发隐式转换, 需要在使用处导入import object名称.隐式值/方法名称
-
只能定义到object中
-
让class A 具有class B的功能
- implicit aToB(a:A) = new B(a.属性值)
- 调用时, import myImplicit.aToB a.B中的方法名
-
隐式转换后, 让class A 具有多个类的同一个方法,
导入时,只能导入某一个隐式转换方法==>a对象只能转换为某一个类型的对象,
day06 spark01(基础)
1. spark是什么?
- spark是一种快速,通用,可扩展的大数据分析引擎
- sparkSQL
- SparkStreaming
- GraphX
- Mlib
- 并行计算框架, spark中的job中间输出和结果可以保存在内存中.
- 特点
- 快: 内存100倍,硬盘10倍, 基于内存来高效处理数据流
- 易用
- 通用
- 兼容
- 可以使用yarn和apache mesos作为资源管理和调度器
- standalone可以使用内置的资源管理和调度框架
2. spark HA
- 如何解决单点故障问题
- 基于文件系统的单点恢复(开发或者测试环境), spark提供目录保存spark application和worker的注册信息, 并将他们的恢复转态写入到该目录中.重新启动master进程(start-master.sh),就可以恢复已运行的spark application和worker的注册信息.
- 基于zookeeper的standby master(生产模式)
- 如何恢复到上一次活着master挂掉之前的状态:
- 只有一个master,同时sparkt集群的元数据信息通过zk中的节点进行保存,而且是临时节点.
- master挂点,zk上面创建的临时节点就会消失,zk就能够感知到.
- 从其他master中选出一个作为活着的master, 这个活着的master会读取保存在zk中的spark集群的元数据信息, 恢复上一次master的转态.需要1-2m
- 在master的恢复阶段对任务的影响
- 正在运行得到任务,已经分配到了资源,不受影响
- 挂掉后提交的新任务, 由于没有活着的master分配资源,无法运行
3. 安装spark
4. spark架构
- 根据有几个master, 有不同的模式
- 一个master, standalone模式
- 多个master, ha高可用模式
- 整体架构
-
Driver Program :运⾏main函数并且新建SparkContext的程序
Application:基于Spark的应用程序,包含了driver程序和集群上的executor
-
standlone—>master负责分配任务和分配资源,
spark on yarn---->resourceManager负责分配资源,appMaster负责分配任务
- master或者rescoureManager会找一台worker node干活
如果有多个work node, 有可能都在同一worker node上干活, 每一个executor-memory有独立的内存
-
Cluster Manager :指的是在集群上获取资源的外部服务。目前有三种类型
- Standalone: spark原生的资源管理,由Master负责资源的分配
- Apache Mesos:与hadoop MR兼容性良好的一种资源调度框架
- Hadoop Yarn: 主要是指Yarn中的ResourceManager
-
worker node: 可以运行application的节点,
- standalon: worker干活
- spark on yarn:nodemanager干活
-
executor: 只负责执行分配过来的task工作单元
- 是在一个worker node上为某应用启动的⼀个进程,该进程负责运行任务,
- 并且负责将数据存在内存或者磁盘上。
- 每个应用程序(application)对应一个executor==>进程
-
task: 被送到某个executor上的工作单元.(container)==>线程
day07 spark02(rdd)
0. 自我理解spark执行
-
一个很大的文件, 有三个block分布于三台机器上
-
每个计算任务就是一个应用程序(application),
当前job执行完,如果要checkpoint()当前的job,需要重启一个新job来checkpoint()
- 遇到action算子, 就会创建一个job
- 一个应用程序会有多个job, 每一个job会被DAG schedule划分
- job–>DAG schedule–>Task schedule–>work node
-
执行spark计算时,每个block会启动一个进程(executor)
-
每一个executor,会启动多个task(线程)
每个executor中同时执行的task数则是由分配给每个executor中cpu的核数决定的。
-
输出的文件个数取决于partitions的个数, 默认numPartitions<=2,
1. RDD & spark
- resilient distributed dataset 弹性分布式数据集合
- 基本的数据抽象,不可变,可分区的,里面的元素可以并行计算的集合
- rdd属性
- 一组分片
- 计算每个分片的函数
- 依赖
- paritioner , 只有k,v的rdd 才有分区函数, 非k,v 是none
- hashPartitioner
- RangePartitioner
- 分区函数决定了父 rdd shuffle输出的分区数量
- 存储分区的优先位置 移动计算比移动数据要廉价
- 为什么产生rdd
- mr 自动容错, 负载均衡, 但反复从磁盘到内存, 从内存到磁盘,占用大量的io
- 它是一种具有容错机制的特殊集合,可以分布在集群的节点上,以函数式编程来操作集合,进行各种并行操作。
- 可以把RDD的结果数据进行缓存,方便进行多次重用,避免重复计算.
- rdd 在spark中的地位及作用
- 迭代计算和交互计算
- 内存计算
- Spark可以完美的运用scala的解释器,使得其中的scala可以向操作本地集合对象一样轻松操作分布式数据集。
- rdd 是一中具有容错性,基于内存计算的方法, rdd是spark core的底层核心, spark是rdd的具体实现
2. rdd 依赖
- 窄依赖 一个父rdd 只对应一个子rdd
- 宽依赖 一个父rdd 对应多个子rdd, shuffle就是宽依赖
- lineage
- rdd只记录当块上的单个操作,
- rdd 的一系列操作, 就是lineage
- 可以rdd.persist()或者rdd.checkpoint() 将系统保存下来
3. DAG(Directed Acyclic Graph)有向无环图,
- 原始的一些列rdd转换, 方向从左往右,不闭合
- 根据rdd依赖关系,划分stage, 从右往左划分, 遇到宽依赖就划分, 窄依赖在划分到同一个stage中
4. spark任务调度(FIFO)
- RDD objects --> DAG
- DAG schedule
-
划分DAG 成多个stage, 每个stage会形成一个taskSet,
-
记录缓存的rdd血统,
-
提交taskSet到Task schedule(不同模式有不同的底层任务调度器)
a)– spark-cluster TaskScheduler
b)– yarn-cluster YarnClusterScheduler
c)– yarn-client YarnClientClusterScheduler ???
-
重新提交丢失的stage到taskScheduler
- Task schedule (不知道stage是哪部分rdd的数据)
- 每个taskSet(stage) 生成一个taskManager
- 决定task任务执行的最佳位置
- 推测执行, 那些任务执行慢, 就停止这些任务重新到新节点上执行
- 提交一组taskSet(tasks) + 参数
- 将丢失的taskSet重新提交给DAG schedule ==> 队列FIFO的原则执行回退过来的stage
- work node 真正的执行任务
- 每个分区开启一个进程(executor)
- 每个executor中,开启一组线程,执行一组taskSet, 每个task就是一个线程
- 实时报告metrics给老大
- 将计算结果返回给driver
5. spark 执行原理
- 提交jar有不同模式,提交到spark还可以提交到yarn, 如下图解提交到yarn, 因为生产模式都是提交到yarn上
1. 本地运行
bin/spark-submit --class cn.spark.com.WordCountForHDFS --master spark:hadoop-01:7077 --executor-memory 1g --total-executor-cores 4 /root/spark-root-1.0-SNAPSHOT.jar /test.txt /out-spark-hdfs123
2、在yanr_cluster上运行
bin/spark-submit --class cn.spark.com.WordCountForHDFS --master yarn-cluster --executor-memory 1g --total-executor-cores 4 /root/spark-root-1.0-SNAPSHOT.jar /test.txt /out-spark-hdfs123v
3、运行在yarn_client
bin/spark-submit --class cn.spark.com.WordCountForHDFS --master yarn-client--executor-memory 1g --total-executor-cores 4 /root/spark-root-1.0-SNAPSHOT.jar /test.txt /out-spark-hdfs123v
5.1 本地master负责调度资源
- 通过启动sparkContext(程序运行的main方法).向资源管理器注册, 并申请每个executor执行的内存, 以及总的executor和核数(spark的交互方式), 以下三种模式对应的资源管理器不同
- master–> master
- yarn–>resourceManager
- mesos–>mesos
- 资源管理器为每个数据的每个分区分配资源,并启动executor, executor汇报资源情况给资源管理器
- sc 构建DAG–>DAG schedule 切割stages(taskSet)–>TASK schedule–> 提交task(脚本&.jar&参数) -->Executor executor向sc申请task
- master–>task schedule
- spark on yarn --> yarnClusterSchedule
- tasks 在每个分区的executor中运行, 运行完毕释放资源
5.2 spark on yarn_cluster执行原理
- sparkt on yarn_cluster:
- applicatin(sparkContext&exector)运行中application master中
- appm是nodeManager向resourceManager申请来了,启动中nodeManager中
- 关闭client,不影响application的运行
- rescoureManager负责分配资源, appm负责任务的分配(yarnClusterScheduler)
- Spark Yarn Client 向 YARN 中提交应用程序,包括 ApplicationMaster 程序、启动 ApplicationM的命令、需要在 Executor 中运行的程序等;
- ResourceManager 收到请求后,在集群中选择一个 NodeManager,为该应用程序分配第Container,要求它在这个 Container 中启动应用程序的 ApplicationMaster,其中 ApplicationMaster 北京市昌平区建材城西路金燕龙办公楼一层 电话:400-618-9090黑马程序员·大数据面试宝典 http://www.itcast.cnSparkContext 等的初始化;
- ApplicationMaster 向 ResourceManager 注册,这样用户可以直接通过 ResourceManage 查看应用的运行状态,然后它将采用轮询的方式通过 RPC 协议为各个任务申请资源,并监控它们的运行状态直行结束;
- 一旦 ApplicationMaster 申请到资源(也就是 Container)后,便与对应的 NodeManager 通信它在获得的 Container 中启动启动 CoarseGrainedExecutorBackend,CoarseGrainedExecutorBackend 启动向ApplicationMaster 中的 SparkContext 注册并申请 Task。这一点和 Standalone 模式一样,只不过 SparkC在 Spark Application 中初始化时,使用 CoarseGrainedSchedulerBackend 配合 YarnClusterScheduler 进行的调度,其中 YarnClusterScheduler 只是对 TaskSchedulerImpl 的一个简单包装,增加了对 Executor 的逻辑等;
- ApplicationMaster 中 的 SparkContext 分 配 Task 给 CoarseGrainedExecutorBackend 执 CoarseGrainedExecutorBackend 运 行 Task 并 向 ApplicationMaster 汇 报 运 行 的 状 态 和 进 度 , ApplicationMaster 随时掌握各个任务的运行状态,从而可以在任务失败时重新启动任务;
- 应用程序运行完成后,ApplicationMaster 向 ResourceManager 申请注销并关闭自己.
5.3 yarn_client
-
Yarn-Client 模式中,Driver 在客户端本地运行,这种模式可以使得 Spark Application 和客户端进行交互,
-
因为 Driver 在客户端,所以可以通过 webUI 访问 Driver 的状态,默认是 http://hadoop1:4040 访问,而
YARN 通过 http:// hadoop1:8088 访问。
- Spark Yarn Client 向 YARN 的 ResourceManager 申请启动 Application Master。同时在 SparkConten始化中将创建 DAGScheduler 和 TASKScheduler 等,由于我们选择的是 Yarn-Client 模式,程序会选YarnClientClusterScheduler 和 YarnClientSchedulerBackend;
- ResourceManager 收到请求后,在集群中选择一个 NodeManager,为该应用程序分配第一个 Contain要 求 它 在 这 个 Container 中 启 动 应 用 程 序 的 ApplicationMaster , 与 YARN-Cluster 区 别 的 是 在 ApplicationMaster 不运行 SparkContext,只与 SparkContext 进行联系进行资源的分派;
- Client 中的 SparkContext 初始化完毕后,与 ApplicationMaster 建立通讯,向 ResourceManager 注册根据任务信息向 ResourceManager 申请资源(Container);
- 一旦 ApplicationMaster 申请到资源(也就是 Container)后,便与对应的 NodeManager 通信,要在获得的 Container 中启动启动 CoarseGrainedExecutorBackend,CoarseGrainedExecutorBackend 启动后会Client 中的 SparkContext 注册并申请 Task;
- Client 中 的 SparkContext 分 配 Task 给 CoarseGrainedExecutorBackend 执 行 CoarseGrainedExecutorBackend 运行 Task 并向 Driver 汇报运行的状态和进度,以让 Client 随时掌握各个务的运行状态,从而可以在任务失败时重新启动任务;
- 应用程序运行完成后,Client 的 SparkContext 向 ResourceManager 申请注销并关闭自吉己
5.4 YARN-Client 与 YARN-Cluster 区别
- 理解 YARN-Client 和 YARN-Cluster 深层次的区别之前先清楚一个概念:Application Master。在 YARN中,每个 Application 实例都有一个 ApplicationMaster 进程,它是 Application 启动的第一个容器。它负责和ResourceManager 打交道并请求资源,获取资源之后告诉 NodeManager 为其启动 Container。从深层次的含义讲 YARN-Cluster 和 YARN-Client 模式的区别其实就是 ApplicationMaster 进程的区别。
- YARN-Cluster 模式下,Driver 运行在 AM(Application Master)中,它负责向 YARN 申请资源,并监督作业的运行状况。当用户提交了作业之后,就可以关掉 Client,作业会继续在 YARN 上运行,因而YARN-Cluster 模式不适合运行交互类型的作业;
- YARN-Client 模式下,Application Master 仅仅向 YARN 请求 Executor,Client 会和请求的 Container 通信来调度他们工作,也就是说 Client 不能离开.
6. spark容错机制—缓存&检查点
cache():默认的调用了无参的persist();无参的persist方法默认带一个参数----》 StorageLevel.MEMORY_ONLY
persist():无参的persist方法默认带一个参数----》 StorageLevel.MEMORY_ONLY
persist(StorageLevel.MEMORY_AND_DISK_2):可以手动的选择缓存的方式
rdd.persist(StorageLevel.MEMORY_AND_DISK_2)
在一个job结束后, 会启动另一个job执行缓存,
缓存后没有血统关系
缓存的是rdd执行action后的数据结果, 以rdd形式缓存
用的时候直接用rdd, spark底层会自动寻找缓存
答:经常缓存shuffle之后的RDD
checkPoint:
1、代码表达方式:
sc.setCheckpointDir("./ck")
2、缓存具体的RDD
result.checkpoint()
3、在生产的环境当中,通常将sc.setCheckpointDir("hdfs://hadoop-01:9000/ck")
主要的目的就是:天然借助HDFS副本机制,可以防止数据丢失
4、 cache&persist&chkpoint的区别:
cache&persist这两个缓存之后,RDD的血统没有改变;但是chkpoint操作之后,血统关系就断了,就没有血统关系
5、缓存和chkpoint设置之后读取顺序:
读取顺序:cache -----> chkpoint ----->重新计算
6、chkpoint在什么时候操作
答:在当前的任务(job)执行完成之后,重新起一个job来进行chkpoint操作 -----设置了chkpoint之后,job就有两个
当前job,就是当前这个应用程序
- hdfs 副本保存位置
- 一份保存在本台机器上
- 一份保存在同一个机架的另一台机器上
- 一份保存在另一个机架(rack)的一台机器上
7. 算子*******spark_day02常用rdd函数有时间每个都算一下
7.1 分类
- transformation
- action, 一个action后,会提交一个job
7.2 获取rdd的三种方式
- 由一个已存在的scala集合创建 val rdd1 = sc.parallelize(Array(1,3,4,9))
- 由外部存储系统的文件创建 val rdd2 = sc.textFile("/words.txt")
- 已有的rdd经过算子转换成新的 val rdd3 = rdd2.flatMap(_.split(" "))
7.3 常用算子
-
join() 两两左连接 Transformation在worker中执行
- rdd1.join(rdd2)–> Array(k, (v, w))
- 以rdd1为基准, --> (k,(v,w)), (k,(q,w)), (k, (p,w), (a,(b,c)))
val rdd7: RDD[(String, Any)] = sc.parallelize(List(("tom", "aa"),("tom", 1), ("tom", "bb"),("jerry", 3), ("kitty", 2)))
val rdd8 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))
//join
val join: RDD[(String, (Any, Int))] = rdd7.join(rdd8)
val c7: Array[(String, (Any, Int))] = join.collect()
//结果
ArrayBuffer((tom,(aa,1)), (tom,(1,1)), (tom,(bb,1)), (jerry,(3,2)))
-
cogroup() 同一个rdd中,相同k的value迭代组成迭代器 k,(Iterator[v],Iterator[w])
rdd1.cogroup(rdd2)
val rdd9: RDD[(String, Any)] = sc.parallelize(List(("tom", "aa"),("tom", 1), ("tom", "bb"),("jerry", 3), ("kitty", 2)))
val rdd10 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))
val cogroup: RDD[(String, (Iterable[Any], Iterable[Int]))] = rdd9.cogroup(rdd10)
val c9: Array[(String, (Iterable[Any], Iterable[Int]))] = cogroup.collect()
//结果
ArrayBuffer((tom,(CompactBuffer(aa, 1, bb),CompactBuffer(1))), (jerry,(CompactBuffer(3),CompactBuffer(2))), (shuke,(CompactBuffer(),CompactBuffer(2))), (kitty,(CompactBuffer(2),CompactBuffer())))
-
groupByKey() 求一个集合的相同k合并后的value, shuffle阶段, 得到的value是迭代器
rdd.groupByKey()得到的是 Array(k, Iterator[v])
val rdd7: RDD[(String, Any)] = sc.parallelize(List(("tom", "aa"),("tom", 1), ("tom", "bb"),("jerry", 3), ("kitty", 2)))
val key: RDD[(String, Iterable[Any])] = rdd7.groupByKey()
val c77: Array[(String, Iterable[Any])] = key.collect()
//结果
ArrayBuffer((tom,CompactBuffer(aa, 1, bb)), (jerry,CompactBuffer(3)), (kitty,CompactBuffer(2)))
-
groupBy()更加灵活 返回的是 k, Iterator(k,v)
ArrayBuffer((tom,CompactBuffer((tom,aa), (tom,1), (tom,bb))), (jerry,CompactBuffer((jerry,3))), (kitty,CompactBuffer((kitty,2))))
-
reduceByKey() shuffle阶段, 求相同k—>v的和
(k, 累加后的v)
-
注意:分区个数
- repartition可以增加和减少rdd中的分区数,coalesce只能减少rdd分区数,增加rdd分区数不会生效。
- sc.parallelize(数组/集合) 分区数由核数决定
- sc.textFile(url), 分区数<= 2
-
Action函数 在driver中执行
- reduce() 求和, 不能求k,v 格式的和
- count() 计算有多少行记录, 得到的是Long类型
- take() 取出动作, 取出前几个, 得到的是数组
- collect() 返回多个分区合并后的数组
8. 数据的广播
- 一个executor有多个task, 每个task都会加载数据, 为了减轻内存压力, 将公共的数据广播出去
- 广播的是字典数据(不会频繁改变的数据)
- (放到每一个的executor中一个公共的位置)
//广播数据(广播action动作后的数据)
val broadcast: Broadcast[Array[(String, String, String, String)]] = sc.broadcast(collect)
//取数据
val valueArr: Array[(String, String, String, String)] = broadcast.value
day08 spark03—>Spark SQL
1. Spark SQL
- Spark SQL是Spark用来处理结构化数据的一个模块,它提供了一个编程抽象叫做DataFrame并且作为分布式SQL查询引擎的作用。
- spark sql 特点
- 易整合
- 统一的数据访问 json, text
- 兼容hive spark.sql(“hive语句”)
- 标准的数据连接
2. RDD/DataFrame/DataSet的区别
-
概念
- RDD是分布式的Java对象的集合。
- DataFrame是分布式的Row对象的集合,
- DataSet是分布式数据集合,是DataFrame的父类
-
-
RDD的优缺点:
优点:
(1)编译时类型安全
编译时就能检查出类型错误
(2)面向对象的编程风格
直接通过对象调用方法的形式来操作数据
缺点:
(1)序列化和反序列化的性能开销
无论是集群间的通信, 还是IO操作都需要对对象的结构和数据进行序列化和反序列化。
(2)GC的性能开销
频繁的创建和销毁对象, 势必会增加GC
-
DataFrame优缺点
- 通过引入schema和off-heap(不在堆里面的内存,指的是除了不在堆的内存,使用操作系统上的内存),解决了RDD的缺点。
- Spark通过schame就能够读懂数据, 因此在通信和IO时就只需要序列化和反序列化数据, 而结构的部分就可以省略了;
- 通过off-heap引入,可以快速的操作数据,避免大量的GC。但是却丢了RDD的优点,DataFrame不是类型安全的, API也不是面向对象风格的。
3. spark SQL===> API
day09 spark04—>Spark Streaming
1. 什么是Spark Streaming
- spark 流式处理模块
- 特点
- 易用 可以像编写离线程序一样编写流式程序
- 高容错 哪里出错重新计算哪里
- 易整合到spark体系 spark(批处理) spark streaming (流式批处理)
2. Spark Streaming 原理
-
基本原理
- 是基于spark的流式批处理引擎
- 将输入的数据,以某一个时间间隔为单位, 进行处理,
- 时间间隔最小0.5-2s Strom最小为100ms
-
计算流程 (spark streming 将流式处理转为批处理, 批处理引擎就是spark core(rdd))
- spark streaming根据batch size将数据划分为一段一段的Discretized Stream,
- 每一段(每一批)数据,就是一个RDD
- spark streaming对DStream的transformation的操作–>底层依赖的就是spark对RDD的transformatiion
- 将RDD的操作可以根据业务需求, 保存在本地(缓存)或者hdfs(checkpoint)–>高容错
-
高容错
- 每一个batch size(DStream)对应一些列的RDD操作, 同一个时间段中最后一个RDD最为本批次的输出
- 连续时间中的相同的RDD, 构成了一个DStream. 同一个批次中有多少个算子, 对应多少个详细的DStream
- Spark Streaming 输入数据时, 会同时拷贝到其他机器上一份, 所以RDD中的任意一个Partition出错, 都会从其他机器上找到相同的缺失数据, 这种容错恢复速度比Strom连续计算要高
-
每个时间段中的操作都是相同的,每一段都经过DAG形成与划分&TaskSet任务调度, 时间间隔秒级别,就可以实时处理数据了
- 一个action动作,会形成一个job
- 一个job,会划分为多个task(stage)
3. DStream
- Discretized Stream 离散数据简称DStream , 是spark streaming的基本的数据抽象,
- spark---->RDD RDD是spark core的底层核心
- spark SQL—>DataFram /DataSet[Row]/DataSet
- spark streaming —> DStream
- 代表持续的数据流和经过spark各种算子操作后的结果流
- 每个batch是一个RDD, 联系时间就构成了DStream
4. DStream 算子操作
- transfromation
- count()
- reduceByKey(_+_)
- updateStateByKey(function函数,)连续时间内累加, 需要设置检查点
- transform(rdd=>{}) 针对某一个DStream, 操作里面的rdd, rdd=>{}返回一个rdd
- reduceByKeyAndWindow((x:Int,y:Int)=> x+y, Seconds(5),Seconds(5))设置开窗函数
- 窗口大小一般为batch size大小的整数倍, 不是整数倍会丧失计算精度
- 窗口滑动的大小也是batch size的整数倍, 而且要和窗口大小相等
- 如果滑动大小>窗口大小 会丢失数据
- 如果滑动大小<窗口大小 会重复计算
5. Spark Streaming 和 flume整合
-
spark streaming 从flume中拉取数据
- flume配置文件,sink类型是 org.apache.spark.streaming.flume.sink.SparkSink
- spark streaming 入口函数为FlumeUtils.createPollingStream(ssc,host, port)
- 从flume拉取过来的数据保存在SparkFlumeEvent中, 每一条数据是一个event
- 先启动flume,再启动spark streaming
将下载好的spark-streaming-flume-sink_2.11-2.0.2.jar放入到flume的lib目录下
删除flume的lib下自带的scala-library-2.10.1.jar包
bin/flume-ng agent -n a1 -c conf/ -f conf/flume-poll-spark.conf -Dflume.root.logger=INFO,console
a1.sources = r1
a1.sinks = k1
a1.channels = c1
a1.sources.r1.channels = c1
a1.sources.r1.type = spooldir
a1.sources.r1.spoolDir = /root/data
a1.sources.r1.fileHeader = true
a1.channels.c1.type =memory
a1.channels.c1.capacity = 20000
a1.channels.c1.transactionCapacity=5000
a1.sinks.k1.channel = c1
a1.sinks.k1.type = org.apache.spark.streaming.flume.sink.SparkSink
a1.sinks.k1.hostname=node01
a1.sinks.k1.port = 8888
a1.sinks.k1.batchSize= 2000
-
flume推数据到spark streaming中
- flume配置文件 sink类型 avro, ip:windowIp
- spark streaming 入口函数为FlumeUtils.createStream(ssc,host,port)
- 从flume拉取过来的数据保存在SparkFlumeEvent中, 每一条数据是一个event
- 先启动spark streaming, 再启动flume
sparkStreaming结合flume操作流程
1 flume push方式
a1.sources = r1
a1.sinks = k1
a1.channels = c1
a1.sources.r1.channels = c1
a1.sources.r1.type = exec
a1.sources.r1.command = tail -f /root/test.txt
a1.sources.r1.fileHeader = true
a1.channels.c1.type =memory
a1.channels.c1.capacity = 20000
a1.channels.c1.transactionCapacity=5000
a1.sinks.k1.channel = c1
a1.sinks.k1.type = avro
a1.sinks.k1.hostname=172.16.43.63
a1.sinks.k1.port = 9999
a1.sinks.k1.batchSize= 2000
6. Spark Streaming 和Kafka整合
6.1 两种方法
都要先启动kafka, 创建主题, 再启动Spark Streaming程序
- spark启动Driver Program(sc)–>receiver 接收kafka中的数据 KafkaUtils.createDstream
- 消费者消费消息会记录offset, offset保存在zk中, 存在重复消费情况
- receiver接收数据会存在数据丢失, 需要开启WAL日志保存模式, 同时开启检查点
- spark启动Driver Program(sc)—>executor直接从kafka中获取数据 KafkaUtils.createDirectStream
- offset记录保存到kafka集群中(本台机器上), 不存在重复消费
- 直接使用executor去获取数据,不存在数据丢失情况
6.2 面试问题
6.2.1 两种方式
可以这样说:在kafka0.10版本之前有二种方式与sparkStreaming整合,一种是基于receiver,一种是direct,然后分别阐述这2种方式分别是什么
- receiver:是采用了kafka高级api,利用receiver接收器来接受kafka topic中的数据,从kafka接收来的数据会存储在spark的executor中,之后spark streaming提交的job会处理这些数据,kafka中topic的偏移量是保存在zk中的。
- 基本使用: val kafkaStream = KafkaUtils.createStream(streamingContext,
[ZK quorum], [consumer group id], [per-topic number of Kafka partitions to consume])
- 还有几个需要注意的点:
- 在Receiver的方式中,Spark中的partition和kafka中的partition并不是相关的,所以如果我们加大每个topic的partition数量,仅仅是增加线程来处理由单一Receiver消费的主题。但是这并没有增加Spark在处理数据上的并行度.
- 对于不同的Group和topic我们可以使用多个Receiver创建不同的Dstream来并行接收数据,之后可以利用union来统一成一个Dstream。
- 在默认配置下,这种方式可能会因为底层的失败而丢失数据. 因为receiver一直在接收数据,在其已经通知zookeeper数据接收完成但是还没有处理的时候,executor突然挂掉(或是driver挂掉通知executor关闭),缓存在其中的数据就会丢失. 如果希望做到高可靠, 让数据零丢失,如果我们启用了Write Ahead Logs(spark.streaming.receiver.writeAheadLog.enable=true)该机制会同步地将接收到的Kafka数据写入分布式文件系统(比如HDFS)上的预写日志中. 所以, 即使底层节点出现了失败, 也可以使用预写日志中的数据进行恢复. 复制到文件系统如HDFS,那么storage level需要设置成 StorageLevel.MEMORY_AND_DISK_SER,也就是KafkaUtils.createStream(…, StorageLevel.MEMORY_AND_DISK_SER)
- direct:在spark1.3之后,引入了Direct方式。不同于Receiver的方式,Direct方式没有receiver这一层,其会周期性的获取Kafka中每个topic的每个partition中的最新offsets,之后根据设定的maxRatePerPartition来处理每个batch。(设置spark.streaming.kafka.maxRatePerPartition=10000。限制每秒钟从topic的每个partition最多消费的消息条数)。
6.2.2 优缺点
- 采用receiver方式:这种方式可以保证数据不丢失,但是无法保证数据只被处理一次,WAL实现的是At-least-once语义(至少被处理一次),如果在写入到外部存储的数据还没有将offset更新到zookeeper就挂掉,这些数据将会被反复消费. 同时,降低了程序的吞吐量。
- 采用direct方式:相比Receiver模式而言能够确保机制更加健壮. 区别于使用Receiver来被动接收数据, Direct模式会周期性地主动查询Kafka, 来获得每个topic+partition的最新的offset, 从而定义每个batch的offset的范围. 当处理数据的job启动时, 就会使用Kafka的简单consumer api来获取Kafka指定offset范围的数据。
- 优点:
- 1、简化并行读取
- 如果要读取多个partition, 不需要创建多个输入DStream然后对它们进行union操作. Spark会创建跟Kafka partition一样多的RDD partition, 并且会并行从Kafka中读取数据. 所以在Kafka partition和RDD partition之间, 有一个一对一的映射关系.
- 2、高性能
- 如果要保证零数据丢失, 在基于receiver的方式中, 需要开启WAL机制. 这种方式其实效率低下, 因为数据实际上被复制了两份, Kafka自己本身就有高可靠的机制, 会对数据复制一份, 而这里又会复制一份到WAL中. 而基于direct的方式, 不依赖Receiver, 不需要开启WAL机制, 只要Kafka中作了数据的复制, 那么就可以通过Kafka的副本进行恢复.
- 3、一次且仅一次的事务机制
- 基于receiver的方式, 是使用Kafka的高阶API来在ZooKeeper中保存消费过的offset的. 这是消费Kafka数据的传统方式. 这种方式配合着WAL机制可以保证数据零丢失的高可靠性, 但是却无法保证数据被处理一次且仅一次, 可能会处理两次. 因为Spark和ZooKeeper之间可能是不同步的. 基于direct的方式, 使用kafka的简单api, Spark Streaming自己就负责追踪消费的offset, 并保存在checkpoint中. Spark自己一定是同步的, 因此可以保证数据是消费一次且仅消费一次。不过需要自己完成将offset写入zk的过程,在官方文档中都有相应介绍.
*简单代码实例:
* messages.foreachRDD(rdd=>{
val message = rdd.map(_._2)//对数据进行一些操作
message.map(method)//更新zk上的offset (自己实现)
updateZKOffsets(rdd)
})
* sparkStreaming程序自己消费完成后,自己主动去更新zk上面的偏移量。也可以将zk中的偏移量保存在mysql或者redis数据库中,下次重启的时候,直接读取mysql或者redis中的偏移量,获取到上次消费的偏移量,接着读取数据。
day10-day11 用户画像01_整合
1 spark & hive整合
2 hive整合hbase
1.2.1 目的
hive中的数据通过sqoop到出到mysql中,由于以下优缺点对比,hive导入hbase更利于用户体验.
- hive导出到mysql中的缺点
- 整个电商网站的注册用户量比较大,表的字段信息比较多,如果把数据写入到mysql中,查询新能比较低,用户体验不好
- 后期可能这些表的字段不断新增(业务需求的增加),或者字段发生改变, 对mysql表结构进行修改不是很方便.
- hive数据映射到hbase中优点
- hbase本身就是存储海量数据, 支持实时读取, 查询效率高.
- mysql数据存储像是javaBean, 成员变量已经规定好,想要修改比较麻烦. 而hbase的存储格式更像map,后续添加字段就像添加map中元素一样(数组+链表)
1.2.2 环境准备
- 编译hive提供的hive-hbase-handler, 能够让hive5.5.1 和 hbase1.2.1保持良好的通信
- 整合环境配置
- hive修改配置文件 添加hbase机器地址
- hive-env.sh中添加hbae/lib的环境变量 目的:hbase下面的lib包导入到hive的环境变量中
- 把编译好的hive-hbase-handler-1.2.1.jar 替换掉hive目录下自带的同名包
1.2.3 实战
3 phoenix 整合hbase
1.3.1 什么是phoenix, 整合目的
- 目的: 通过phoenix操作hbase表中的数据, 将sql解析为操作hbase的java api代码. 为数据可视化提供便利.
1.3.2 ACID 事务四大特性
- Atomicity(原子性):一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。
- Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等。
- Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
- Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
1.3.3 数据仓库与数据库区别
- 数据库(OLTP)与数据仓库(OLAP)的区别实际讲的是 OLTP 与 OLAP 的区别。
- 操作型处理,叫联机事务处理 OLTP(On-Line Transaction Processing,),也可以称面向交易的处理系统,它是针对具体业务在数据库联机的日常操作,通常对少数记录进行查询、修改。用户较为关心操作的响应时间、数据的安全性、完整性和并发支持的用户数等问题。传统的数据库系统作为数据管理的主要手段,主要用于操作型处理。
- 分析型处理,叫联机分析处理 OLAP(On-Line Analytical Processing)一般针对某些主题的历史数据进行分析,支持管理决策。Kylin是OLAP的分析手段
- phoenix 就是使用sql查询hbase(nosql), OLTP分析手段就有phoenix
1.3.4 环境配置
-
zk, hadoop,hbase
-
解压
-
将phoenix目录下的phoenix-4.8.2-HBase-1.2-server.jar、phoenix-core-4.8.2-HBase-1.2.jar拷贝到各个 hbase的lib目录下。
-
hbase的配置文件hbase-site.xml 放到phoenix/bin/目录下
-
hadoop/etc/hadoop下的core-site.xml 、hdfs-site.xml放到phoenix/bin/下
-
重启hbase集群,使Phoenix的jar包生效。
1.3.5 phoenix使用
day10-day11用户画像02_知识梳理
1. 什么是电商用户画像
基于用户在整个电商网站上所填写的基本信息和一系列的操作行为(游览,点击,查看,收藏,转发,评论,加入购物车,下订单等等)数据,通过一些标签(特征)把用户描述出来。描绘用户的标签就是用户画像,构建用户画像最核心的就是给用户贴上对应的标签(特征)。
2. 构建电商用户画像的重大意义
(1) 可以构建一个公司的大数据平台,进行大量的数据统计分析,生成报表,为公司高层领导提供战略上的决策
(2) 精准营销,分析产品潜在用户,针对特定群体利用短信邮件等方式进行营销
(3) 进行数据挖掘,构建智能推荐系统
3. 用户画像标签体系的建立
3.1 什么是标签体系
-
基础属性标签: 客观属性(身份证)
-
行为属性标签
-
标签级别(标签的体系结构)
-
不需要运算的层级
-
运算层级
-
事实标签:
是通过对于原始数据库的数据进行统计分析而来的,比如用户投诉次数,是基于用户一段时间内实际投诉的行为做的统计。
-
模型标签:
模型标签是以事实标签为基础,通过构建事实标签与业务问题之间的模型,进行模型分析得到。比如,结合用户实际投诉次数、用户购买品类、用户支付的金额等,进行用户投诉倾向类型的识别,方便客服进行分类处理。}
-
预测标签
则是在模型的基础上做预测,比如针对投诉倾向类型结构的变化,预测平台风险指数.
-
标签的命名&赋值
命名是字段名称(列名), 赋值是赋给列名一个具体的列值
-
标签的属性: 标签属性说明了标签的来源,一个标签可能具有多个属性
-
标签体系成功建立,但有可能标签无法获得或者标签无法赋值
- 数据无法采集(无法准确采集, 身份证号)
- 数据库不能打通
- 建模失败(预测指标无法获得赋值)
3.2 用户画像标签层级的建模方法(重点)
3.2.1 建模分层
- 原始数据层。(bdm 贴源缓存层)。
- **mysql数据库中的业务数据, 埋点数据(flume采集到kafka), 其他数据–>**清洗,结构化后的数据数据
- 文本挖掘的算法进行分析如常见的TF-IDF、TopicModel主题模型、LDA 等算法,主要是对原始数据的预处理和清洗,对用户数据的匹配和标识。
- 事实标签层(fdm层)。
- 对原始数据层进行统计分析(分类和聚类)。分类主要用于预测新用户,信息不全的用户的信息,对用户进行预测分类。聚类主要用于分析挖掘出具有相同特征的群体信息,进行受众细分,市场细分。
- 对于原始数据层的文本的特征数据,其主要使用相似度计算,如余弦夹角,欧式距离等。
- 模型标签层 gdm
- 用到的算法:回归,决策树,支持向量机等.
- 对事实标签层建模分析: 我们可以进一步挖掘出用户的群体特征和个性权重特征,从而完善用户的价值衡量,服务满意度衡量等。
- 预测层(adm层)。
- 也是标签体系中的营销模型预测层。这一层级利用预测算法,如机器学习中的监督学习,计量经济学中的回归预测,数学中的线性规划等方法。
- 实习对用户的流失预测,忠实度预测,兴趣程度预测等等,从而实现精准营销,个性化和定制化服务。
3.2.2 用户画像建模(gdm通用聚合层)–>重要的5张表
数据可视化需要的标签
(1) 用户基本属性表
-
数据来源: 用户表,用户调查表,孕妇模型表(数据挖掘),马甲模型表(数据挖掘)
马甲是指一个用户注册多个账号
多次访问地址相同的用户账号是同一个人所有
同一台手机登陆多次的用户是同一个人所有
收货手机号相同的账号同一个人所有
-
模型算法: 将纯文本通过laberencoder/onehotencoder编码==>特征, 最终得到类别标签. 回归算法
- 性别模型
- 用户汽车模型
- 用户忠诚度模型
- 用户身高模型
- 用户马甲标志模型
- 收集相关标签模型
-
可以得到的重要标签
- 姓名,性别,身高,体重,星座等
- 是否孕妇,是否有小孩,孩子性别概率,孩子年龄概率
- 潜在汽车用户概率,
- 手机品牌,更换手机频率
- 马甲标志,马甲账号数量
(2) 客户消费订单表
- 数据来源:用户表, 购物车表,订单表, 退货表,
- 可以得到的标签及标签作用
- 订单宽表 gdm层的一个表
- 尾单距今时间–分析用户什么时候来购买商品以及多久没有购买了.
- 累计使用代金券次数–分析用户总体消费情况.
- 近60天客单件(含退拒)–分析用户消费水平
- 常用支付方式----分析用户常用的消费属性,方便做定向营销
- 退货表
- 近30天购买金额(含退拒)–分析用户最近的消费能力。
- 最近一次退货时间-----分析用户拒收和退货习惯。
- 购物车表可以得到相关标签
- 最近30天购物车成功率------分析用户购物车使用习惯
- 订单表和用户表可以得到相关标签
(3) 客户购买类名表
- 主要数据来源:订单表、购物车表、类目维表
- 可以得到的标签及标签作用
- 类目维表可以得到相关标签
- 订单表和类目维表可以得到相关标签
- 累计购买类目金额—-分析用户最近都购买了哪些类目
- 最后一次购买类目距今天数----分析用户多久没有购买这个类目
- 购物车表和类目维表可以得到相关标签
- 近90天购物车类目金额----分析用户最近都挑中哪些类目
(4) 用户访问信息表
- 主要数据来源:点击流日志行为表(PC/APP端)
- 可以得到的标签及标签作用
- 点击流日志行为表可以得到相关标签
- 最近一次访问的省份-----分析用户最近一次访问情况
- 第一次访问的省份-----分析用户第一次访问情况
- 近365天APP/PC端访问次数----分析用户APP/PC端访问次数
- 近30天PC/APP端最常用IP-----分析用户访问详情
- 近30天22-23点访问的次数----分析用户喜欢在哪个时间上网访问。
(5) 用户宽表汇总模型表
gdm层的4张表ETL抽取出来的
3.3 hive仓库分层
-
数据仓库分层:
BDM:贴源缓存层,源数据的直接映像 Buffered data
FDM:基础数据层,数据拉链处理、分区处理 Foundation data
GDM:通用聚合 General aggregation
ADM:高度聚合 altitude
-
先把数据从源数据库中抽取加载到BDM层中,
然后FDM层根据BDM层的数据按天分区
ADM |
用户画像宽表模型 |
adm.itcast_adm_personas |
|
|
|
4. 商用户画像数据处理的流程
4.1 数据源
- 业务数据
- 当前电商系统中产生的数据,包括用户表,订单表,订单商品表,订单详细信息表,购物车表等,该数据是存储在业务系统的关系型数据库中(例如mysql)。
- 点击流数据
- 该数据记录了用户的游览轨迹数据,随着用户在页面上的一系列操作都会产生大量的点击流数据。
4.2 数据采集
考虑一定的数据清洗,比如异常字段的处理、字段命名规范化、时间字段的统一等
- 1、业务数据采集
- 通过sqoop工具每天定时的把昨天业务系统中的增量数据抽取到HDFS中,每天的数据按照分区进行存储。bdm层
- 2、点击流数据的采集–>结构化后到达bdm层
- 通过部署flume进行日志数据的实时收集到HDFS中,每天的数据按照分区进行存储。
4.3 hive构建数据仓库
- 业务数据和点击流数据收集到HDFS中后,通过hive构建数据仓库系统。
- 构建数据仓库的2种模式(星型模型和雪花模型)区别和优缺点
- https://blog.csdn.net/nisjlvhudy/article/details/7889422
3.4 数据的处理
- 通过sparksql 整合hive,获取hive表中的元数据,然后通过sparksql来进行大量的分析统计, 把HiveHQL底层采用MapReduce来处理任务,导致性能慢的特点,改为更加强大的Spark引擎来进行相应的分析处理,快速的为用户打上标签构建用户画像。
4.5 hive整合hbase
- 把通过sparksql统计分析得到的模型表数据整合到用户画像宽表中,最后把数据写到hbase中,提供海量数据的存储和快速查询,把满足条件用户筛选出来。
- rowkey如何设计?
- (1)还是通过用户画像宽表中user_id最后去构建对应rowkey
- 1、可以写一个自定义udf函数,函数逻辑:给定义一个user_id,最后返回一个经过md5加密之后的字符串 md5(user_id)=xxxxxxxxxxxxxxxxxxxxxx
- 2、拼接rowkey=md5(user_id)取后10位 + user_id
4.6 hbase整合phoenix
- 数据存入hbase后,后期需要查询hbase表中的数据来展示不同用户的标签属性,这个时候可以开发的客户端代码,这里由于标签属性比较多,客户端代码开发起来比较大,并且性能也不是特别好,可以通过hbase整合phoenix,后期通过phoenix的sql进行查询,代码量大大减少,性能也优越hbase原生的api代码
4.7 数据可视化
5. 电商用户画像大数据平台规划(★★★★★)
-
整个集群的规模有多大?30台
-
集群都部署了哪些服务?zk(7台,journalNode), hadoop,flume,sqoop,hive,hbase,phoenix,spark(高可用),
-
每天的增量数据是多少?50-100G
电商用户画像开发周期:
大数据项目划分为几个周期:
第一期:目的是希望把当前电商网站中产生的所有数据都接入进来,然后基于这些数据把上标签。
业务数据+点击流日志数据------->它自身的一些数据
实现周期4个月左右。
第二期:比如后期有新的数据源引入进来---->举例:京东商城 还有对应京东白条,京东金融
第三期:京东和第三方公司合作:比如腾讯、阿里
6. 公司集群规划
-
1、先梳理下公司的业务
- 主要是看一下有哪些数据源,每天的增量数据是多少
- 比如大致的算了一下每天的增量数据100G
-
2、数据会存储在HDFS上
- hdfs上副本数为3
- bdm 100G -->buffered data 缓冲数据,外部表
- fdm 50G -->fundation data 基础数据层,分区层.
- gdm 20G --> general data 通用聚合层
- adm 10G --> aggregation data 高度聚合
- 再次算一下每天增量数据
- 100+50+20+10=180G
- hdfs上副本数为3
- 在实际通过服务器存储数据的时候,最好是不要超过服务器实际硬盘存储的容量的80%
-
3、每天的实际需要存储容量540G
7. 构建用户画像的关键难题
- 实时采集用户数据: flume挂掉了, 重新采集
- 数据倾斜: 出现了热销产品 订单放到缓存中
- 内存不够用: 原因是task数量过多,每一个task都要存一部分数据, 把不变的字典数据广播出去.
- 数据不全,总是少几个省份的数据, kafka主题名重复了, 给主题每个起的加上自己的工号.
day12 flink
1. 三代计算引擎
- hadoop shuffle, 环形缓冲区
- spark
- flink 计算引擎
- DAG
- 批处理,流处理
- flink1.5版本出现了sql
- 快照容错,
- 自动优化,避免shuffle
2. flink和spark对比
3. flink的框架
taskManager之间传递的是流
访问端口 : http://node01:8081
4. flink流式处理特性
4.1 特点
支持小文件上传, 会话机制, 能一直占着这个资源,只要不关闭, 小文件过来直接处理.
4.2 API支持
-
对于Streaming数据类应用, 提供DataStream API
-
对于批处理类应用, 提供DataSet API(java/scala)
4.3 本地库支持
4.4 整合支持
- flink on yarn
- hdfs
- kafka
- hbase
- elasticSearch
day13 kylin
1. kylin概述
1.1 什么是kylin
- 开源的分布式存储引擎,把各种要用到的中间分析数据和结果提前分析查询出来,存储到一个地方, 到时候想要用到什么结果,去固定地方取数据就可以了,快速.
- 提供sql查询接口及多维分析OLAP(联机分析处理,数据仓库)
1.2 为什么使用kylin
- 存储和批处理问题已得到了妥善解决
- 高速地分析数据? hive, impala,phoenix(hbase),使用到的技术
1.3 kylin如何解决大数据分析高效问题
-
OLAP
- 统计结果,原始记录不重要
- 聚合是按维度进行的.有意义的维度是有限的,不会随着数据的膨胀而增加维度
-
kylin采用预计算
- 订单表, 三个维度, 就有2的3次方8种组合
- sql查询接口获取结果
-
kylin的应用场景
2. 前置知识
2.1 数据仓库,OLAP,BI
- 数据仓库 data warehouse
- 面向主题
- 集成的
- 相对稳定的 分析,不删除和修改
- 随着时间不断变化(不同时间)的数据集合
- OLAP& OLTP
- OLAP面向分析的数据库,数据仓库是它的一个子集
- OLTP面向事务, 关系型数据库
- BI(business Intelligence) 商务智能
2.2 事实表和维度表
2.3 数据立方体(data cube)
- cube 是一种常用于数据分析与索引的计数, 可以对原始数据建立多维度索引,通过cube对数据进行分析, 可以大大加快数据的查询效率
- 每一种维度的组合, 叫做cuboid
- 所用维度组合cuboid作为一个整体叫做cube.
3. Kylin工作原理
4. kylin的体系结构
5. 搭建初体验
5.1 安装
- 端口7077
- 自带两个维度表,一个事实表, 1M(10000条数据),smple.sql, 能够导入到hive中
--------第三部分
day01 java重点关注的知识点
1. 杂记知识
1.1 反射
-
原理
Demo.java javac.exe>Demo.classjava.exe>JVM虚拟中运行
java.exe运行,类加载器会把.class文件加载到方法区中,然后会在堆内存中创建一个Class类的对象,
即字节码对象,字节码对象中包含了Filed字段对象,Contructor构造器对象,Method方法对象,
可以单独获取到这些对象,进行相应的操作
为什么使用反射:
好处: java中有一个原则: 开闭原则(对修改源代码是关闭的;但是对扩展是开放的)
我们只定义了一个规则: 配置文件中有哪些内容:
classname 具体配置的值是什么,不固定
method 具体配置的值不固定
1.2 Servlet相关
1.3 多线程
public class Thread1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
public static void main(String[] args) {
Thread1 thread1 = new Thread1();
thread1.start();
}
}
public class Thread2 implements Runnable {
@Override
public void run() {
}
public static void main(String[] args) {
Thread thread = new Thread(new Thread2());
thread.start();
}
}
public class Thread3 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return null;
}
}
public class Thread4 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
ExecutorService executorService1 = Executors.newCachedThreadPool();
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
ExecutorService executorService2 = Executors.newSingleThreadExecutor();
ScheduledExecutorService scheduledExecutorService1 = Executors.newSingleThreadScheduledExecutor();
ExecutorService executorService3 = Executors.newWorkStealingPool();
}
}
1.4 io
2. jvm
day02 算法
1. 协同过滤算法