1、Presto简介
Presto是Facebook开源的MPP(Massive Parallel Processing)SQL引擎,其理念来源于一个叫Volcano的并行数据库,该数据库提出了一个并行执行SQL的模型,它被设计为用来专门进行高速、实时的数据分析。
Presto是一个SQL计算引擎,分离计算层和存储层,其不存储数据,通过Connector SPI实现对各种数据源(Storage)的访问。
1.1 架构
Presto沿用了通用的Master-Slave架构,一个Coordinator,多个Worker。
Coordinator负责解析SQL语句,生成执行计划,分发执行任务给Worker节点执行,Worker节点负责实际执行查询任务。
Presto提供了一套Connector接口,用于读取元信息和原始数据。
Presto 内置有多种数据源,如 Hive、MySQL、Kudu、Kafka 等。
Presto 的扩展机制允许自定义 Connector,从而实现对定制数据源的查询。
假如配置了Hive Connector,需要配置一个Hive MetaStore服务为Presto提供Hive元信息,Worker节点通过Hive Connector与HDFS交互,读取原始数据。
1.2 实现低延时的原理
Presto是一个交互式查询引擎,我们最关心的是Presto实现低延时查询的原理,以下几点是其性能脱颖而出的主要原因:
- 完全基于内存的并行计算
- 流水线
- 本地化计算
- 动态编译执行计划
- 小心使用内存和数据结构
- GC控制
- 无容错
2、Presto查询优化
2.1 存储优化
① 合理设置分区
与Hive类似,Presto会根据元信息读取分区数据,合理的分区能减少Presto数据读取量,提升查询性能。
② 使用列式存储
Presto对ORC文件读取做了特定优化,因此在Hive中创建Presto使用的表时,建议采用ORC格式存储。相对于Parquet,Presto对ORC支持更好。
③ 使用压缩
数据压缩可以减少节点间数据传输对IO带宽压力,对于即席查询需要快速解压,建议采用snappy压缩
④ 预先排序
有条件的话提前做好排序,对于已经排序的数据,在查询的数据过滤阶段,ORC格式支持跳过读取不必要的数据。
比如对于经常需要过滤的字段可以预先排序。
2.2 查询优化
① select时只选择必要字段,避免使用 * 号
② 过滤条件加上分区字段,减少查询数据量
③ 合理安排Group by语句中字段顺序对性能有一定提升
将Group By语句中字段按照每个字段distinct数据多少进行降序排列。示例中uid是用户id,比性别数据大很多。
代码语言:javascript复制[GOOD]: SELECT GROUP BY uid, gender
[BAD]: SELECT GROUP BY gender, uid
④ Order by时使用Limit
Order by需要扫描数据到单个worker节点进行排序,导致单个worker需要大量内存。如果是查询Top N或者Bottom N,使用limit可减少排序计算和内存压力。
⑤ 用regexp_like代替多个like语句
Presto查询优化器没有对多个like语句进行优化,使用regexp_like对性能有较大提升
代码语言:javascript复制[GOOD]
SELECT ...
FROM access
WHERE regexp_like(method, 'GET|POST|PUT|DELETE')
[BAD]
SELECT ...
FROM access
WHERE
method LIKE '%GET%' OR
method LIKE '%POST%' OR
method LIKE '%PUT%' OR
method LIKE '�LETE%'
⑥ 使用Rank函数代替row_number函数来获取Top N
在进行一些分组排序场景时,使用rank函数性能更好
2.3 Join优化
① 使用Join语句时将大表放在左边
Presto中join的默认算法是broadcast join,即将join左边的表分割到多个worker,然后将join右边的表数据整个复制一份发送到每个worker进行计算。
如果右边的表数据量太大,则可能会报内存溢出错误。
② 如果左表和右表都比较大
为防止内存溢出,做如下配置:
1)修改配置distributed-joins-enabled (presto version >=0.196)
2)在每次查询开始使用distributed_join的session选项
代码语言:javascript复制set session distributed_join = 'true'
SELECT ...
FROM
large_table1
join large_table2
on large_table1.id = large_table2.id
核心点就是使用distributed join,也就是hash join。
Presto的这种配置类型会将左表和右表同时以join key的hash value为分区字段进行分区。
所以即使右表也是大表,也会被拆分,相比broadcast join,这种join方式的会增加很多网络数据传输,效率慢。
③ 多个join的OR条件使用union代替
代码语言:javascript复制SELECT ...
FROM
t1
JOIN t2
ON t1.a1 = t2.a1 OR
t1.a2 = t2.a2
改为
SELECT ...
FROM
t1
JOIN t2
ON t1.a1 = t2.a1
union
SELECT ...
FROM
t1
JOIN t2
ON t1.a2 = t2.a2
④ 使用WITH语句
使用Presto分析统计数据时,可考虑把多次查询合并为一次查询,用Presto提供的子查询完成。
代码语言:javascript复制WITH tmp AS (
SELECT DISTINCT a1, a2
FROM t2
)
SELECT ...
FROM t1
JOIN tmp
ON t1.a1 = tmp.a1
union
SELECT ...
FROM t1
JOIN tmp
ON t1.a2 = tmp.a2;
⑤ 尽量用UNION ALL代替UNION
和distinct类似, UNION有去重的功能, 所以会使用到内存,如果只是拼接两个或者多个SQL查询的结果, 考虑用UNION ALL
资深数据专家,关注大数据、数仓、Java、Flink/Spark/Hadoop、OLAP等技术,微信搜索【硬核林川】公众号,回复【资料】可领取最新的大数据视频教程和书籍。