Presto有三种内存池,分别为GENERAL_POOL、RESERVED_POOL、SYSTEM_POOL。这三个内存池占用的内存大小是由下面算法进行分配的:node
builder.put(RESERVED_POOL, new MemoryPool(RESERVED_POOL, config.getMaxQueryMemoryPerNode())); builder.put(SYSTEM_POOL, new MemoryPool(SYSTEM_POOL, systemMemoryConfig.getReservedSystemMemory())); long maxHeap = Runtime.getRuntime().maxMemory(); maxMemory = new DataSize(maxHeap - systemMemoryConfig.getReservedSystemMemory().toBytes(), BYTE); DataSize generalPoolSize = new DataSize(Math.max(0, maxMemory.toBytes() - config.getMaxQueryMemoryPerNode().toBytes()), BYTE); builder.put(GENERAL_POOL, new MemoryPool(GENERAL_POOL, generalPoolSize));
梳理这块代码对应的逻辑和配置文件,得出RESERVED_POOL大小由config.properties里的query.max-memory-per-node指定;SYSTEM_POOL由config.properties里的resources.reserved-system-memory指定,若是不指定,默认值为Runtime.getRuntime().maxMemory() * 0.4,即0.4 * Xmx值;而GENERAL_POOL值为 总内存(Xmx值)- 预留的(max-memory-per-node)- 系统的(0.4 * Xmx)。算法
而这三种内存池分别用于不一样的地方,分析代码和阅读Presto开发手册,大致能够定位出:并发
简单说GENERAL_POOL用于普通查询的physical operators;SYSTEM_POOL用于读写buffer;而RESERVED_POOL比较特殊,大部分时间里是不参与计算的,只有当同时知足以下情形下,才会被使用,而后从全部查询里获取占用内存最大的那个查询,而后将该查询放到 RESERVED_POOL 里执行,同时注意RESERVED_POOL只能用于一个Query。ui
一、GENERAL_POOL有节点出现阻塞节点(block node)状况,即该node内存不足
二、RESERVED_POOL没有被使用spa
GENERAL_POOL、RESERVED_POOL、SYSTEM_POOL应配合合理的值,若是并发比较大时,建议SYSTEM_POOL保持默认或者稍微再大一点。目前个人经验配置是SYSTEM_POOL为1/3 * Xmx(虽然咱们并发较多,可是依然调低了此值);RESERVED_POOL 为 1/9 * XMX。3d
固然你能够经过HTTP请求查看每台Worker的/v1/status,来预估具体须要配置多大的内存,如图所示,显示了各内存池的使用量。rest
http://10.84.99.3:8080/v1/status
{ "nodeId": "10-84-99-3", "nodeVersion": { "version": "2bcd31d-dirty" }, "environment": "product", "coordinator": true, "uptime": "13.23d", "externalAddress": "10.84.99.53", "internalAddress": "10.84.99.53", "memoryInfo": { "totalNodeMemory": "35433480192B", "pools": { "reserved": { "maxBytes": 10737418240, "reservedBytes": 0, "reservedRevocableBytes": 0, "queryMemoryReservations": { }, "queryMemoryRevocableReservations": { }, "freeBytes": 10737418240 }, "general": { "maxBytes": 24696061952, "reservedBytes": 0, "reservedRevocableBytes": 0, "queryMemoryReservations": { }, "queryMemoryRevocableReservations": { }, "freeBytes": 24696061952 }, "system": { "maxBytes": 16106127360, "reservedBytes": 0, "reservedRevocableBytes": 0, "queryMemoryReservations": { }, "queryMemoryRevocableReservations": { }, "freeBytes": 16106127360 } } }, "processors": 40, "processCpuLoad": 0.0004106776180698152, "systemCpuLoad": 0.00041050903119868636, "heapUsed": 9741942512, "heapAvailable": 51539607552, "nonHeapUsed": 264888168 }
GENERAL_POOL每次内存申请时,都会判断内存使用量是否超过了最大内存,若是超过了就报错,错误为“Query exceeded local memory limit of x”,这保护了Presto会无限申请内存,只会致使当前查询出错。同时,若是该节点的GENERAL_POOL可以使用内存以及可回收内存为0,那么认为该node为Block node。code
RESERVED_POOL能够认为是查询最大的SQL,其能知足GENERAL_POOL的内存限制策略,那么确定会知足RESERVED_POOL的策略(复用了GENERAL_POOL策略)。内存
RESERVED_POOL目前版本未发现能够限制内存,因此当并发很是高,且scan的数据很是大时,有低几率会引发OOM问题。可是配合Resource Group,内存设置合理,也基本会避免OOM问题。开发
同时知足如下两点时,Presto便认为集群超出要求的内存了:
当判断出集群超出CLuster Memory时,有两种方式管理内存: 一、挨个遍历每一个查询,判断当前查询占用的总内存是否超过了query.max-memory(config.properties里配置),若是超过了,那么该查询就被failed。 二、若是query.max-memory配置的不合理,值很是大,那么可能过了5秒(默认时间)依然不知足第一种情形,那么将会使用第二种方法管理查询。第二种管理方法又分为两种小的管理,根据LowMemoryKillerPolicy来决定Kill查询策略,其分为total-reservation和total-reservation-on-blocked-nodes。配置total-reservation的做用是kill掉全部查询里最费内存的查询;而total-reservation-on-blocked-nodes杀死在内存不足(阻塞)的节点上使用最多内存的查询。