【presto】转载:Presto集群内存不足时保护机制

前言

一个任务什么时候会被kill掉?

1.sql 本身使用的资源超出配置

相关的配置如下:

  • query.max-memory=120GB
  • query.max-memory-per-node=20GB
  • query.max-total-memory-per-node=32GB

2.集群内存不足时,触发内存保护机制
query.low-memory-killer.policy

  • none
  • total-reservation-on-blocked-nodes
  • total-reservation

presto内存pool分布

为了防止集群里节点OOM,Presto有个循环线程来获取当前集群节点和集群整体内存占用情况。我们知道Presto里分为RESERVED_POOL和GENERAL_POOL。

判断节点是否阻塞(内存不足):
如果使用RESERVED_POOL(意思是说最大SQL使用这个POOL),那判断集群内存超出内存的方法就是:

  • RESERVED_POOL内存被SQL占用了

  • GENERAL_POOL里有被阻塞的Node

因为RESERVED_POOL会导致内存浪费,我们集群配置参数没有使用这个POOL,只使用了GENERAL_POOL,所以只需要查看下GENERAL_POOL是怎么判断节点是否Block住的。

if (poolInfo.getFreeBytes() + poolInfo.getReservedRevocableBytes() <= 0) {
    blockedNodes++;
}

getReservedRevocableBytes 这个是用于获取spill到磁盘的内存,目前我们集群是不允许内存Spill到磁盘的,因为Presto面向的是ad-hoc场景,要求是快,如果说需要spill到磁盘,那spark是一个更好的选择,且早期版本Presto spill到磁盘之前测试过稳定性比较差,场景也比较少。

所以就判断GENERAL_POOL里是否还有剩余内存,如果小于等于0,那就表示该节点是个Block状态。

Kill策略:

把所有query遍历一遍,如果查询有RESOURCE_OVERCOMMIT标志,且内存溢出了,那就把带有此标志的SQL Kill掉。

if (resourceOvercommit && outOfMemory) {
    // If a query has requested resource overcommit, only kill it if the cluster has run out of memory
    DataSize memory = succinctBytes(getQueryMemoryReservation(query));
    query.fail(new PrestoException(CLUSTER_OUT_OF_MEMORY,
       format("The cluster is out of memory and %s=true, so this query was killed. It was using %s of memory", RESOURCE_OVERCOMMIT, memory)));
    queryKilled = true;
}

如果没有RESOURCE_OVERCOMMIT标志,那就看看内存是否超过集群允许的内存:

if (!resourceOvercommit) {
    long userMemoryLimit = min(maxQueryMemory.toBytes(), getQueryMaxMemory(query.getSession()).toBytes());
    if (userMemoryReservation > userMemoryLimit) {
        query.fail(exceededGlobalUserLimit(succinctBytes(userMemoryLimit)));
        queryKilled = true;
    }
 
    // enforce global total memory limit if system pool is disabled
    long totalMemoryLimit = min(maxQueryTotalMemory.toBytes(), getQueryMaxTotalMemory(query.getSession()).toBytes());
    if (!isLegacySystemPoolEnabled && totalMemoryReservation > totalMemoryLimit) {
        query.fail(exceededGlobalTotalLimit(succinctBytes(totalMemoryLimit)));
        queryKilled = true;
    }
}

由上面我们可以知道,想让上述配置生效,query.max-memory需要配置合理的值,比如我们有5台Worker,每台Worker最大允许的内存为10G(超过10GB,每台Worker会自动Kill掉SQL),那此集群允许的最大内存为5*10G = 50GB,如果你配置query.max-memory(maxQueryMemory)100GB,那上面的逻辑将不会走,导致此逻辑失效。

同时,如果发现集群有节点已经OOM了,但是过了5S,依然没有SQL被Kill掉,那就会使用以下触发Presto SQL Kill策略,一共2种策略,query.low-memory-killer.policy指定:

public static class LowMemoryKillerPolicy
{
    public static final String NONE = "none";
    public static final String TOTAL_RESERVATION = "total-reservation";
    public static final String TOTAL_RESERVATION_ON_BLOCKED_NODES = "total-reservation-on-blocked-nodes";
}
  • total-reservation表示杀掉集群中占有内存最大的SQL
  • total-reservation-on-blocked-nodes表示杀死在内存不足(阻塞)的节点上使用最多内存的查询。

假如我们通过RESOURCE_GROUP限制集群的并发大小为10,单个节点最大内存为10,集群允许的最大内存少于100GB,那此配置集群肯定不会出现OOM现象(不考虑内存泄露问题),但是这样的话,会导致资源利用率不够,所以线上一般会配置并发数 * 单机最大内存远大于集群允许的最大内存,这就指望LowMemoryKillerPolicy能帮助我们Kill掉内存占用最大的SQL。但是实际上依然会出现Worker OOM情况,此时一般思路:

1、dmesg 查看系统日志,确定是操作系统触发的OOM还是JVM OOM,如果是操作系统触发的OOM,可能堆外内存或Presto内存泄露会导致这个问题,但是更多情况下需要确定下Presto节点是否有其他服务,一般情况下是其他服务内存突然增大,导致系统内存不够。

2、配置是否正确,是否限制了单机最大内存,是否限制了集群最大内存且小于单机最大内存 * 集群节点数,是否配置了LowMemoryKillerPolicy。

3、如果上面配置都正确,需要考虑2个问题

1)什么原因没有触发LowMemoryKillerPolicy,可以将判断节点阻塞(内存不足)的逻辑变的更严格点,因为不是个实时过程。
2)结点是否有内存泄露情况。

从上面可以看出Presto内存管理比较粗暴,内存不够就杀掉SQL,而其他一些引擎做法就比较细腻些,SQL会一直Block住,直到有可用内存为止,不过Presto是面向ad-hoc场景,这种内存管理方法更简单有效吧。

转自:http://armsword.com/2020/02/18/presto-memory-kill-policy/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值