Spark实战
实战
数据导入Hive中
全量:
拉链
增量:
用户、商品表数据量大时用 拉链表
动作表 增量
城市信息 全量
需求一: 获取点击、下单和支付数量排名前 的品类
①使用累加器:
click_category_id,个数
order_category_ids,个数
pay_category_ids,个数 ②在Driver端进行累加处理
click_category_id_click,个数
order_category_ids_order,个数
pay_category_ids_pay,个数 (9_click,)
(2_click,)
(,,3_order,)
(,,3_pay,) ③分组合并groupBy
(click_category_id,Map(click_category_id_click -> 个数))
(order_category_ids,Map(order_category_ids_order -> 个数))
(pay_category_ids, Map(order_category_ids_order -> 个数)) (,Map(12_click -> ))
(,,,Map(,,3_order -> , ,,3_pay -> ))
(,Map(3_click -> )) ④将合并的数据转换为一条数据map
CategoryTop10(taskid, categoryid, clickcount, ordercount, paycount)
CategoryTop10(-852b--99d2-1ebb99292df6,,,,) ⑤把它变成集合可排序,将数据排序后取前10条 .sortWith .take()
按clickcount排序,从大到小
CategoryTop10(7a867338-d4b6-4da4-b7c0-fbd7c1477157,,,,)
CategoryTop10(7a867338-d4b6-4da4-b7c0-fbd7c1477157,,,,)
一个用户可能会有多个session,所以不按用户进行分组而是按session
需求二: Top10 热门品类中 Top10 活跃 Session 统计
① 获取前10的品类数据 CategoryTop10(taskid, categoryid, clickcount, ordercount, paycount)
②将当前的日志数据进行筛选过滤filter(品类,点击)筛选出categoryid, clickcount
categoryIds <-- top10Data.map(_.categoryId)
从原始数据中过滤出top10的品类: click_category_id != - categoryIds.contains("" + action.click_category_id)
UserVisitAction(--,,bf9dfe31--49a4-a513-78ce49758d08,,-- ::,null,,,null,null,null,null,)
③对筛选后的数据进行聚合:
filterRDD.map( ).reduceByKey(_+_) 转换结构,聚合 (categoryId_sessionId, clickCount)
(8_fae66028-0fe7-44f7--e24665523e9e,)
(12_1b1328cb-fabd--a029-416d9c7e6c78,) ④根据品类进行分组
.map split("_") (categoryId, (sessionId, clickCount)) (,(1b1328cb-fabd--a029-416d9c7e6c78,))
.groupBy 只保留V即可 groupBy会把分组之后的保存在CompactBuffer中
((3315e7ec-931f-45a0-95f2-183208f9ca34,),CompactBuffer((,(3315e7ec-931f-45a0-95f2-183208f9ca34,)), (,(3315e7ec-931f-45a0-95f2-183208f9ca34,)))) ⑤变成List再排序.sortWith(降序),获取前10条数据
.mapValues() 是针对于(K,V)形式的类型只对V进行操作排序.sortWith--scala中的一个排序方法,而sortBy是可以自定义排序规则
List((,(3196a615-f034-4a6b-a3b2-aab8a928addc,)), (,(3196a615-f034-4a6b-a3b2-aab8a928addc,))) 针对V排序 ._2._2对clickCount排序从高到低 转换结构map(_._2) 只保留V即可--> List((,(ffe3e64f-7e0a-40a0-9b63-d0264e00de65,)), (,(ffe3e64f-7e0a-40a0-9b63-d0264e00de65,))) ⑥偏平化处理变成一条一条.flatMap(x => x)
(categoryId, (sessionId, clickCount)) (,(ffe3e64f-7e0a-40a0-9b63-d0264e00de65,))
(,(ffe3e64f-7e0a-40a0-9b63-d0264e00de65,))
需求3 页面单跳转化率统计
. 获取分母:每一个页面点击次数总和
①从配置文件中读取并切分.split转成Array类型 pageids
②过滤,从原始数据(.contains)中过滤出符合 pageid的数据
③转换结构(pageid, 1L)如: (,) (,)并聚合.reduceByKey(_+_)统计 (,) (,)
④转换结构为map类型k, v对的形式 (,), (,) . 获取分子:应该是根据过滤之前的数据进行统计,不应该是过滤之后的数据
① 根据session进行分组, 不应该根据用户分组,一个用户可以访问多个
.groupBy 按传入函数的返回值 session_id进行分组
(cff267a0-36f5-4be6-abe1-51bcaee5e976,CompactBuffer(UserVisitAction(--,,cff267a0-36f5-4be6-abe1-51bcaee5e976,,-- ::,null,,,null,null,null,null,), UserVisitAction(--,,cff267a0-36f5-4be6-abe1-51bcaee5e976,,-- ::,i7,-,-,null,null,null,null,)))
②将分组后的数据进行排序
.mapValues{ .toList.sortWith } //mapValues是对V进行操作排序,按action_time从大到小排
.map(_.page_id)
.zip(pageidList.tail) 再使用拉链 zip 将List(,,,, ) zip List(,,, ) 组合在一块(-), (-), (-), (-);
再转换结构.map ( (-, ), (-, ), (-, ), (-, ) ) ===>>(437ffda2-0e5b--b5ca-b86441b10dc9,List((-,), (-,), (-,), (-,))) ③转换结构,只保留V List类型即可List((-,), (-,), (-,), (-,))
再扁平化 .flatMap(x => x) -->(-,) (-,) (-,) (-,) ④将不需要关心页面流转的数据过滤掉
.filter (pageids.zip(pageids.tail).map()).contains(k) k即上边数据(-,)中的k即8-
pageids.zip(pageids.tail).map()
-
-
-
-
-
-
⑤统计符合要求的个数.reduceByKey(_+_) // (1-2, 100)
. 页面单跳点击次数 / 页面点击次数
遍历分子(-, ),
分母: (,), (,) 切分split("-")取出第一个()作为key值取出value即分母
补充需求:网站页面平均停留时长
网站页面平均停留时长
1 获取离线数据
2 将数据根据session进行分组 .groupBy
3 将分组后的数据按照访问时间进行排序
.mapValues(datas => {
datas.toList.sortWith { }.map() //(page_id,action_time).zip( .tail).map() //(before._1, (endTime - startTime))
})
4 采用拉链的方式将数据进行组合,形成页面跳转路径(A=>B,B=>C)
.map() //不关心session了 将集合数据进行扁平化操作.flatMap(x => x) 5 对跳转路径中的第一个页面进行分组.groupByKey(),聚合数据
.mapValues(datas => {
datas.sum / datas.size
})
6 对聚合后的数据进行计算,获取结果.foreach(println)
实时数据分析
redis
redis中5大数据类型是指V,K都是String
①Set:
set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,
当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,
并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。
Redis的Set是string类型的无序集合。它底层其实是一个value为null的hash表,所以添加,删除, ②hash 是一个键值对集合。
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
类似Java里面的Map<String,Object>
DSream 代表了一系列连续的RDD,DStream中每个RDD包含特定时间间隔的数据
实时数据分析: 广告黑名单实时统计
实现实时的动态黑名单机制:将每天对某个广告点击超过 100 次的用户拉黑。
注:黑名单保存到redis中。
已加入黑名单的用户不在进行检查。
[kris@hadoop101 bin]$ ./kafka-topics.sh --zookeeper hadoop101:2181 --list
kafka中创建topic: ads_log
[kris@hadoop101 bin]$ ./kafka-topics.sh --zookeeper hadoop101:2181,hadoop102:2181,hadoop103:2181 --create --topic ads_log --partitions 3 --replication-factor 3
[kris@hadoop101 bin]$ ./kafka-console-consumer.sh --zookeeper hadoop101:2181 --topic ads_log --from-beginning
序列化、反序列化--生产者、消费者
kafka分段日志,spark分段文件;序列化调用writeReplace()
需求4:广告黑名单实时统计
kafka中的数据格式:
timestamp province city userid adid
1555928451187 华北 北京 4 2
1555928451187 华北 北京 3 6
1.从kafka中周期性获取广告点击数据
KafkaUtil.getKafkaStream(topic, streamContext) 2.将数据进行分解和转换:
①转换成含有这样类型的kafkaMessage的数据 .map(),使用样例类
②将获取的数据进行筛选(过滤掉黑名单的数据)使用.transform
.transform(rdd => {
②.1获取Jedis的连接; ②.2取出集合中的所有值,使用redis的Set类型
②.3设置广播变量(可实现序列化和向所有节点发送数值) 这些都是在Driver端执行,可随着executor执行相同的次数
rdd.filter({
在executor端执行
})
})
②.4关闭jedis.close()
另外一种方法是将cp状态值保存到redis中,这样就不用下面这些步骤了;
.foreachRDD(rdd=>{
rdd.foreachPartition(messages=>{
for (message <- messages) {
val dateString: String = SparkmallUtils.parseStringDateFromTs(message.ts.toLong, "yyyy-MM-dd")
val key = "date:user:ad:clickcount" //dateString + "_" + message.userid + "_" + message.adid
val field = dateString + "_" + message.userid + "_" + message.adid
jedis.hincrBy(key, field, 1) // 将redis中指定的key集合,对field字段的值进行数据累加;选择的redis类型为hash
val sumString: String = jedis.hget(key, field) //获取值进行判断是否点击次数超过100次,超过就添加到黑名单中
val sum = sumString.toLong
if ( sum >= 100 ) {
jedis.sadd("blacklist", message.userid) //将黑名单也添加到redis集合中,类型为set
}
}
jedis.close()
}
})
})
③转换为这种格式:(ts_user_ad, 1) 3 将转换的结果进行聚合 (采集周期内):(ts_user_ad, sum)
4 将不同周期中采集的数据进行累加(有状态的)
①设定CP的路径 streamingContext.sparkContext.setCheckpointDir("cp")
②.updateStateByKey{
case (seq, opt) => { //Seq[Int], Option[s] Seq可直接.sum
// 将当前采集周期的统计结果和CP中的数据进行累加
val sum: Int = seq.sum + opt.getOrElse(0)
Option(sum) // 将累加的结果更新到CP中
}
}
5 将累加的结果进行判断,是否超过阈值(100)
.foreachRDD(rdd => {
rdd.foreach{
case(key, sum) => {
if(sum > 100){
6 如果超过阈值,那么将用户加入黑名单,防止用户继续访问
val jedis: Jedis = new Jedis("hadoop101", 6379) val userid: String = key.split("_")(1)// 获取用户
jedis.sadd("blacklist", userid)
}
}
}
})
127.0.0.1:6379> SMEMBERS blacklist
//foreachRDD参数应该实现将每一个RDD中数据推送到外部系统,如将RDD存入文件或者通过网络将其写入数据库。
//对DStream中的RDD采取任意的操作
需求5: 广告点击量实时统计
1.从kafka中周期性获取广告点击数据
KafkaUtil.getKafkaStream(topic, streamContext) 2.将数据进行分解和转换:
①转换成含有这样类型的kafkaMessage的数据 .map(),使用样例类 方法二:可直接添加到redis中,省去下面的步骤了;
.foreachRDD(rdd => {
rdd.foreachPartition(messages => {
val jedis: Jedis = RedisUtil.getJedisClient
for (message <- messages) {
val dateString: String = SparkmallUtils.parseStringDateFromTs(message.ts.toLong, "yyyy-MM-dd")
val key = "date:area:city:ads"
val field = dateString + "_" + message.area + "_" + message.city + "_" + message.adid
jedis.hincrBy(key, field, 1)
}
jedis.close()
})
}) ②将采集周期中的数据进行聚合转换结构--> (ds_area_city_ad, 1)
3.将不同周期中采集的数据进行累加(有状态的)
①设定CP的路径 streamingContext.sparkContext.setCheckpointDir("cp")
②.updateStateByKey{
case (seq, opt) => { //Seq[Int], Option[s] Seq可直接.sum
// 将当前采集周期的统计结果和CP中的数据进行累加
val sum: Int = seq.sum + opt.getOrElse(0)
Option(sum) // 将累加的结果更新到CP中
}
}
(2019-04-22_华北_北京_1,109)
(2019-04-22_华北_天津_2,40)
(2019-04-22_华东_上海_6,133) 4.将聚合后的数据更新到redis中; 是更新不是累加,跟上一个需求有点不一样;
.foreachRDD(rdd => {// RDD[(String, Int )] => Unit
rdd.foreachPartition(datas => { //foreachPartition用于在每个分区创建一个连接
val jedis: Jedis = RedisUtil.getJedisClient
for ((field, sum) <- datas) {
val key = "date:area:city:ads"
jedis.hset(key, field, "" +sum)
}
jedis.close()
})
})
基于需求5广告点击量实时统计 --> 需求6 每天各地区 top3 热门广告
每天各地区 top3 热门广告
1 获取需求5的数据
2 将获取的数据进行格式转换(date_area_city_adv,sum)-->(date_area_adv,sum)
转换结构.map
3 将转换的数据进行聚合统计:(date_area_adv,sum)--> (date_area_adv,totalSum)
.reduceByKey(_+_)
4 将统计的结果进行结构转换:(date_area_adv,totalSum) -->( date_area, ( adv, totalSum ) )
.map
5 将转换后的数据进行分组排序:( date_area,Map( adv1totalSum1, adv2totalSum2 ) )
.groupByKey()
(2019-04-22_华东,ArrayBuffer((上海,199)))
(2019-04-22_华南,ArrayBuffer((广州,49), (深圳,118)))
(2019-04-22_华北,ArrayBuffer((天津,57), (北京,138))) .mapValues(datas => { datas.toList.sortWith {
case (left, right) => {
left._2 > right._2
}
}.take(3).toMap //对数据进行排序,获取排序后数据的前三名; 转换成map集合类型
})
6 将结果保存到redis中
.foreachRDD(rdd => {
rdd.foreachPartition(datas => {
val jedis: Jedis = RedisUtil.getJedisClient
for ((k, map) <- datas) {
val ks: Array[String] = k.split("_")
val key = "top3_ads_per_day:" + ks(0)
// 将Scala集合转换为JSON字符串;因为结果是{ }类型的
import org.json4s.JsonDSL._
val value: String = JsonMethods.compact(JsonMethods.render(map))
jedis.hset(key, ks(1), value)
}
jedis.close()
})
})
需求7:最近一小时广告点击趋势
1.从kafka中周期性获取广告点击数据
KafkaUtil.getKafkaStream(topic, streamContext) 2.将数据进行分解和转换:
①转换成含有这样类型的kafkaMessage的数据 .map(),使用样例类 3.使用窗口函数将多个采集周期的数据作为一个整体进行统计
.window(Seconds(60), Seconds(10)) //周期60s,步长10s
4 获取数据,将数据根据窗口的滑动的幅度进行分组
将数据进行结构的转换(KafkaMessage)==> (time,1)
.map(message => {
var time: String = SparkmallUtils.parseStringDateFromTs(message.ts.toLong, "yyyy-MM-dd HH:mm:ss")
val prefixTime: String = time.substring(0, time.length - 1)
time = prefixTime + "0" //将秒变成的个位数去掉,补0
(time, 1)
})
5 将分组后的数据进行统计聚合.reduceByKey(_ + _)
6 将统计的结果按照时间进行排序
.transform(rdd => { //转换,用时间进行排序,false是从小往大
rdd.sortBy(t => {
t._1
}, false)
})
Spark实战的更多相关文章
- Spark实战1
1. RDD-(Resilient Distributed Dataset)弹性分布式数据集 Spark以RDD为核心概念开发的,它的运行也是以RDD为中心.有两种RDD:第一种是并行Col ...
- Spark GraphX宝刀出鞘,图文并茂研习图计算秘笈与熟练的掌握Scala语言【大数据Spark实战高手之路】
Spark GraphX宝刀出鞘,图文并茂研习图计算秘笈 大数据的概念与应用,正随着智能手机.平板电脑的快速流行而日渐普及,大数据中图的并行化处理一直是一个非常热门的话题.图计算正在被广泛地应用于社交 ...
- Spark实战--搭建我们的Spark分布式架构
Spark的分布式架构 如我们所知,spark之所以强大,除了强大的数据处理功能,另一个优势就在于良好的分布式架构.举一个例子在Spark实战--寻找5亿次访问中,访问次数最多的人中,我用四个spar ...
- Spark入门实战系列--6.SparkSQL(下)--Spark实战应用
[注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .运行环境说明 1.1 硬软件环境 线程,主频2.2G,10G内存 l 虚拟软件:VMwa ...
- 云计算分布式大数据神器Spark实战高手之旅
从2012年1月份研究Spark到如今已经两年多的时间了. 在这两年多的时间里比較彻底的研究了Spark的源码并已经在2014年4月24日编写完毕了世界上第一本Spark书籍. 鉴于CSDN在大陆IT ...
- Spark实战之读写HBase
1 配置 1.1 开发环境: HBase:hbase-1.0.0-cdh5.4.5.tar.gz Hadoop:hadoop-2.6.0-cdh5.4.5.tar.gz ZooKeeper:zooke ...
- Spark实战系列目录
1 Spark rdd -- action函数详解与实战 2 Spark rdd -- transformations函数详解与实战(上) 3 Spark rdd -- transformations ...
- Spark实战电影点评系统(二)
二.通过DataFrame实战电影点评系统 DataFrameAPI是从Spark 1.3开始就有的,它是一种以RDD为基础的分布式无类型数据集,它的出现大幅度降低了普通Spark用户的学习门槛. D ...
- Spark实战电影点评系统(一)
一.通过RDD实战电影点评系统 日常的数据来源有很多渠道,如网络爬虫.网页埋点.系统日志等.下面的案例中使用的是用户观看电影和点评电影的行为数据,数据来源于网络上的公开数据,共有3个数据文件:uers ...
随机推荐
- 开源顶级持久层框架——mybatis(ibatis)——day01
mybatis-day01 1.对原生态jdbc程序中的问题总结 1.1环境 java环境:jdk eclipse:indigo ...
- 移除文件(git rm)
git rm`命令会把文件从已跟踪列表(及暂存区)中移除,并且移除把文件从工作目录中移除,这样下一次你就不会在未跟踪文件列表中看到这些文件了. 如果你只是简单的把文件从工作目录移除,而没有使用git ...
- 20164305徐广皓 - Exp1 PC平台逆向破解(5)M
1.逆向及Bof基础实践说明 1.1实践目标 实践对象:pwn1的linux可执行文件 实践目的:使程序执行另一个代码(ShellCode) 实践内容: 手工修改可执行文件,改变程序执行 ...
- pythonのdjango 缓存
由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcache中,5 ...
- Power BI行级别安全性(数据权限管理)
自从PowerBI 的DAX 函数 支持username() 或 userprincipalname()的函数后,我们就可以在Power BI中实现根据用户的行级数据权限的控制. username() ...
- [Kubernetes]深入理解StatefulSet
前面我写的一系列博客,如果你能够耐心看到这一篇,那你应该对一个概念就不是太陌生了:Deployment. 为什么提这个概念呢,这就要说到Deployment的一个不足了.Deployment不足以覆盖 ...
- 项目Alpha冲剂(3/10)
1.项目燃尽图 2.今日进度描述 项目进展 完成数据库和服务器的连接部分,完成了一些应用的基本功能. 问题困难 完成了服务器的成功配置,同时实现了客户端与服务器的连接 心得体会 进度有明显的变化,成员 ...
- Virtual Machine
之前说到可以使用Assembly language来实现程序编写,把程序通过一个Assembler就可以得到计算机可以操作的二进制文件. 但是Assembly language依旧不适于编程,但怎么将 ...
- 前端笔记之JavaScript(五)关于数组和字符串那点事
一.数组 1.1数组概念 数组(array)是一个有序的数据集合.说白了,数组就是一组数.数组内部可以存放一个或多个单独的数据,整体组成数组. 定义数组最简单的方式:数组字面量. 数组的字面量“[]” ...
- python安装过程中的一些问题
因为看到大神的教程是基于python V2.7,下载该版本且安装成功. 安装目录: https://www.python.org/download/releases/2.7/ 根据系统进行安装包下载 ...