presto是如何保证作业内存不会发生冲突和溢出

423 阅读4分钟

本文分享自华为云社区《presto是如何保证作业内存不会发生冲突和溢出?presto内存管理机制深入分析》,作者:breakDawn。

首先,presto分了如下3个内存池 image.png

System Pool

System Pool,指系统内存池,是用来保留给系统和缓冲区使用的,默认为40%的内存空间留给系统使用。

看来对于presto来说,作业之间的缓冲区是存在共用的,认为是系统、通用层面的部分。

General Pool

常规内存池,用来分配每个query运行时内存的。其中大部分的query使用general Pool。

Reserved Pool

保留内存池,用来为可能突然触发的超大作业进行内存保留分配。即最大的一个query,会使用Reserved Pool

Reserved Pool的空间等同于一个query在一个机器上运行使用的最大空间大小,默认是10%的空间。

在真正执行物理计划前,内存需求都来自于systemMemoryPool,包括临时数据结构,传输buffer等。执行物理计划时,不同的Operator类型都根据需要申请内存,比如aggregationOperator使用getEsctimatedSize()方法预估需要的内存。

这里获取的内存来自于reservatedMemoryPool或者generalMemoryPool,究竟使用哪个pool取决于当前查询是否耗用内存最大。

问:为什么要引出一个Reserved内存池且只提供给1个作业使用?

如果没有Reserved Pool, 那么当query非常多,并且把内存空间几乎快要占完的时候,某一个内存消耗比较大的query开始运行。

但是这时候已经没有内存空间可供这个query运行了,这个query一直处于挂起状态,一直在等待可用的内存。

其他的小内存query跑完后, 可能只腾出一点点的空间, 又有新的小内存query加进来。由于小内存query占用内存小,很容易找到可用内存。 这种情况下,大内存query就一直挂起直到饿死。

所以为了防止出现这种饿死的情况,必须预留出来一块空间,共大内存query运行。 预留的空间大小等于query允许使用的最大内存。Presto每秒钟挑出来一个内存占用最大的query,允许它使用reserved pool,避免一直没有可用内存供该query运行。

保留池的选举机制(Reserved池是如何选出来的)

如下图所示: image.png Presto内存管理,分两部分:

1. query内存(作业内存)管理

query划分成很多task, 每个task会有一个线程循环获取task的状态,包括task所用内存。汇总成query所用内存。如果query的汇总内存超过一定大小,则强制终止该query。

2. 机器内存管理

coordinator有一个线程,定时的轮训每台机器,查看当前的机器内存状态。当query内存和机器内存汇总之后,coordinator会挑选出一个内存使用最大的query,分配给Reserved Pool。内存管理是由coordinator来管理的, coordinator每秒钟做一次判断,指定某个query在所有的机器上都能使用reserved 内存。

问:如果某台机器上,没有运行该query,那岂不是该机器预留的内存浪费了?为什么不在单台机器上挑出来一个最大的task执行?

原因还是死锁,假如query,在其他机器上享有reserved内存,很快执行结束。但是在某一台机器上不是最大的task(即这个task在另一个节点可能只排第二名,被另一个大作业占了保留池, 导致下一步卡住了,无法连续的执行),一直得不到运行,导致该query无法结束。

所以首要目的是保证 此刻已感知到的最大作业尽快执行完毕。

如何在内存不足时杀掉不需要的query?

每次作业提交存在一个会话级别的配置 query_max_memory,即本次查询规定的最大内存。在轮询过程种如果发现内存超出本次查询上限内存, 会杀掉这个query。

还有个会话配置resource_overcommit,如果设为true,后面即使内存暂时超出单作业规定内存,业不会被杀掉 但如果整个集群的内存出现不足,他仍然会被杀掉。

  • 集群内存不足的判定: 存在某个worker节点的内存池出现内存不足(即该节点阻塞了),则认为发生了内存溢出。

image.png

image.png

点击关注,第一时间了解华为云新鲜技术~