关于性能优化,任何系统都需要从硬件、软件、业务层面三个维度去考虑,衡量某个方案的提升与代价来确定是否合适,再决定从哪里入手。
这篇文章仅针对魔镜的使用者从业务层面优化自己的系统提供一些思路。
总体思路
- Spark不适合做并发查询,尽可能多的减少重复请求。
- 对于并发要求高、数据量小的表,尽可能计算完毕后,导出到mysql中进行直连分析。
- 对于大表,尽可能使用分区减少HDFS扫描的数据量。
场景一:分析台使用”实时数据”,在拖入字段的过程中产生的无用请求。
解决方案:根据情况,修改custom.js,禁用"实时数据”。
场景二:对于大表,拖入过程中,或者忘记拖入筛选条件导致全表扫描。
解决方案: 修改custom.js,开启 未拖入筛选器的请求,会增加二次确认弹出框(后续优化为仅针对分区表,并且没有分区筛选的请求弹出该提示)。
场景三:对于大表,虽然做了天分区,但是由于查询条件没有自动转换pt_day导致分区不生效请求。
解决方案:
- 表管理创建会显示pt_day和其他分区字段。
- 对于这个时间维度字段,“编辑表达式”或“数据映射”将字段映射到pt_day字段上。
场景四:对于大宽表,筛选器中获取所有“维度”(如客户名称)的请求,会导致全表扫描。
解决方案:
- 创建相应维表,如客户维度表。
- 使用维度->编辑表达式功能 将 “客户维表.客户名称”绑定到相应的维度上。
这样由于魔镜的星型模型支持的功能,会将不涉及事实表的查询优化走维度表进行查询。
场景五:对于大数据量的表,业务中经常通过某个条件查询明细,如订单明细表
解决方案:这种场景绝对不是spark擅长场景,理论上可以放到hbase进行处理,但是现阶段hbase还不能支持,所以建议将结果表存储到myql等关系数据库中,创建索引优化,然后使用直连功能用魔镜进行分析展示。
场景六:对于分组字段和计算字段中,产生大量的case when 字段 in()
解决建议:这种场景会导致sql看起来很长,而且in 的效率会比较低,对于大数据量,比较字符串的in 很耗性能。 建议从数据层面上扩展一个业务字段。筛选或者查询的时候使用扩展字段,避免长sql。
场景七:对于canal推过来的kafka增量表,消费线程导致部分性能下降问题。(可选)
解决方案:对于实时性要求不是很高,如几个小时,甚至每天一次的增量表,考虑使用根据update_time增量更新的模式进行同步数据,减轻魔镜的kafka消费线程压力。
场景八:由于对大表数据预览导致未走分区、全表扫描导致的大任务卡死。
解决方案**:和场景二类似,增加二次确认弹框,尽量在业务层通知大家先不要对大表做数据预览,需要看数据格式,拖需要的字段到分析台,并且增加分区条件进行预览。
场景九:sql虽然加了分区条件,但是从RM UI上看到任务的task数量太多,并且每个子任务处理的数据量都很小,导致查询变慢。
问题产生原因:该问题一般是由于hdfs上存在大量小文件,而对于spark来说,每个block都会是一个子任务,文件数量直接影响了spark的子任务数。该问题一般是由于插入脚本中的spark.sql.shuffle.partitions 参数控制,默认值200,所以每个分区可能都有200个block块。
排查思路:
- 查看RM UI上sql的执行情况,对于慢sql(数据量不大,超过2-3秒),查看task数量。
- 如果task数量明显对于扫描的分区数量,则点进去查看每个task处理的数据量,如果每个task处理的数据量在几M,甚至几K,可能明显有问题。
- 如果每个task处理数据量很小,则从hdfs->namenodeUI上检查hdfs对应目录的block数量和大小。
解决方案(该方案很重要,而且会增加整个hdfs性能,减轻namenode压力,同样,该方案会降低sql的执行效率,但是对于数据量不大的情况,影响不大): - 根据实际情况(一般是每个分区的数据量,并行度等)脚本中增加合适的spark.sql.shuffle.partitions
- sql中增加distribute by 分区字段,将一个分区中的小文件合并成一个大文件。
本文固定链接:杨晨辉的个人博客 » 魔镜性能优化思路(业务方向)(持续更新)
本站内容除特别标注外均为原创,欢迎转载,但请保留出处!