查询速度慢, 如何优化?

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

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

  • count(distinct x)

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

    UNION有个功能是: 如果两条记录一样, 会只保留一条记录(去重).

    • 如果不考虑去重的情况, 请使用UNION ALL
  • ORDER BY

    Presto对数据排序是作用在单节点上的

    • 如果要排序的数据量超过百万行, 要谨慎考虑. 如果非要排序,尽量将排序的字段减少些.

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

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

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

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

要明确写出所有要访问的列, 能加快速度.
例如

SELECT * FROM my_table

改成:

SELECT id, name, address FROM my_table

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

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

例如

SELECT...
FROMaccess
WHEREmethod LIKE '%GET%' ORmethod LIKE '%POST%' ORmethod LIKE '%PUT%' ORmethod LIKE '%DELETE%'

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

SELECT...
FROMaccess
WHEREregexp_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 (SELECTCAST((b.year * 10000 + b.month * 100 + b.day) as VARCHAR) date,  # generate join keynameFROM right_table
) b
ON a.date = b.date  # Simple equi-join

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

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

WITH b AS (SELECTCAST((b.year * 10000 + b.month * 100 + b.day) as VARCHAR) date,  # generate join keynameFROM 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语句,这个魔术注释将被忽略。

如何拼接字符串

解决方法:用 || 运算符

SELECT 'hello ' || 'presto'

如何在字段包含NULL的情况下 添加default value

解决方法:用COALESCE(v1,v2,...)函数

-- This retuns 'N/A' if name value is null
SELECT COALESCE(name, 'N/A') FROM table1

如何从两个数中选出最大/最小值

解决方法:用greatest / least 函数

SELECT greatest(5, 10) -- returns 10

Binary函数的应用

这里主要的问题是:如何将binary/varbinary类型转换为varchar类型

转换SHA256/MD5

SELECT to_hex(sha256(to_utf8('support@treasure-data.com'))) as emailSELECT to_hex(md5(to_utf8('support@treasure-data.com'))) as email

转换base64

SELECT to_base64(to_utf8('support@treasure-data.com')) as email
=> "c3VwcG9ydEB0cmVhc3VyZS1kYXRhLmNvbQ=="
SELECT FROM_UTF8(from_base64('c3VwcG9ydEB0cmVhc3VyZS1kYXRhLmNvbQ=='))
=> "support@treasure-data.com"

使用Presto SQL一些常见问题总结相关推荐

  1. json函数 presto_Hive sql和Presto sql的一些对比

    最近由于工作上和生活上的一些事儿好久没来博客园了,但是写博客的习惯还是得坚持,新的一年需要更加努力,困知勉行,终身学习,每天都保持空杯心态.废话不说,写一些最近使用到的Presto SQL和Hive ...

  2. PRESTO SQL总结分享

    1.常用SQL语法 1.1 字符substr(staff_leave_date,1,10) concat(string1, ..., stringN) → varchar--拼接多个字符串 lengt ...

  3. presto sql语法总结

    presto sql语法总结 Hive: collect_set 转为数组并去重,concat_ws 将数组用逗号间隔连接成字符串 presto sql语法问题可以参考:https://prestod ...

  4. 【翻译】Presto: SQL on Everything

    文章目录 作者 公司 摘要 关键词 I. 引言 II. 用例 A. 交互式分析 B. 批处理 ETL C. A/B 测试 D. 开发者/广告主分析 III. 架构概览 IV. 系统设计 A. SQL ...

  5. 【能力提升】SQL Server常见问题介绍及快速解决建议

    前言 本文旨在帮助SQL Server数据库的使用人员了解常见的问题,及快速解决这些问题.这些问题是数据库的常规管理问题,对于很多对数据库没有深入了解的朋友提供一个大概的常见问题框架. 下面一些问题是 ...

  6. SQL Server常见问题介绍及快速解决建议

    前言 本文旨在帮助SQL Server数据库的使用人员了解常见的问题,及快速解决这些问题.这些问题是数据库的常规管理问题,对于很多对数据库没有深入了解的朋友提供一个大概的常见问题框架. 下面一些问题是 ...

  7. SQL Server常见问题总结

    写在前面 在QQ群,微信群,论坛中经常帮助使用SQL Server数据库的朋友解决问题,但是有一些最常见最基本的问题,每天都有人问,回答多了也不想再解答了,索性把这些问题整理一下,再有人问到直接发链接 ...

  8. sql语句常见问题集锦

    一.group by .where.having问题 group by要求返回的所有字段,要么出现在聚合函数(avg.sum.count.max.min等)中,要么出现Group By后面作为分组依据 ...

  9. SQL新手常见问题001:1292 incorrect date value

    SQL新手问题001:1292 incorrect date value 我是在运行sql语句后,发现结果和要求不一致了,想到应该是表格数据类型设置出错了.在更改了数据类型之后,保存表格时,sql弹出 ...

最新文章

  1. mysql 查看运行级别_Linux的运行级别和chkconfig用法
  2. 对代理ARP技术的误读、无法完成代理ARP实验的故障分析
  3. 单台主机 kafka + zookeeper 集群搭建
  4. debian 7上安装svn
  5. TFRecords转化和读取
  6. 48小时单GPU训练DistilBERT!这个检索模型轻松达到SOTA
  7. 番茄花园win11 32位专业版镜像v2021.08
  8. leetcode971. Flip Binary Tree To Match Preorder Traversal
  9. android token机制_Android之window机制token验证
  10. 企业应用开发(1)--软件学院OJ系统需求分析与原型图设计
  11. 东方通php支持,应用服务器TongWeb
  12. java实现解压war_java文件操作之war压缩解压
  13. 关于苹果绕ID的一些注意事项,A6-A7 Sliver 6.2 小白少走弯路。
  14. 使用docx4j追尾合并多个docx文件为一个docx文件
  15. 如何进行自动驾驶算法开发
  16. publish over ssh、 Kubernetes Continuous Deploy插件
  17. JVM - 垃圾回收(垃圾标记阶段算法,内存泄漏与溢出)(2)
  18. 马里奥(附代码windows)
  19. Pspice——Source用法的汇总
  20. 医药CRM解决方案,助力医药企业数字化转型

热门文章

  1. HibernateCRUD基础框架(1)-实体类
  2. jquery 插件开发小组
  3. java - rest-assured 接口测试
  4. 从支付宝SDK的支付流程理解什么是公钥和私钥,什么是加密和数字签名
  5. webpack模块化原理-ES module
  6. bootstrap按钮组(二)
  7. 增强环路现象!? 为你揭示云计算魔力!
  8. 创建型模式学习总结——设计模式学习笔记
  9. Office 程序默认打开方式
  10. Q112:用Xcode调试程序(以PBRT-V3中的pbrt为例)