zoukankan      html  css  js  c++  java
  • Presto常见问题优化

    转载:http://www.voidcn.com/article/p-kmaltben-bse.html

    presto参数优化

    查询速度慢, 如何优化?

    解决方法1: 避免单节点处理

    虽然Presto是分布式查询引擎, 但是一些操作是必须在单节点中处理的. 例如:

    • count(distinct x)

      • 考虑使用approx_distinct(x)代替
      • 但是需要注意这个函数有个大约在2.3%的标准误差, 如果需要精确统计的情况, 请绕道.
    • UNION

      UNION有个功能是: 如果两条记录一样, 会只保留一条记录(去重).
      • 如果不考虑去重的情况, 请使用UNION ALL
    • ORDER BY

      Presto对数据排序是作用在单节点上的
      • 如果要排序的数据量超过百万行, 要谨慎考虑. 如果非要排序,尽量将排序的字段减少些.

    解决方法2: 减少表扫描的范围

    通过添加条件达到减少表扫描的范围.

    也可以考虑将大数据量的表, 水平查分, 通过查不同的表分区达到效果.

    解决方法3: 避免使用 SELECT * FROM

    要明确写出所有要访问的列, 能加快速度.比如:你去拿几个东西过来/你去把1,2,3拿过来一个道理
    例如

    SELECT * FROM my_table

    改成:

    SELECT id, name, address FROM my_table

    解决方法4: 将几个LIKE语句放到函数regexp_like()

    Presto的查询优化器不能改善许多LIKE语句使用的地方, 导致这样的语句查询速度慢.

    例如

    SELECT
      ...
    FROM
      access
    WHERE
      method LIKE '%GET%' OR
      method LIKE '%POST%' OR
      method LIKE '%PUT%' OR
      method LIKE '%DELETE%'

    上面的语句能用regexp_like函数优化成一句

    SELECT
      ...
    FROM
      access
    WHERE
      regexp_like(method, 'GET|POST|PUT|DELETE')

    如何优化JOIN性能?

    尽量让JOIN的条件简单,最好是ON后面的比较表达式两边必涉及计算。

    例如

    SELECT a.date, b.name FROM
    left_table a
    JOIN right_table b
    ON a.date = CAST((b.year * 10000 + b.month * 100 + b.day) as VARCHAR)

    上面的SQL语句的JOIN性能不高,因为JION条件包含了表达式计算。我们可以通过子查询的形式来优化上面的语句。

    SELECT a.date, b.name FROM
    left_table a
    JOIN (
      SELECT
        CAST((b.year * 10000 + b.month * 100 + b.day) as VARCHAR) date,  # generate join key
        name
      FROM right_table
    ) b
    ON a.date = b.date  # Simple equi-join

    上面的语句,就是直接比较两个VARCHAR的值,这样会比比较一个VARCHAR和一个表达式结果的性能高。

    我们还能继续优化,使用Presto的WITH语句进行子查询。

    WITH b AS (
      SELECT
        CAST((b.year * 10000 + b.month * 100 + b.day) as VARCHAR) date,  # generate join key
        name
      FROM right_table
    )
    SELECT a.date, b.name FROM
    left_table a
    JOIN b
    ON a.date = b.date

    如何使查询简单化

    解决方法1: 使用WITH语句

    如果你的查询语句非常复杂或者有多层嵌套的子查询,请试着用WITH语句将子查询分离出来。

    例如

    SELECT a, b, c FROM (
       SELECT a, MAX(b) AS b, MIN(c) AS c FROM tbl GROUP BY a
    ) tbl_alias

    可以被重写为下面的形式

    WITH tbl_alias AS (SELECT a, MAX(b) AS b, MIN(c) AS c FROM tbl GROUP BY a)
    SELECT a, b, c FROM tbl_alias

    同样,也可以将各个步骤的子查询通过WITH语句罗列出来,子查询之间用“,”分割。

    WITH tbl1 AS (SELECT a, MAX(b) AS b, MIN(c) AS c FROM tbl GROUP BY a),
         tbl2 AS (SELECT a, AVG(d) AS d FROM another_tbl GROUP BY a)
    SELECT tbl1.*, tbl2.* FROM tbl1 JOIN tbl2 ON tbl1.a = tbl2.a

    解决方法2:在CREATE TABLE语句中使用WITH语句

    如果CREATE TABLE语句的查询部分很复杂或者潜逃了多层子查询,就需要考虑用WITH语句

    例如:

    CREATE TABLE tbl_new AS WITH tbl_alias AS (SELECT a, MAX(b) AS b, MIN(c) AS c FROM tbl1)
    SELECT a, b, c FROM tbl_alias
    CREATE TABLE tbl_new AS WITH tbl_alias1 AS (SELECT a, MAX(b) AS b, MIN(c) AS c FROM tbl1),
                                 tbl_alias2 AS (SELECT a, AVG(d) AS d FROM tbl2)
    SELECT tbl_alias1.*, tbl2_alias.* FROM tbl_alias1 JOIN tbl_alias2 ON tbl_alias1.a = tbl_alias2.a

    解决方法3:用GROUP BY语句时,GROUP BY的目标可用数字代替

    在Presto SQL中,GROUP BY语句需要与SELECT语句中的表达式保持一致,不然会提示语法错误。

    例如:

    SELECT TD_TIME_FORMAT(time, 'yyyy-MM-dd HH', 'PDT') hour, count(*) cnt
    FROM my_table
    GROUP BY TD_TIME_FORMAT(time, 'yyyy-MM-dd HH', 'PDT')

    上面的SQL语句的GROUP BY部分可以用GROUP BY 1,2,3 ...来表示

    SELECT TD_TIME_FORMAT(time, 'yyyy-MM-dd HH', 'PDT') hour, count(*) cnt
    FROM my_table
    GROUP BY 1
    Note: 这些数字是从1开始的,有别于程序要思维从0开始。

    Exceeded max (local) memory 错误

    Presto会跟踪每个查询的内存使用情况.可用内存的多少是根据你的查询计划变动的,所以在大多数情况下可以从写查询语句来达到优化内存使用的目的.

    下面列出来的就是内存密集型的语句块:

    • district
    • UNION
    • ORDER BY
    • GROUP BY (许多字段的情况)
    • joins (各种JOIN)

    解决方法1: 尽量少使用distinct

    distinct 会排除所有不唯一的行.下面的例子就是检查你的数据表中是否包含了相同的数据行(c1,c2,c3)

    SELECT distinct c1, c2, c3 FROM my_table

    上面的操作会存储一整字段c1,c2和c3到presto的单个工作节点的内存, 然后检查(c1,c2,c3)的唯一性. 随着字段的增多以及字段数据量的增大,所需要的内存也会直线上升.

    所以, 去掉查询语句中的distinct关键字, 或者只在子查询(有有限少量字段的情况下)使用.

    解决方法2: 用approx_distinct(x)代替count(distinct x)

    NOTE: approx_distinct(x)会返回一个正确的近似值, 如果只是需要看一个大概的趋势,可以考虑.

    解决方法3: 尽量用UNION ALL代替UNION

    和distinct的原因类似, UNION有去重的功能, 所以会引发内存使用的问题.

    如果你只是拼接两个或者多个SQL查询的结果, 考虑用UNION ALL

    解决方法4: 尽量避免ORDER BY

    SELECT c1, c2 FROM my_table ORDER BY c1

    Presto在排序的时候启用的是单一节点进行工作, 所以整个数据需要在单节点内存限制的范围内, 超过这个内存限制就会报错.

    如果你需要排序的数据在一个小的量级, 用ORDER BY没有问题; 如果需要排序的数据在GB的级别,需要考虑其他的解决方案.

    例如: 大量级的数据排序可以考虑结合HIVE和presto. 首先, 用Presto将大量的数据存储到一个临时表中,然后用HIVE取对数据排序.

    解决方法5: 减少GROUP BY的字段

    SELECT avg(c1), min_by(c2, time), max(c3), count(c4), ...
    FROM my_table
    GROUP BY c1, c2, c3, c4, ...

    减少GROUP BY语句后面的排序一句字段的数量能减少内存的使用.

    解决方法6:用大表取JOIN小表

    下面这种用小数据表去JOIN大数据表的查询会极度消耗内存.

    SELECT * FROM small_table, large_table
    WHERE small_table.id = large_table.id

    Presto 会默认执行广播式的JOIN操作,它会将左表拆分到几个工作节点上, 然后发送整个右表分别到已拆分好的处理左表的工作节点上. 如果右表非常大就会超出工作节点的内存限制,进而出错.
    所以需要用大表JOIN小表

    SELECT * FROM large_table, small_table
    WHERE large_table.id = small_table.id

    如果左表和右表都比较大怎么办?

    1. 修改配置distributed-joins-enabled (presto version >=0.196)
    2. 在每次查询开始使用distributed_join的session选项
    -- set session distributed_join = 'true'
    SELECT * FROM large_table, large_table1
    WHERE large_table1.id = large_table.id
    核心点就是使用distributed join. Presto的这种配置类型会将左表和右表同时以join key的hash value为分区字段进行分区. 所以即使右表也是大表,也会被拆分.

    缺点是会增加很多网络数据传输, 所以会比broadcast join的效率慢.

    查询生成的大量数据优化的问题

    Presto用JOSN text的形式保存数据。如果查询出来的数据大于100G,Presto将传输大于100G的JSON text来保存查询结果。所以,即使查询处理即将完成,输出这么大的JOSN text也会消耗很长时间。

    解决方法1:不要用==SELECT *==

    解决方法2:用result_output_redirect='true' 注释

    在查询语句前添加注释(result_output_redirect='true'),能让查询更快些。

    -- set session result_output_redirect='true'
    select a, b, c, d FROM my_table

    上面的语句能让Presto用并行的方式生成查询结果,能跳过在Presto协调器进行JSON转换的过程。

    Note: 但是,如果使用了ORDER BY语句,这个魔术注释将被忽略。

    Presto查询优化

    数据存储

    合理设置分区

    与Hive类似,Presto会根据元信息读取分区数据,合理的分区能减少Presto数据读取量,提升查询性能。

    使用列式存储

    Presto对ORC文件读取做了特定优化,因此在Hive中创建Presto使用的表时,建议采用ORC格式存储。相对于Parquet,Presto对ORC支持更好。

    使用压缩

    数据压缩可以减少节点间数据传输对IO带宽压力,对于即席查询需要快速解压,建议采用snappy压缩

    预先排序

    对于已经排序的数据,在查询的数据过滤阶段,ORC格式支持跳过读取不必要的数据。比如对于经常需要过滤的字段可以预先排序。

    SQL优化

    • 只选择使用必要的字段: 由于采用列式存储,选择需要的字段可加快字段的读取、减少数据量。避免采用*读取所有字段
    • 过滤条件必须加上分区字段
    • Group By语句优化: 合理安排Group by语句中字段顺序对性能有一定提升。将Group By语句中字段按照每个字段distinct数据多少进行降序排列, 减少GROUP BY语句后面的排序一句字段的数量能减少内存的使用.
    • Order by时使用Limit, 尽量避免ORDER BY: Order by需要扫描数据到单个worker节点进行排序,导致单个worker需要大量内存
    • 使用近似聚合函数: 对于允许有少量误差的查询场景,使用这些函数对查询性能有大幅提升。比如使用approx_distinct() 函数比Count(distinct x)有大概2.3%的误差
    • 用regexp_like代替多个like语句: Presto查询优化器没有对多个like语句进行优化,使用regexp_like对性能有较大提升
    • 使用Join语句时将大表放在左边: Presto中join的默认算法是broadcast join,即将join左边的表分割到多个worker,然后将join右边的表数据整个复制一份发送到每个worker进行计算。如果右边的表数据量太大,则可能会报内存溢出错误。
    • 使用Rank函数代替row_number函数来获取Top N
    • UNION ALL 代替 UNION :不用去重
    • 使用WITH语句: 查询语句非常复杂或者有多层嵌套的子查询,请试着用WITH语句将子查询分离出来
  • 相关阅读:
    LeetCode:Length of Last Word
    Team Queue(POJ 2259)
    LeetCode:Largest Rectangle in Histogram(update)
    bzoj4821 && luogu3707 SDOI2017相关分析(线段树,数学)
    luogu1438无聊的数列(区间加等差数列,求一个数的和)
    luogu1081 开车旅行2012 D1T3 (倍增,set,O2)
    bzoj4094 && luogu3097 最优挤奶
    luogu3888 GDOI2014拯救莫里斯 (状压dp)
    noip2017D1T3逛公园(拓扑图上dp,记忆化搜索)
    bzoj1065 NOI2008奥运物流 (dp,树上背包,推式子)
  • 原文地址:https://www.cnblogs.com/pengpenghuhu/p/14417396.html
Copyright © 2011-2022 走看看