学习路线指引(点击解锁) | 知识定位 | 人群定位 |
---|---|---|
Python实战微信订餐小程序 | 进阶级 | 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。 |
Python量化交易实战 | 入门级 | 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统 |
最近由于工作需要,要分析大几百G的Nginx日志数据。之前也有过类似的需求,但那个时候数据量不多。一次只有几百兆,或者几个G。因为数据都在Hive里面,当时的做法是:把数据从Hive导到MySQL,然后写代码查询MySQL并处理。如果你的处理逻辑比较简单,或只是查询统计,不会设计上游的服务调用,也可以直接写Hive SQL。
上面的做法在面对少量数据时还可以应付,对于大量数据就很不可取了。从Hive导数据到MySQL,光这一步就够呛,就更别说自己写的Java脚本效率性能如何了。请教同事过后,告诉我可以用Spark,并潇洒地丢给我一个Spark-Demo的jar包。之前只接触过HDFS和Hive,Spark只听说过,也准备学,但一直没时间。这下好了,有了带薪学习的机会。其实照着同事给我的jar包,照葫芦画瓢也能写出来,但是很多API都非常陌生,写出来的代码自己也不放心,所以还是有必要学学Spark的。
不过从头开始,完整学一遍Spark的话,时间肯定不够。当时接需求时,虽然知道自己不会,但是还挺相信自己的学习能力的,承诺了开发时间。所以我们的目标就是——用Spark处理Hive里面的数据,并把结果输出到MySQL中。
学习一个新知识的正常路径是:了解产生背景、了解整体架构、分模块学习功能和了解API、实战、深入学习原理和优化。由于这次目的性很强,在第三步时,只用学习跟本次需求相关的模块即可,然后就可以实战了。先从以下两个问题入手,初步了解Spark。
我们本次的目标就是用Spark处理大规模的数据集。
为什么选择Spark而不是MR?
Spark支持 Scala、Java、Python、SQL 和 R 等编程语言。其提供了大量模块化功能,可以适用于各种场景。其中包括 Spark SQL、Spark Structured Streaming、Spark MLlib,以及 GraphX 等模块。模块化带来的好处就是扩展性高,Spark 的重心在于快速的分布式计算引擎,而不是存储。和 Apache Hadoop 同时包含计算和存储不同,Spark 解耦了计算和存储。这意味着你可以用 Spark 读取存储在各种数据源(Apache Hadoop、Apache Cassandra、Apache HBase、MongoDB、Apache Hive、RDBMS 等)中的数据,并在内存中进行处理。你还可以扩展 Spark 的 DataFrameReader 和 DataFrameWriter,以便将其他数据源(如 Apache Kafka、Kinesis、Azure 存储、亚马逊 S3)的数据读取为DataFrame 的逻辑数据抽象,以进行操作。
Spark 提供了一种称作 RDD(resilient distributed dataset,弹性分布式数据集)的简单逻辑数据结构,它是 Spark 最基本的抽象。Spark 各种其他高级的结构化数据抽象(比如 DataFrame 和 Dataset)都是基于 RDD 构建的。
RDD 是 Spark 最基本的抽象。RDD 关联着三个至关重要的属性:
RDD的操作可以分为转化操作和行动操作。顾名思义,转化操作就是将 Spark DataFrame 转化为新的 DataFrame,而不改变原有数据的操作。比如select()、filter()这样的操作,不会改变原有数据,这些操作只会将转化结果作为新的 DataFrame 返回。一般转化操作后,会迎来一个行动操作。比如通过filter()过滤数据,最后通过count()统计过滤后的数据。这个count()就是行动操作。
上面提到了DataFrame,它是一个结构化、有格式的,且支持一些特定操作的数据集。就像分布式内存中的表一样,每列都有名字,有表结构定义,每列都有特定的数据类型。
引入Jar包,这里导入的版本不是很高,是因为公司的Spark集群也是2.3版本的,要跟你安装的Spark版本保持一致。
org.scala-langgroupId>
scala-libraryartifactId>
2.11.8version>
providedscope>
dependency>
org.apache.sparkgroupId>
spark-core_2.11artifactId>
2.3.2version>
providedscope>
dependency>
org.apache.sparkgroupId>
spark-hive_2.11artifactId>
2.3.2version>
providedscope>
dependency>
下面代码中有必要的注释,带序号的注释会在代码之后会展开说说。
public class SparkDemo {
//数据库相关配置
private static final Properties connectionProperties = new Properties();
private static final String HIVE\_DATABASE = "****";
private static final String HIVE\_TABLE\_NAME = "****";
private static final String JDBC\_URL = "****";
private static final String MYSQL\_TABLE\_NAME = "****";
static {
connectionProperties.put("user","*****");
connectionProperties.put("password","*****");
connectionProperties.put("driver","com.mysql.jdbc.Driver");
}
public static void main(String[] args) {
String dt = args[0];
//1.SparkSession是所有功能的入口,创建好后就可以用它的API来执行操作了
SparkSession sparkSession = SparkSession.builder()
.appName("SparkDemo")
.config("spark.driver.maxResultSize", "3g")
.enableHiveSupport()
.getOrCreate();
String sqlText = String.format("select host,url,uri,res\_data,dt from %s.%s where dt=%s", HIVE_DATABASE, HIVE_TABLE_NAME, dt);
//执行SQL并创建分区
Dataset sql = sparkSession.sql(sqlText).repartition(8);
//2.RDD转为JavaRDD
JavaRDD dataRows = sql.toJavaRDD();
//3.以分区的模式遍历数据集
JavaRDD scanResultJavaRDD = dataRows.mapPartitions((FlatMapFunction, Object>) rowIterator -> {
List list = new ArrayList<>();
Row row;
while (rowIterator.hasNext()) {
row = rowIterator.next();
String host = row.getString(0);
String url = row.getString(1);
String uri = row.getString(2);
String res\_data = row.getString(3);
//处理逻辑
}
return list.iterator();
});
writeToMySQL(sqlContext,scanResultJavaRDD);
sparkSession.stop();
}
//4.使用SQLContext提供的API读写数据库,不只是MySQL,支持JDBC就行
private static Dataset readMySQL(SQLContext sqlContext,String uri){
return sqlContext.read().jdbc(JDBC\_URL, MYSQL\_TABLE\_NAME, connectionProperties)
.select("*")
.where("uri=" + uri)
.limit(1000);
}
private static void writeToMySQL(SQLContext sqlContext,JavaRDD resultRDD){
sqlContext.createDataFrame(resultRDD,Object.class).write().mode(SaveMode.Append).jdbc(JDBC\_URL,MYSQL\_TABLE\_NAME,connectionProperties);
}
}
折叠
createDataFrame(resultRDD,Object.class)
会创建一个DataFrame,resultRDD是RDD,Object.class单个元素的数据结构。这里只是演示,实际可以是你自己定义的实体类。上面这个Demo只能演示一部分功能,反正它满足我的需求。有可能不太满足你的需求,可以去看看官方的文档:https://spark.apache.org/docs/3.3.0/sql-getting-started.html 。更多的读写数据方式和操作API基本都有。
参考资料:《Spark快速大数据分析 第二版》、官方文档