思路:SparkSql转化为MR任务,充分利用硬件资源
利用好分布式系统的并行计算
1.提交查询任务的并行度
- 集群的处理能力
SPARK_WORKER_INSTANCES参数、SPARK_WORKER_CORES参数决定
- total_executor_cores集群的有效处理能力
SparkSql的查询并行度主要和集群的core数量有关,合理配置每个节点的core可以提高集群的并行度,提高查询效率。
- Spark任务默认为200个task
task过多,会产生很多的任务启动开销,task多少,每个task处理的时间过长,容易straggle.
2.高效的数据格式
一方面是为了加快数据的读入速度,另一方面可以减少内存的消耗
- 数据本地性
合理设置数据副本、合理设置数据缓存级别:PROCESS_LOCAL>NODE_LOCAL>ANY
- 合适的数据类型
使用合适的数据类型可以节省有限的内存资源,能定义为tinyint,就不定义成int。tinyint占1个字节,int占4个字节。
- sql书写优化
在写sql时尽量写出要查询的列名。
根据自己的业务需求,测试并选择合适的数据存储格式及压缩算法,将有利于SparkSql的查询效率
3.内存的使用
参数设置:
SPARK_WORKER_MEMORY 调节worker节点的内存,SPARK_WORKER_INSTANCES
spark.storage.memoryFraction Spark应用程序所申请的内存资源可用于cache的比例。
spark.shuffle.memoryFraction Spark应用程序锁申请的内存资源可用于cache的比例
注意点:
对于频繁使用的表或者查询才进行缓存,对于只使用一次的表才不需要缓存。
对join操作优先缓存较小的表。
要多注意对stage的监控,多思考如何才能使用更多的task使用PROCESS_LOCAL;
要多注意storage的监控,思考如何才能Fractioin cached的比例更多
- 其他
想要获取更好的表达式查询速度,可以将spark.sql.codegen设置为true,spark.sql.codegen 默认值为false,当它设置为true时,Spark SQL会把每条查询的语句在运行时编译为java的二进制代码。这有什么作用呢?它可以提高大型查询的性能,但是如果进行小规模的查询的时候反而会变慢,就是说直接用查询反而比将它编译成为java的二进制代码快。所以在优化这个选项的时候要视情况而定。
对于大数据集的计算结果,不要使用collect(),collect()就是将结果返回给Driver,很容易撑爆Driver的内存。一般将这种结果直接存储到分布式文件系统中了。
对于数据倾斜,采用加入中间步骤,如聚合后cache,具体情况具体分析;
适当地使用序列化方案以及压缩方案;
善于利用集群监控系统,将集群的运行状况维持在一个合理、稳定的状态;
善于解决重点矛盾,多观察stage中的task,查看最耗时的task,查找原因并改善。
4.主要的参数调优
并行度:spark.sql.shuffle.partitions,一个partitions相当于一个task。这是配置当shuffle数据去join或者聚合的时候的partitions的数量。200一般情况下在生产上是不够的,需要做相应的调整。
spark.sql.inMemoryColumnStorage.compressed 默认值为false 它的作用是自动对内存中的列式存储进行压缩
spark.sql.inMemoryColumnStorage.batchSize 默认值为1000 这个参数代表的是列式缓存时的每个批处理的大小。如果将这个值调大可能会导致内存不够的异常,所以在设置这个的参数的时候得注意你的内存大小。
spark.sql.parquet.compressed.codec 默认值为snappy 这个参数代表使用哪种压缩编码器。可选的选项包括uncompressed/snappy/gzip/lzo
uncompressed这个顾名思义就是不用压缩的意思
5.推测执行
推测执行(Speculative Execution)是指在分布式集群环境下,因为程序BUG,负载不均衡或者资源分布不均等原因,造成同一个job的多个task运行速度不一致,有的task运行速度明显慢于其他task(比如:一个job的某个task进度只有10%,而其他所有task已经运行完毕),则这些task拖慢了作业的整体执行进度,为了避免这种情况发生,Hadoop会为该task启动备份任务,让该speculative task与原始task同时处理一份数据,哪个先运行完,则将谁的结果作为最终结果。
推测执行优化机制采用了典型的以空间换时间的优化策略,它同时启动多个相同task(备份任务)处理相同的数据块,哪个完成的早,则采用哪个task的结果,这样可防止拖后腿Task任务出现,进而提高作业计算速度,但是,这样却会占用更多的资源,在集群资源紧缺的情况下,设计合理的推测执行机制可在多用少量资源情况下,减少大作业的计算时间。
检查逻辑代码中注释很明白,当成功的Task数超过总Task数的75%(可通过参数spark.speculation.quantile设置)时,再统计所有成功的Tasks的运行时间,得到一个中位数,用这个中位数乘以1.5(可通过参数spark.speculation.multiplier控制)得到运行时间门限,如果在运行的Tasks的运行时间超过这个门限,则对它启用推测。简单来说就是对那些拖慢整体进度的Tasks启用推测,以加速整个Stage的运行。