sparksql使用总结

前言

Spark是专为大规模数据处理而设计的快速通用的计算引擎,在计算能力上优于MapReduce,被誉为第二代大数据计算框架引擎。Spark采用的是内存计算方式。Spark的四大核心是Spark RDD(Sparkcore),SparkSQL,Spark Streaming,Spark ML。而SparkSQL在基于Hive数仓数据的分布式计算上尤为广泛。本编博客主要介绍基于Java API的SparkSQL的一些用法建议和利用Spark处理各种大数据计算的性能优化建议。

 

SparkSQL的一些用法及建议

实例化SparkSSql的SparkSession,Spark2.0之后都是利用SparkSession来进行SparkSQL,2.0以前是利用SparkSQLContext

SparkConf conf = new SparkConf().setAppName("sql-app");
setSparkConf(parameterParse, conf);
JavaSparkContext jsc = new JavaSparkContext(conf);

SparkSession.clearDefaultSession();
SparkSession session = SparkSession.builder().appName(parameterParse.getSpark_app_name())
.config("hive.metastore.uris", parameterParse.getHive_metastore_uris())
.config("spark.sql.warehouse.dir", parameterParse.getHive_metastore_warehouse_dir())
.enableHiveSupport().getOrCreate();

实例化之后直接执行Hive查询语句

session.sql("select name,age,job from employee").foreachPartition(iterator -> {
            while (iterator.hasNext()) {
                Row row = iterator.next();
                //逻辑处理
            }
});

注意:

a.这里的查询语句只要在hive命令能正确执行的都可以

b.这里的foreachPartition相当于对数据的一个遍历,iterator得到对数据rdd集遍历的一个迭代器,Row就是hive的每行数据,例如['Tom','25','软件工程师'],遍历之后可以对数据做任何的逻辑操作,也可以写到其他组件如hbase,mysql中。

建议:

a.假设将hive数据利用SparkSQL查询出来写入mysql中,对于小部分数据不建议在遍历rdd遍历,创建连接写入mysql,类似如下写法

session.sql("select min(age),max(age)from employee").foreachPartition(iterator -> {
                Connection conn = MysqlConnectionPool.getConnection(mysqlConnectBroadcast.getValue(), mysqlUserBroadcast.getValue(), mysqlPasswordBroadcast.getValue());
                String maxRecordPositionValue = "";
                while (iterator.hasNext()) {
                    Row row = iterator.next();
                    min = row.get(0).toString();
                    max = row.get(1).toString();
                }

                String sql = "update employee_age set max_age=? where min_age=?";
                PreparedStatement ps = conn.prepareStatement(sql);
                ps.setInt(1, Integer.parseInt(min));
                ps.setString(2, Integer.parseInt(max));
                ps.executeUpdate();
                MysqlConnectionPool.release(ps, conn);
});

这样写法会导致每个task创建mysql连接,如果在session.sql("select min(age),max(age)from employee").foreachPartition()外部也有写入mysql的逻辑,这样会造成mysql连接过多,从而导致在PreparedStatement,ResultSet错乱甚至会出现Sql异常。建议利用collectAsList()这个方法来实现,类似如下写法

List<Row> minAndMaxAgeList = session.sql("select min(age),max(age)from employee").collectAsList()
			min = minAndMaxAgeList.get(0).get(0).toString();
            max = minAndMaxAgeList.row.get(1).toString();

这里得到的值只有两个,对于session.sql()查大数据,如查询几亿条数据,首先第一种方法更加不合适,会造成不断的建立连接,关闭连接,同时写入太过频繁导致mysql崩溃。其次大数据量应该避免这样写到mysql,不管是第一种还是第二种方法。因为第二种方法可能导致GC崩溃。

b.如果实在要在foreachPartition里写,建议mysql的链接方式应该通过广播遍历传入

final Broadcast<String> mysqlConnectBroadcast = jsc.broadcast(parameterParse.getMysql_conn());
final Broadcast<String> mysqlUserBroadcast = jsc.broadcast(parameterParse.getMysql_user());
final Broadcast<String> mysqlPasswordBroadcast = jsc.broadcast(parameterParse.getMysql_password());

 

c.对于Row这个SparkSQL特殊的对应,很多新手会直接row.toString()得到一个数组字符串,再对数组字符串做数组切割,这种用法在逻辑上不存在问题,但是在大数据中,数据可能是多种多样的,这样写法会造成数据错乱问题,应该用如下写法

min = row.get(0).toString();
max = row.get(1).toString();

row本质上是一个集合对象,所以提供类似集合操作的方法。

 

Spark的性能优化】

关于这个网上很多配置参数的建议,让开发者看的很缭乱,以下是精简的,可以应用于常规开发中

a.参数级的优化

spark_driver_memory=4g
spark_num_executors=6
spark_executor_memory=4g
spark_executor_cores=1
spark_executor_memory_over_head=1024
spark_sql_shuffle_partitions=18
spark.default.parallelism=18

主要是这六个参数,这七个个参数的说明如下
spark_driver_memory设置driver的内存大小
spark_num_executors设置executors的个数
spark_executor_memory设置每个spark_executor_cores的内存大小
spark_executor_cores设置每个executor的cores数目
spark_executor_memory_over_head设置executor执行的时候,用的内存可能会超过executor-memoy,所以会为executor额外预留一部分内存。该参数代表了这部分内存
spark_sql_shuffle_partitions设置executor的partitions个数,注意这个参数只对SparkSQL有用
spark.default.parallelism设置executor的partitions个数,注意这个参数只对SparkRDD有用

对于这七个参数,需要充分理解Spark执行的逻辑才能明白并合适的配置,Spark的执行逻辑如下(这里不再细讲),可以参照这边博客https://www.cnblogs.com/cxxjohnson/p/8909578.html 或官方API其中关于内存的配置要结合hadoop yarn的集群的资源情况而定,不是越大越好。而对于spark_num_executors,spark_executor_cores,spark_sql_shuffle_partitions这三个参数,根据实际的经验需满足spark_sql_shuffle_partitions=spark_num_executorsspark_executor_cores3,而spark_executor_cores一般保持在1
再提交任务时:

spark-submit \
--master yarn \
--deploy-mode cluster \
--num-executors $spark_num_executors \
--driver-memory $spark_driver_memory \
--executor-memory $spark_executor_memory \
--executor-cores $spark_executor_cores \
--queue yarn_queue_test \
--conf spark.app.name=spark_name_test \
--conf spark.yarn.executor.memoryOverhead=$spark_executor_memory_over_head \
--conf spark.core.connection.ack.wait.timeout=300 \
--conf spark.dynamicAllocation.enabled=false \
--jars test.jar 

b.Task数据分布的优化

在一般情况下Task数据分配是随机默认的,这样会带来一个问题,如果多大的Task,而只是部分的Task数据处理量大,大部分很小,那么如果能做到将小部分的Task数据处理量优化到和大部分的大致相等,那么性能自然就提升上去了。这样优化分为两步:

a.在执行的Java代码中获取num_executors参数的值,上面的例子是spark_num_executors=6

b.不管是rdd的遍历还是直接的session.sql("sql").foreachPartition()在遍历之前加上一个方法repartition(partition)‘

session.sql(sqlStr).repartition(partition).foreachPartition(iterator -> {
            while (iterator.hasNext()) {
                Row row = iterator.next();
                //逻辑处理
                }

 

这样做后,在任务的管理页面看到的executor数据分布式非常均匀的,从而提高性能

c.分而治之

分而治之是贯穿整个大数据计算的核心,不管是MapReduce,Spark,Flink等等,而这里要说的分而治之可以初略的物理流程上的分而治之,而不是对Spark的driver,executor,Task分而治之,因为本身就是分布式的分而治之。假设经过反复的性能压力测试,得出Spark在现有规定资源上只有1000000条/s的性能,而现在的数据有一亿条。现在不做任何处理提交session.sql("sql").foreachPartition()或rdd.foreachPartition(),虽然最终会处理完,但发现时间是比预定的100000000/1000000s多得多,这样会拖累整体性能,这个时候是可以对现有的一亿条数据做以1000000条为组的组合切割分配成100000000/1000000个集合,对集合数据依次执行,这样性能上会有所提升。当然这种优化方式还是需要跟实际业务逻辑来定

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spark SQL的orderBy操作用于按照指定的列对数据进行排序。orderBy默认是升序排序,如果需要降序排序,则需要使用desc()函数。 示例代码: ```python from pyspark.sql.functions import desc # 读取数据 df = spark.read.format("csv").load("path/to/file.csv", header=True) # 按照列名进行升序排序 df.orderBy("column_name").show() # 按照列名进行降序排序 df.orderBy(desc("column_name")).show() # 按照多个列进行排序,先按照列1排序,再按照列2排序 df.orderBy("column1", "column2").show() ``` 注意:orderBy操作是一个宽依赖操作,会导致数据的全局重排,因此在处理大规模数据时需要注意性能问题。 ### 回答2: SparkSQL中的orderBy函数用于对结果进行排序。orderBy函数接收一个或多个列作为参数,并且可以指定排序的方式,如升序或降序。 使用orderBy函数的一般格式如下: ``` df.orderBy(col("column1").asc(), col("column2").desc()) ``` 在上面的示例中,orderBy函数按照column1列进行升序排序,并按照column2列进行降序排序。 orderBy函数可以直接对列进行排序,也可以对列的别名进行排序。例如,可以对一个计算列进行排序,如下所示: ``` df.withColumn("calculated", col("column1") + col("column2")).orderBy(col("calculated").asc()) ``` 在这个示例中,首先创建了一个名为calculated的计算列,该列为column1和column2列之和,并且将结果按照calculated列进行升序排序。 orderBy函数还可以接收多个列的排序条件,并且支持链式调用。例如: ``` df.orderBy(col("column1").asc()).orderBy(col("column2").desc()) ``` 在这个示例中,首先按照column1列进行升序排序,然后再按照column2列进行降序排序。 需要注意的是,orderBy函数返回一个新的DataFrame,原始的DataFrame不会发生改变。因此,如果需要对排序结果进行后续操作,需要将排序后的结果保存到一个变量中。 在使用orderBy函数时,还可以进行一些其他的操作,如对列进行表达式计算、使用函数等,以满足不同的需求。 总结来说,orderBy函数用于对SparkSQL中的DataFrame进行排序,可以按照一个或多个列进行排序,支持升序或降序排序,并且可以进行一些其他操作,如计算列、使用函数等。 ### 回答3: Spark SQL中的orderBy是用于对结果进行排序的函数。orderBy可以根据给定的列对结果进行升序或降序排序。 orderBy函数的使用方法如下: 1. 单个列排序: orderBy("column"):按照指定的列对结果进行升序排序,默认为升序。 orderBy($"column".desc):按照指定的列对结果进行降序排序。 2. 多个列排序: orderBy("column1", "column2"):首先按照column1排序,如果有相同的值,则再按照column2排序。 orderBy($"column1".desc, $"column2"):首先按照column1进行降序排序,如果有相同的值,则按照column2排序。 例如,假设有一个包含姓名和年龄的表,我们可以使用orderBy函数对结果按照姓名进行升序排序,或者按照年龄进行降序排序。 示例代码如下: 1. 单个列排序: df.orderBy("name"):按照姓名升序排序。 df.orderBy($"name".desc):按照姓名降序排序。 2. 多个列排序: df.orderBy("name", "age"):首先按照姓名排序,如果有相同的姓名,则按照年龄排序。 df.orderBy($"name".desc, $"age"):首先按照姓名降序排序,如果有相同姓名,则按照年龄排序。 总结: orderBy函数用于对Spark SQL查询结果进行排序,可以按照单个列或多个列进行排序,并且可以指定升序或降序排序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值