Spark SQL支持读写存储在Apache Hive中的数据。在Spark 2.2.1中,不需要显式地创建SparkContext、SQLContext、HiveContext实例,其已经封装在SparkSession中。 Spark 2.2.1中的 SparkSession对于 Hive 的各个特性提供了内置支持,包括使用 HiveQL 编写查询语句,使用 Hive UDFs 以及从 Hive 表中读取数据。
Spark SQL 支持从 Hive 中读取数据以及保存数据到Hive中。第三方框架Hive 有大量依赖包,默认部署的 Spark 不包含这些依赖包,需将Hive 的依赖包添加到Spark的 Classpath,Spark 将自动加载这些依赖包。注意,Hive的依赖包也必须分发到各个节点,因为需要通过Hive序列化和反序列化库来读取Hive数据和将数据写入 Hive。
Spark 2.2.1使用HiveQL语句的两种情况:
1) 已经部署Hive环境,Spark和 Hive协作。Spark需要实例化支持Hive 的
SparkSession。将配置文件hive-site.xml, core-site.xml (如果有安全相关配置) 以及 hdfs-site.xml拷贝到 $SPARK_HOME/conf 目录下。
2) 如果没有现成部署好的Hive,Spark仍可启用 Hive 支持。当没有使用
hive-site.xml 进行配置时,Spark会自动的在当前目录创建 metastore_db 并在 spark.sql.warehouse.dir 指定的目录创建一个目录,用作 spark-warehouse 目录。注意:hive-site.xml 中的hive.metastore.warehouse.dir从Spark 2.0.0 取而代之,使用 spark.sql.warehouse.dir 来指定 warehouse 数据库的默认目录。另外,需要赋予启动Application用户对这个目录的写权限。
下面给出两种情况下使用HiveQL语句的示例。
(一)不使用现有的Hive环境
1) 如果没有现成部署好的Hive环境,Spark 2.2.1默认也可启用HiveQL支持。
默认在当前用户/root下建立元数据数据库metastore_db及数据仓库存储目录spark-warehouse。
root@master:~/metastore_db/log#cd /root/
root@master:~# ls/root |grep metastore_db
metastore_db
root@master:~# ls/root |grep spark
spark-warehouse
SparkSession使用spark.catalog实例访问Spark系统的Catalog元数据,例如:查询Spark当前所有的数据库。默认数据库是default,描述为默认的Hive数据库(Default Hive database),本地数据库文件地址是(file:/root/spark-warehouse)。
scala>spark.catalog.listDatabases.show(false)
+-------+---------------------+--------------------------+
|name |description |locationUri |
+-------+---------------------+--------------------------+
|default|DefaultHive database|file:/root/spark-warehouse|
+-------+---------------------+--------------------------+
查询Spark当前所有数据库中所有的表。
scala>spark.catalog.listTables.show(false)
18/02/18 20:21:56INFO parser.CatalystSqlParser: Parsing command: array
18/02/18 20:21:56INFO parser.CatalystSqlParser: Parsing command: bigint
18/02/18 20:21:56INFO parser.CatalystSqlParser: Parsing command: string
+-------------+--------+-----------+---------+-----------+
|name |database|description|tableType|isTemporary|
+-------------+--------+-----------+---------+-----------+
|people_table|default |null |MANAGED |false |
|people_table2|default|null |MANAGED |false |
|parquetfile |null |null |TEMPORARY|true |
|people |null |null |TEMPORARY|true |
+-------------+--------+-----------+---------+-----------+
2) 构建数据库表(表的名称为src)。
通过spark.sql("CREATE TABLE IF NOT EXISTS src (keyINT, value STRING)")的HiveQL的建表语句,构建了一个名为src的数据库表,包含key和value两列。
scala>spark.sql("CREATE TABLE IF NOT EXISTS src (key INT, value STRING)")
18/02/18 20:37:49INFO spark.ContextCleaner: Cleaned accumulator 736
18/02/18 20:45:02INFO execution.SparkSqlParser: Parsing command: CREATE TABLE IF NOT EXISTS src(key INT, value STRING)
18/02/18 20:45:07WARN metastore.HiveMetaStore: Location: file:/root/spark-warehouse/srcspecified for non-external table:src
res56:org.apache.spark.sql.DataFrame = []
Spark的默认数据库是default,默认的Hive数据库的本地数据库文件目录是(file:/root/spark-warehouse),查询src表已经在spark-warehouse目录下构建成功。
root@master:~/spark-warehouse#ls -ltr
total 12
drwxr-xr-x 2 rootroot 4096 Feb 17 20:13 people_table
drwxr-xr-x 2 rootroot 4096 Feb 17 20:34 people_table2
drwxr-xr-x 2 rootroot 4096 Feb 18 20:45 src
3) 加载本地文件到表(src)中。
通过HiveQL的Load语句:"LOAD DATA LOCAL INPATH ' /usr/local/spark-2.2.1-bin-hadoop2.6/examples/src/main/resources/kv1.txt' INTO TABLE src",将本地文件系统中的kv1.txt文件加载到src表中。该文件位于Spark部署目录下。
kv1.txt文件的格式(Key-Value)包括2列,第一列Key是随机数字,第二列Value是将一列的数字加上“val_”前缀。kv1.txt文件的部分记录如下。
238 val_238
86 val_86
311 val_311
27 val_27
165 val_165
409 val_409
……
使用HiveQL的Load语句加载kv1.txt文件。
scala>spark.sql("LOAD DATA LOCAL INPATH '/usr/local/spark-2.2.1-bin-
hadoop2.6/examples/src/main/resources/kv1.txt'INTO TABLE src")
18/02/18 21:22:02INFO execution.SparkSqlParser: Parsing command: LOAD DATA LOCAL INPATH'/usr/local/spark-2.2.1-bin-hadoop2.6/examples/src/main/resources/kv1.txt' INTOTABLE src
18/02/18 21:22:02INFO parser.CatalystSqlParser: Parsing command: int
18/02/18 21:22:02INFO parser.CatalystSqlParser: Parsing command: string
res61:org.apache.spark.sql.DataFrame = []
4) 查询src表。
scala>spark.sql("SELECT key, value FROM src ")
18/02/18 21:24:20INFO execution.SparkSqlParser: Parsing command: SELECT key, value FROM src
18/02/18 21:24:20INFO parser.CatalystSqlParser: Parsing command: int
18/02/18 21:24:20INFO parser.CatalystSqlParser: Parsing command: string
res62:org.apache.spark.sql.DataFrame = [key: int, value: string]
通过HiveQL的查询语句:" SELECT key, value FROMsrc ",从src表中查询指定的key和value两列数据来构建出DataFrame实例。
5) 在界面输出src表的查询结果。
scala>spark.sql("SELECT key, value FROM src ").collect().foreach(println)
18/02/18 21:26:11INFO execution.SparkSqlParser: Parsing command: SELECT key, value FROM src
18/02/18 21:26:11INFO parser.CatalystSqlParser: Parsing command: int
18/02/18 21:26:11INFO parser.CatalystSqlParser: Parsing command: string
18/02/18 21:26:12INFO codegen.CodeGenerator: Code generated in 16.122687 ms
…….
18/02/18 21:26:14INFO rdd.HadoopRDD: Input split: file:/root/spark-warehouse/src/kv1.txt:0+5812
……
[238,val_238]
[86,val_86]
[311,val_311]
[27,val_27]
[165,val_165]
[409,val_409]
[255,val_255]
……
6) 删除src表。
scala>spark.sql("DROP table src")
18/02/18 21:32:36INFO execution.SparkSqlParser: Parsing command: DROP table src
18/02/18 21:32:36INFO parser.CatalystSqlParser: Parsing command: int
18/02/18 21:32:36INFO parser.CatalystSqlParser: Parsing command: string
18/02/18 21:32:36INFO parser.CatalystSqlParser: Parsing command: int
18/02/18 21:32:36INFO parser.CatalystSqlParser: Parsing command: string
18/02/18 21:32:37INFO spark.ContextCleaner: Cleaned accumulator 877
18/02/18 21:32:37INFO storage.BlockManagerInfo: Removed broadcast_42_piece0 on master:43661 inmemory (size: 4.3 KB, free: 413.9 MB)
18/02/18 21:32:37INFO storage.BlockManagerInfo: Removed broadcast_41_piece0 on master:43661 inmemory (size: 21.7 KB, free: 413.9 MB)
res64:org.apache.spark.sql.DataFrame = []
查询Spark的默认Hive数据库的本地数据库文件目录(file:/root/spark-warehouse),可以看到src表已经被删除。
root@master:~/spark-warehouse#ls -ltr
total 8
drwxr-xr-x 2 rootroot 4096 Feb 17 20:13 people_table
drwxr-xr-x 2 rootroot 4096 Feb 17 20:34 people_table2
oot@master:~/spark-warehouse#
(二) 使用现有的Hive数据仓库。Spark 2.2.1基于Hive进行操作。
1) 前提准备:已经安装好Mysql数据库。Mysql数据库版本检查如下。
root@master:/usr/local/apache-hive-1.2.1/conf#mysql -V
mysql Ver 14.14 Distrib 5.5.47, fordebian-linux-gnu (x86_64) using readline 6.3
root@master:~#mysql -u root -p
Enter password:
Welcome to theMySQL monitor. Commands end with ; or\g.
Your MySQL connectionid is 40
Server version:5.5.47-0ubuntu0.14.04.1 (Ubuntu)
……
Mysql驱动的下载地址(https://dev.mysql.com/downloads/connector/j/)。
2) 前提准备:已经安装好Hive环境,当前Hive环境使用Mysql作为默认的元数据数据库。
root@master:/usr/local/hadoop-2.6.0/share/hadoop/yarn/lib#ls | grep jline-2.12.jar
jline-2.12.jar
3) 在Linux系统/etc/profile文件中配置Hadoop、Hive环境变量,或者在hive-env.sh配置文件中配置。
root@master:~#cat /etc/profile
……
exportHADOOP_HOME=/usr/local/hadoop-2.6.0
exportHIVE_HOME=/usr/local/apache-hive-1.2.1
exportPATH=.:$PATH:$JAVA_HOME/bin:$SCALA_HOME/bin:$HADOOP_HOME/bin:$SPARK_HOME/bin:$HIVE_HOME/bin
……
4) Hive的hive-site.xml配置示例。
javax.jdo.option.ConnectionURL
jdbc:mysql://localhost:3306/hive?createDatabaseIfNotExist=true
JDBC connect string for a JDBCmetastore
javax.jdo.option.ConnectionDriverName
com.mysql.jdbc.Driver
Driver class name for a JDBCmetastore
javax.jdo.option.ConnectionUserName
root
username to use against metastoredatabase
javax.jdo.option.ConnectionPassword
root
password to use against metastoredatabase
hive.metastore.schema.verification
false
hive.metastore.warehouse.dir
/user/hive/warehouse
其中,javax.jdo.option.ConnectionURL为Mysql数据库连接的URL信息,这里连接到localhost(192.168.189.1)的数据库Hive (已在Mysql中里面建立数据库,其名称设置为Hive),如果在Mysql中的Hive数据库不存在,将自动新建一个数据库(名称设置为Hive)。javax.jdo.option.ConnectionUserName、javax.jdo.option.ConnectionPassword分别设置用户名、密码信息。hive.metastore.warehouse.dir是Hive数据在HDFS中的存储位置。
使用Mysql作为Hive MetaStore元数据的存储数据库,Mysql中和Hive的相关表,如表3-2所示。
表名 |
说明 |
关联键 |
TBLS |
所有hive表的基本信息(表名,创建时间,所属者等) |
TBL_ID,SD_ID |
TABLE_PARAMS |
表级属性,(如是否外部表,表注释,最后修改时间等) |
TBL_ID |
COLUMNS_V2 |
Hive表字段信息(字段注释,字段名,字段类型,字段序号) |
SD_ID |
SDS |
所有hive表、表分区所对应的HDFS数据目录和数据格式 |
SD_ID,SERDE_ID |
SERDE_PARAMS |
序列化反序列化信息,如行分隔符、列分隔符、NULL的表示字符等 |
SERDE_ID |
PARTITIONS |
Hive表分区信息(所属表,分区值) |
PART_ID,SD_ID,TBL_ID |
PARTITION_KEYS |
Hive分区表分区键(即分区字段) |
TBL_ID |
PARTITION_KEY_VALS |
Hive表分区名(键值) |
PART_ID |
表 3 - 2 Mysql作为Hive MetaStore元数据的相关表
本节案例是实践Spark SQL,因此对Hive的环境配置只给出最基本的配置。如果需要修改,可以参考Hive官网的部署(http://hive.apache.org/)。例如:
5) 在Hive中操作新建表。
查询Hive中已有的数据库。
hive> showdatabases;
OK
default
hive
hivestudy
Time taken: 2.305seconds, Fetched: 3 row(s)
Hive中已经建立了hive、hivestudy数据库及Hive的默认数据库default,这里使用hive数据库。
hive> use hive;
OK
Time taken: 0.12seconds
在Hive的数据库(hive)中新建pokes表。
hive> showtables;
OK
Time taken: 0.03seconds
hive> CREATE TABLE pokes (foo INT, bar STRING);
OK
Time taken: 1.481seconds
hive> showtables;
OK
pokes
Time taken: 0.046seconds, Fetched: 1 row(s)
hive>
查看Web Interface界面上HDFS的文件系统信息,如图3-6所示。
图 3 - 6Hadoop文件系统在Hive的CLI中构建表之后的界面
在HDFS系统/user/hive/warehouse/hive.db的 hive数据库目录下,生成对应表名的目录pokes。
在Mysql中查询Hive数据库新建立的pokes表的元数据信息。例如,Mysql数据库中TBLS表记录中包括Hive中表的基本信息(表名,创建时间,所属者等),从Mysql的TBLS表中查询新建pokes的所属者是root,表名是pokes。
mysql> select CREATE_TIME,OWNER,TBL_NAME from TBLS where TBL_NAME
="pokes";
+-------------+-------+----------+
| CREATE_TIME |OWNER | TBL_NAME |
+-------------+-------+----------+
| 1519009476 | root | pokes |
6) 在Spark 2.2.1中操作,查询Hive数据库的表的信息。
前提准备:将Hive的配置文件hive-site.xml拷贝到 Spark2.2.1的$SPARK_HOME/conf 目录下。
root@master:/usr/local/apache-hive-1.2.1/conf#cp /usr/local/apache-hive-1.2.1/conf/hive-site.xml
/usr/local/spark-2.2.1-bin-hadoop2.6/conf/
……
root@master:/usr/local/spark-2.2.1-bin-hadoop2.6/conf#ls |grep hive-site.xml
hive-site.xml
在执行Spark Shell或者Spark Submit命令的时候,需在--driver-class-path配置mysql-connector-java-5.1.35-bin.jar的路径。示例如下。
--driver-class-path/usr/local/apache-hive-1.2.1/lib/mysql-connector-java-5.1.13-bin.jar
注意:如果启动Spark-Shell脚本时,没有配置--driver-class-path的mysql-connector-java-5.1.35-bin.jar包的路径,在加载类时会找不到类“com.mysql.jdbc.Driver”的路径,导致Mysql的数据库连接失败,出现以下异常提示:
scala>spark.sql("show tables").show
…….
Caused by:org.datanucleus.exceptions.NucleusException: Attempt to invoke the"BONECP" plugin
to create aConnectionPool gave an error : The specified datastore driver("com.mysql.jdbc.Driver") was not found in the CLASSPATH. Pleasecheck your CLASSPATH specification, and the name of the driver.
at org.datanucleus.store.rdbms.ConnectionFactoryImpl.generateDataSources(ConnectionFactoryImpl.java:259)
……..
小技巧:
l 当日志出现Error时,跟踪其调用堆栈信息,查找关键字为Caused by,可能中一个异常堆栈信息中会出现多个Caused by,这时候最下面那个,也就是最早抛出异常的地方,往往就是错误的根源,问题的根源才是解决问题的出发点。
l 类找不到的根本原因,主要是由JVM的类加载机制导致的。如果当前执行环境(需要注意在分布式计算框架下,是对应真正执行的节点,与提交点等其他节点的环境无关)下的CLASSPATH中找不到该jar包,就会出现类找不到的异常。
l Spark计算框架已经提供了自动将所需Jar包添加到执行环境的CLASSPATH上了,如果执行环境本身没有部署该Jar包,就可以充分利用spark的--jars参数,自动设置。
配置--driver-class-path参数,运行Spark Shell应用程序。
root@master:~#spark-shell --masterspark://192.168.189.1:7077 \
> --driver-class-path/usr/local/apache-hive-1.2.1/lib/mysql-connector-java-5.1.13-bin.jar \
> --executor-memory 512m --total-executor-cores 4
Setting default loglevel to "WARN".
To adjust logginglevel use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).
18/02/19 13:43:13WARN util.NativeCodeLoader: Unable to load native-hadoop library for yourplatform... using builtin-java classes where applicable
Spark context WebUI available at http://master:4040
Spark contextavailable as 'sc' (master = spark://192.168.189.1:7077, app id =app-20180219134317-0000).
Spark sessionavailable as 'spark'.
Welcome to
____ __
/ __/__ ___ _____/ /__
_\ \/ _ \/ _ `/ __/ '_/
/___/ .__/\_,_/_/ /_/\_\ version 2.2.1
在Spark Shell交互式界面中,通过spark.sql("usehive")语句使用Hive中的hive数据库,查询hive数据库现有的表信息,其中可以查看到hive刚创建的pokes表。
scala> spark.sql("use hive")
18/02/19 15:32:12 WARN metastore.ObjectStore: Failed toget database global_temp, returning NoSuchObjectException
res0: org.apache.spark.sql.DataFrame = []
scala> spark.sql("show tables").show
+--------+---------+-----------+
|database|tableName|isTemporary|
+--------+---------+-----------+
| hive| pokes| false|
+--------+---------+-----------+
3) 在Spark 2.2.1中操作,在Spark SQL中创建表pokes_test。
scala>spark.sql("CREATE TABLE pokes_test (foo INT, bar STRING)")
18/02/19 15:43:41WARN metastore.HiveMetaStore: Location:hdfs://master:9000/user/hive/warehouse/hive.db/pokes_test specified fornon-external table:pokes_test
res2:org.apache.spark.sql.DataFrame = []
登陆Hive,在Hive中查看Spark 2.2.1 SQL新建的表pokes_test。查看pokes_test表已经在Hive中建立。
hive> use hive;
OK
Time taken: 0.03seconds
hive> showtables;
OK
pokes
pokes_test
Time taken: 0.06seconds, Fetched: 2 row(s)
hive>
查看Web Interface界面上HDFS的文件系统信息,如图3-7所示。
图 3 - 7 pokes_test表在HDFS的文件系统信息
登陆Mysql数据库,查询Hive数据库元数据的信息。在Spark 2.2.1 SQL新建的表pokes_test,Hive已经将pokes_test的元数据同步到Mysql数据库,例如:在Mysql数据库hive的TBLS表中查询pokes_test表的建立时间、所属者、及表名。
mysql> use hive;
Reading table informationfor completion of table and column names
You can turn offthis feature to get a quicker startup with -A
mysql> selectCREATE_TIME,OWNER, TBL_NAME from TBLSwhere
TBL_NAME='pokes_test';
+-------------+-------+------------+
| CREATE_TIME |OWNER | TBL_NAME |
+-------------+-------+------------+
| 1519026222 | root | pokes_test |
+-------------+-------+------------+
1 row in set (0.00sec)
4) 在Spark Shell中查询Hive中表名信息。
scala>spark.sqlContext.tableNames
res7: Array[String]= Array(pokes, pokes_test)
(二) UDF扩展用户定义函数场景下的实战案例。
本节给出UDFs注册实例,基于UDF的定义,给出DataFrame和SQL中的两种案例。
1) 加载数据源。
数据源使用Spark 2.2.1本身自带的本地数据源文件examples/src/main/resources/people.txt,people.txt数据源文件包括姓名、年龄两列,内容如下:
root@master:/usr/local/spark-2.2.1-bin-hadoop2.6/examples/src/main/resources#cat people.txt
Michael, 29
Andy, 30
Justin, 19
将文件examples/src/main/resources/people.txt上传到HDFS文件系统中/resources。
root@master:~# hdfs dfs -put /usr/local/spark-2.2.1-bin-
hadoop2.6/examples/src/main/resources/people.txt /resources
root@master:~# hdfsdfs -ls /resources |grep people.txt
-rw-r--r-- 3 rootsupergroup 32 2018-02-19 20:52/resources/people.txt
定义caseclass People类,包括姓名及年龄成员变量。
scala> caseclass People(name: String, age: Int)
defined classPeople
加载HDFS系统中的people.txt数据源文件,每行数据按“,”切分以后,提取第0个元素姓名,第1个元素年龄放入People类中,并转换为DataFrame。然后注册为临时表people。
scala> val people
=spark.sparkContext.textFile("/resources/people.txt").map(_.split(",")).map(attributes=> People(attributes(0), attributes(1).trim.toInt)).toDF()
people: org.apache.spark.sql.DataFrame = [name: string, age: int]
scala> people.createOrReplaceTempView("people")
2) 在Spark SQL构建求字符串长度的UDF,注册名为strLenScala,其使用占位符“_”将每个读入的字符串元素计算长度。
scala>spark.udf.register("strLenScala", (_: String).length)
res8: org.apache.spark.sql.expressions.UserDefinedFunction =UserDefinedFunction(,IntegerType,Some(List(StringType)))
3) 在Spark SQL构建对列进行格式化的UDF,注册名为fmtNum。导入DecimalFormat类,将每个读入的整型元素进行格式化。
scala> importjava.text.DecimalFormat
importjava.text.DecimalFormat
scala> spark.udf.register("fmtNum", (d: Int) => {
| val dfmt = newDecimalFormat("###.00")
| dfmt.format(d.toDouble)})
res11: org.apache.spark.sql.expressions.UserDefinedFunction =UserDefinedFunction(,StringType,Some(List(IntegerType)))
注册的strLenScala的函数功能为将传入的字符串转换为字符串的长度,fmtNum对Int的age进行格式化。
4) 在Spark 2.2.1 SQL中的使用UDF案例如下。在SQL语句的WHERE条件表达式中调用strLenScala的UDF函数,计算姓名列的长度。查询姓名长度小于等于13,以及年龄小于32岁的人员信息。
scala> val udfRslt = spark.sql("SELECT name, age FROM peopleWHERE strLenScala(name) <=
13 AND age <= 32").show
+-------+---+
| name|age|
+-------+---+
|Michael| 29|
| Andy| 30|
| Justin| 19|
+-------+---+
udfRslt: Unit = ()
5) 在Spark 2.2.1 DataFrame中的UDF使用案例如下。使用selectExpr调用strLenScala的UDF函数显示姓名长度、年龄的人员信息。
scala>people.selectExpr("strLenScala(name)","age").toDF("nameLen","age").show
+-------+---+
|nameLen|age|
+-------+---+
| 7| 29|
| 4| 30|
| 6| 19|
+-------+---+
scala>people.selectExpr("strLenScala(name) as nameLen","age").show
+-------+---+
|nameLen|age|
+-------+---+
| 7| 29|
| 4| 30|
| 6| 19|
+-------+---+
使用注册的UDF函数strLenScala,返回的DataFrame的Schemma会自动进行推导。
scala>people.selectExpr("strLenScala(name) as nameLen","age").printSchema
root
|-- nameLen: integer (nullable = true)
|-- age: integer (nullable = false)
使用selectExpr分别调用strLenScala、fmtNum的UDF函数查询姓名长度、年龄格式化的人员信息。
scala>people.selectExpr("strLenScala(name) as nameLen","fmtNum(age)").show
+-------+---------------+
|nameLen|UDF:fmtNum(age)|
+-------+---------------+
| 7| 29.00|
| 4| 30.00|
| 6| 19.00|
+-------+---------------+
使用注册的UDF的fmtNum后,返回的DataFrame的Schemma也会自动进行推导,Age字段自动推导为String类型。
scala>people.selectExpr("strLenScala(name) as nameLen","fmtNum(age)").printSchema
root
|-- nameLen: integer (nullable = true)
|-- UDF:fmtNum(age): string (nullable = true)
2018年新春报喜!热烈祝贺王家林大咖大数据经典传奇著作《SPARK大数据商业实战三部曲》畅销书籍 清华大学出版社发行上市!
本书基于Spark 2.2.0最新版本(2017年7月11日发布),以Spark商业案例实战和Spark在生产环境下几乎所有类型的性能调优为核心,以Spark内核解密为基石,分为上篇、中篇、下篇,对企业生产环境下的Spark商业案例与性能调优抽丝剥茧地进行剖析。上篇基于Spark源码,从一个动手实战案例入手,循序渐进地全面解析了Spark 2.2新特性及Spark内核源码;中篇选取Spark开发中最具有代表的经典学习案例,深入浅出地介绍,在案例中综合应用Spark的大数据技术;下篇性能调优内容基本完全覆盖了Spark在生产环境下的所有调优技术。
本书适合所有Spark学习者和从业人员使用。对于有分布式计算框架应用经验的人员,本书也可以作为Spark高手修炼的参考书籍。同时,本书也特别适合作为高等院校的大数据教材使用。
当当网、京东、淘宝、亚马逊等网店已可购买!欢迎大家购买学习!