Impala只用于hive,稍领先于Presto,但是presto在数据源的支持上非常丰富,有hive,redis,tushujuku,图数据库、redis、关系型数据库。Presto是一个facebook开源的分布式SQL查询引擎,适用于交互式分析查询,数据量支持GB到PB字节。presto的架构由关系型数据库的架构演化而来。presto之所以能在各个内存计算型数据库中脱颖而出,在于以下几点:
Prestore的优缺点和MR的对比,prestore基于内存,尽量的少使用join,所以应该提前在hive中创建好宽表,然后直接查询。
Presto采用典型的master-slave模型:
在worker的配置中,可以选择配置:
{
"environment": "production",
"services": [
{
"id": "ffffffff-ffff-ffff-ffff-ffffffffffff",
"type": "discovery",
"location": "/ffffffff-ffff-ffff-ffff-ffffffffffff",
"pool": "general",
"state": "RUNNING",
"properties": {
"http": "http://192.168.1.1:8080"
}
}
]
}
2和3的原理是基于service inventory, worker 会动态监听这个文件,如果有变化,load出最新的配置,指向最新的discovery节点。
在设计上,discovery和coordinator都是单节点。如果有多个coordinator同时存活,worker 会随机的向其中一个汇报进程和task状态,导致脑裂。调度query时有可能会发生死锁。
discovery和coordinator可用性设计。由于service inventory的使用,监控程序可以在发现discovery挂掉后,修改service inventory中的内容,指向备机的discovery。无缝的完成切换。coordiantor的配置必须要在进程启动时指定,同一个集群中无法存活多个coordinator。因此最好的办法是和discovery配置到一台机器。 secondary机器部署备用的discovery和coordinator。在平时,secondary机器是一个只包含一台机器的集群,在primary宕机时,worker的心跳瞬间切换到secondary。
presto采取三层表结构:
presto的存储单元包括:
不同类型的block:
2. 可变宽度的block,应用于string类数据,由三部分信息组成
3. 固定宽度的string类型的block,所有行的数据拼接成一长串Slice,每一行的长度固定。
4. 字典block:对于某些列,distinct值较少,适合使用字典保存。主要有两部分组成:
了解了presto的数据模型,就可以给presto编写插件,来对接自己的存储系统。presto提供了一套connector接口,从自定义存储中读取元数据,以及列存储数据。先看connector的基本概念:
插件能够帮助开发者添加这些功能:
Presto提供了一个简单的connector : local file connector ,可用于参考如何实现自己的connector。不过local file connector中使用的遍历数据的单元是cursor,即一行数据,而不是一个page。 hive 的connector中实现了三种类型,parquet connector, orc connector, rc file connector。
上文从宏观上介绍了presto的一些原理,接下来几篇文章让我们深入presto 内部,了解一些内部的设计,这对性能调优会有比较大的用处,也有助于添加自定义的operator。
Presto是一款内存计算型的引擎,所以对于内存管理必须做到精细,才能保证query有序、顺利的执行,部分发生饿死、死锁等情况。
Presto采用逻辑的内存池,来管理不同类型的内存需求。
Presto把整个内存划分成三个内存池,分别是System Pool ,Reserved Pool, General Pool。
System Pool用于系统使用的内存,例如机器之间传递数据,在内存中会维护buffer,这部分内存挂载system名下。
那么,为什么需要保留区内存呢?并且保留区内存正好等于query在机器上使用的最大内存?
如果没有Reserved Pool, 那么当query非常多,并且把内存空间几乎快要占完的时候,某一个内存消耗比较大的query开始运行。但是这时候已经没有内存空间可供这个query运行了,这个query一直处于挂起状态,等待可用的内存。 但是其他的小内存query跑完后,又有新的小内存query加进来。由于小内存query占用内存小,很容易找到可用内存。 这种情况下,大内存query就一直挂起直到饿死。
所以为了防止出现这种饿死的情况,必须预留出来一块空间,共大内存query运行。 预留的空间大小等于query允许使用的最大内存。Presto每秒钟,挑出来一个内存占用最大的query,允许它使用reserved pool,避免一直没有可用内存供该query运行。
Presto内存管理,分两部分:
当query内存和机器内存汇总之后,coordinator会挑选出一个内存使用最大的query,分配给Reserved Pool。
内存管理是由coordinator来管理的, coordinator每秒钟做一次判断,指定某个query在所有的机器上都能使用reserved 内存。那么问题来了,如果某台机器上,,没有运行该query,那岂不是该机器预留的内存浪费了?为什么不在单台机器上挑出来一个最大的task执行。原因还是死锁,假如query,在其他机器上享有reserved内存,很快执行结束。但是在某一台机器上不是最大的task,一直得不到运行,导致该query无法结束。