package com.fuyun.bigdate.spark
import java.sql.{Connection, PreparedStatement}
import org.apache.spark.storage.StorageLevel
import org.apache.spark.{SparkConf, SparkContext}
/**
* 统计PVUV将结果保存到MySQL中.
*/
object PvUvToMysql {
def main(args: Array[String]): Unit = {
/**
* 初始化
*/
// 实例化sparkconf并设置程序运行名称
val sparkConf = new SparkConf().setAppName("pvuv")
// 实例化sparkcontext并将sparkconf传入
val sc = new SparkContext(sparkConf)
// 设置日志级别
sc.setLogLevel("WARN")
/**
* 输入
*/
// 读取输入文件的内容,并设置分区数为4
val inputRdd = sc.textFile("hdfs://192.168.xxx.xx/datas/2015082818.data",4)
// 打印文件内容的第一行
println(s"first = ${inputRdd.first()}")
// 统计文件的行数
println(s"count = ${inputRdd.count()}")
/**
* 转换
*/
// 先将输入的每行去除首尾空格,再保留长度大于0的元素
val filterRdd = inputRdd.filter(_.trim.length > 0)
.map(line => {
// 将每行数据按"\t"分割
val arr = line.split("\t")
// 返回date、url、guid
(arr(17).substring(0, 10),arr(1), arr(5))
})
// 缓存
filterRdd.persist(StorageLevel.MEMORY_AND_DISK_SER_2)
// pv
val pvRdd = filterRdd.map{
// 取出date、url
case (date, url, guid) => (date, url)
}
// 先将每个元素去除首尾空格,然后只保留长度大于0的元素
.filter(_._2.trim.length > 0)
// 返回(date,1)的元组
.map(tuple => (tuple._1, 1))
// 按日期分组求和统计每天的pv数
.reduceByKey(_ + _)
println("====================pv output======================")
// foreachPartition比foreach更优化
pvRdd.foreachPartition(iter => iter.foreach(println))
// uv
// 先得到(date, guid)
val uvRdd = filterRdd.map{case (date, url, guid) => (date, guid)}
// 去重
.distinct()
// 先将元素去除首尾空格再保留长度大于0 的元素
.filter(_._2.trim.length > 0)
// 构造(date, 1)的元组
.map(tuple => (tuple._1, 1))
// 按date分组再根据value求和统计每天的uv数
.reduceByKey(_ + _)
println("======================uv output========================")
uvRdd.foreachPartition(iter => iter.foreach(println))
/**
* union
* def union[T: ClassTag](first: RDD[T], rest: RDD[T]*)
* 注意:union合并时要求两个rdd的类型必须一致
*/
val unionRdd = pvRdd.union(uvRdd)
unionRdd.foreach(println)
/**
* join
* def join[W](other: RDD[(K, W)], partitioner: Partitioner): RDD[(K, (V, W))]
* 注意:join时要求所有的RDD必须为二元组类型的rdd
*
*/
val joinRdd = pvRdd.join(uvRdd)
joinRdd.foreach{case (date, (pv, uv)) => println(s"${date}\t${pv}\t${uv}")}
/**
* 输出
*/
// 合并后设置分区数为1
joinRdd.coalesce(1).foreachPartition(iter => {
// conn初始化为null
var conn:Connection = null
// 初始化ps_create为null
var ps_create:PreparedStatement =null
// 初始化ps_tru为null
var ps_tru:PreparedStatement =null
// 初始化ps_insert为null
var ps_insert:PreparedStatement =null
try{
// 得到connection
conn = ConnectionUtils.getConnection
// 如果表不存在,则创建表
val sql_create = "create table if not exists pvuv_tb (date varchar(66), pv int(10), uv int(10))"
ps_create = conn.prepareStatement(sql_create)
ps_create.execute()
// 清空表格内容
/*val sql_tru = "truncate pvuv_tb"
ps_tru = conn.prepareStatement(sql_tru)
ps_tru.execute()*/
// 插入数据
val sql_insert = "insert into pvuv_tb values (?, ?, ?)"
ps_insert = conn.prepareStatement(sql_insert)
iter.foreach{
case (date, (pv, uv)) => println(s"${date}\t${pv}\t${uv}")
ps_insert.setString(1, date) // 将date添加到第一个?
ps_insert.setInt(2, uv) // 将pv添加到第二个?
ps_insert.setInt(3, uv) // 将uv添加到第三个?
ps_insert.executeUpdate() // 提交SQL语句
}
}catch{
case e:Exception => println("MySQL Exception")
}finally{
// 关流
if (ps_create != null) {
ps_create.close()
}
if (ps_tru != null) {
ps_tru.close()
}
if (ps_insert != null) {
ps_insert.close()
}
ConnectionUtils.closeConnection(conn)
}
})
// 关闭缓存
filterRdd.unpersist()
/**
* 关闭资源
*/
// 设置线程睡眠时间
Thread.sleep(10000000000L)
sc.stop()
}
}
package com.fuyun.bigdate.spark
import java.io.InputStream
import java.sql.{Connection, DriverManager, PreparedStatement}
import java.util.Properties
/**
* Created by lenovo on 2018/9/26.
*/
object ConnectionUtils {
// 读取properties文件
val inputStream:InputStream = ConnectionUtils.getClass.getClassLoader.getResourceAsStream("jdbc.properties")
// 实例化properties对象
val prop = new Properties()
// 加载properties文件
prop.load(inputStream)
// 加载driver.class.name
Class.forName(prop.getProperty("driver.class.name"))
// 加载mysql.url
val url = prop.getProperty("mysql.url")
// 加载mysql.user
val username = prop.getProperty("mysql.user")
// 加载mysql.password
val password = prop.getProperty("mysql.password")
def getConnection:Connection = {
DriverManager.getConnection(url, username, password)
}
def closeConnection(conn: Connection){
if (conn != null) {
conn.close()
}
}
}
此文件在main中的resources中创建,方便以后修改此文件而实现不同的需求
driver.class.name=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://192.168.xxx.xx:3306/spark_db
mysql.user=root
mysql.password=123456
idea打包java可执行jar包
1,在项目上鼠标右键 --> Open Module Settings
2, Artifacts --> + --> JAR --> From modules with dependencies…
3, Main Class是你这个项目(脚本)的主方法,就是要运行的类,选一个
4,如下图,设置 META-INF/MANIFEST.MF
5,选中你这个项目的根目录,一定要放在根目录下
6,设置完是这样子的,关于 JAR files from libraries的两个选项:
选中第一个的话,打完包后是一个jar包
选中第二个的话,打完包后是一个jar包,外带你项目所用的jar包,个人推荐第二个
7,设置完后就可以点OK了
8,这个页面, Build on make打上勾,其他的不一样也没事
9,最后一步, Build Artifacts… --> XXX.jar --> Build
先将jar包放入linux的/opt/datas/目录
将mysql-connector-java-5.1.27-bin.jar放入/opt/cdh-5.7.6/spark-2.2.0-cdh5.7.6/jars/目录中
--master 设置模式:本地、集群、yarn
--class 包含main方法的driver类的地址
--driver-memory 设置driver的内存
--executor-memory 设置executor的内存
--executor-cores 设置每个executor的核心数
--total-executor-cores 设置executor的总核心数
又上两个参数可以算出executor的个数total-executor-cores/executor-cores
--num-executors 设置executor的个数(只有在yarn上运行才有这个参数)
bin/spark-submit
–master local[2]
–class com.fuyun.bigdate.spark.PvUvToMysql
–driver-memory 512M
–executor-memory 512M
–executor-cores 1
–total-executor-cores 2
/opt/datas/sparkLearning.jar
集群运行
bin/spark-submit
–master spark://192.168.xxx.xx:7077
–class com.fuyun.bigdate.spark.PvUvToMysql
–driver-memory 512M
–executor-memory 512M
–executor-cores 1
–total-executor-cores 2
/opt/datas/sparkLearning.jar
yarn上运行
bin/spark-submit
–master yarn
–class com.fuyun.bigdate.spark.PvUvToMysql
–driver-memory 512M
–executor-memory 512M
–num-executors 2
/opt/datas/sparkLearning.jar