数据库中的资源管理允许管理员对资源进行控制,并为会话分配一个优先级,确保最关键的事务得到系统资源的重要份额。分布式环境中的资源管理使数据的可及性更强,并通过自主计算机网络(即分布式系统)管理资源。分布式系统中的资源管理的基础也是资源共享。
PrestoDB是一个分布式查询引擎,由Facebook编写,作为Hive的继承者,用于高度可扩展地处理大量数据。为Hadoop生态系统编写的PrestoDB可以扩展到数万个节点并处理PB级的数据。为了能在生产规模上使用,PrestoDB可以为多个用户提供数以千计的查询,而不会面临瓶颈和 "嘈杂的邻居 "问题。PrestoDB利用资源组来组织不同工作负载的优先次序。这篇文章讨论了PrestoDB引入资源组的一些范式,以及在建立资源组的生产系统之前需要考虑的最佳实践和注意事项。
入门
Presto有多种 "资源",它可以管理资源配额。两个主要的资源是CPU和内存。此外,还有一些细化的资源限制可以指定,如并发性、时间和cpuTime。所有这些都是通过一个非常难看的 JSON配置文件完成的,下面的例子来自PrestoDB的文档页面。
{
"rootGroups": [
{
"name": "global",
"softMemoryLimit": "80%",
"hardConcurrencyLimit": 100,
"maxQueued": 1000,
"schedulingPolicy": "weighted",
"jmxExport": true,
"subGroups": [
{
"name": "data_definition",
"softMemoryLimit": "10%",
"hardConcurrencyLimit": 5,
"maxQueued": 100,
"schedulingWeight": 1
},
{
"name": "adhoc",
"softMemoryLimit": "10%",
"hardConcurrencyLimit": 50,
"maxQueued": 1,
"schedulingWeight": 10,
"subGroups": [
{
"name": "other",
"softMemoryLimit": "10%",
"hardConcurrencyLimit": 2,
"maxQueued": 1,
"schedulingWeight": 10,
"schedulingPolicy": "weighted_fair",
"subGroups": [
{
"name": "${USER}",
"softMemoryLimit": "10%",
"hardConcurrencyLimit": 1,
"maxQueued": 100
}
]
},
{
"name": "bi-${tool_name}",
"softMemoryLimit": "10%",
"hardConcurrencyLimit": 10,
"maxQueued": 100,
"schedulingWeight": 10,
"schedulingPolicy": "weighted_fair",
"subGroups": [
{
"name": "${USER}",
"softMemoryLimit": "10%",
"hardConcurrencyLimit": 3,
"maxQueued": 10
}
]
}
]
},
{
"name": "pipeline",
"softMemoryLimit": "80%",
"hardConcurrencyLimit": 45,
"maxQueued": 100,
"schedulingWeight": 1,
"jmxExport": true,
"subGroups": [
{
"name": "pipeline_${USER}",
"softMemoryLimit": "50%",
"hardConcurrencyLimit": 5,
"maxQueued": 100
}
]
}
]
},
{
"name": "admin",
"softMemoryLimit": "100%",
"hardConcurrencyLimit": 50,
"maxQueued": 100,
"schedulingPolicy": "query_priority",
"jmxExport": true
}
],
"selectors": [
{
"user": "bob",
"group": "admin"
},
{
"source": ".*pipeline.*",
"queryType": "DATA_DEFINITION",
"group": "global.data_definition"
},
{
"source": ".*pipeline.*",
"group": "global.pipeline.pipeline_${USER}"
},
{
"source": "jdbc#(?<tool_name>.*)",
"clientTags": ["hipri"],
"group": "global.adhoc.bi-${tool_name}.${USER}"
},
{
"group": "global.adhoc.other.${USER}"
}
],
"cpuQuotaPeriod": "1h"
}
好吧,这里显然有很多事情要做,所以让我们从基本的开始,然后再向上滚动。首先要了解的是Presto用来执行查询资源限制的机制。
惩罚措施
Presto并没有在执行时强制执行任何资源。相反,Presto为超出其资源规格的用户引入了一个 "惩罚 "的概念。例如,如果用户 "bob "启动了一个巨大的查询,最终占用的CPU时间远远超过分配的时间,那么 "bob "就会招致惩罚,即 "bob "的查询将被迫在排队状态下等待,直到他们可以再次运行。为了了解这种情况,让我们把集群资源分成两半,看看当两个用户同时试图提交5个不同的查询时会发生什么。
资源组规范
下面的例子是关于如何在两个不同的用户之间平均分配CPU资源的资源规范。
{
"rootGroups": [
{
"name": "query1",
"softMemoryLimit": "50%",
"hardConcurrencyLimit": 1,
"maxQueued": 5,
"schedulingPolicy": "fair",
"jmxExport": true
},
{
"name": "query2",
"softMemoryLimit": "50%",
"hardConcurrencyLimit": 1,
"maxQueued": 5,
"schedulingPolicy": "fair",
"jmxExport": true
}
],
"selectors": [
{
"user": "alice",
"group": "query1"
},
{
"user": "bob",
"group": "query2"
}
],
"cpuQuotaPeriod": "1h"
}
上面的资源配置定义了两个主要的资源组,称为 "query1 "和 "query2"。这些组将作为不同查询/用户的桶。这里有几个参数在起作用。
- hardConcurrencyLimit设置组内可运行的并发查询数量
- maxQueued设定了可以排队的查询数量的限制
- schedulingPolicy 'fair'决定了同一组内的查询如何被优先排序
作为每个用户启动一个查询没有任何影响,但随后的查询将保持QUEUED直到第一个完成。这至少证实了hardConcurrencyLimit的设置。测试排队的6个查询也表明,maxQueued正在按预期工作。
{
"rootGroups": [
{
"name": "query1",
"softMemoryLimit": "50%",
"hardConcurrencyLimit": 1,
"maxQueued": 1,
"softCpuLimit": "30s",
"schedulingPolicy": "fair",
"jmxExport": true
},
{
"name": "query2",
"softMemoryLimit": "50%",
"hardConcurrencyLimit": 1,
"maxQueued": 1,
"softCpuLimit": "30s",
"schedulingPolicy": "fair",
"jmxExport": true
}
],
"selectors": [
{
"user": "alice",
"group": "query1"
},
{
"user": "bob",
"group": "query2"
}
],
"cpuQuotaPeriod": "1m"
}
引入软CPU限制将惩罚任何在给定的CPU周期内被发现使用过多CPU时间的查询。目前,这被设置为1分钟,每组被给予该CPU时间的一半。然而,测试上述配置产生了一些奇怪的结果。主要是,一旦第一个查询完成,其他的查询就会排队很长的时间。看一下Presto的源代码就知道了其中的原因。softCpuLimit和hardCpuLimit是基于总核数和cpuQuotaPeriod的组合。例如,在一个有r5.2xlarge实例的10个节点集群中,每个Presto工作节点有8个vCPU。这导致工人总共有80个vCPU,然后在给定的cpuQuotaPeriod中导致80m的vCPUminutes。因此,正确的值如下所示。
{
"rootGroups": [
{
"name": "query1",
"softMemoryLimit": "50%",
"hardConcurrencyLimit": 1,
"maxQueued": 1,
"softCpuLimit": "40m",
"schedulingPolicy": "fair",
"jmxExport": true
},
{
"name": "query2",
"softMemoryLimit": "50%",
"hardConcurrencyLimit": 1,
"maxQueued": 1,
"softCpuLimit": "40m",
"schedulingPolicy": "fair",
"jmxExport": true
}
],
"selectors": [
{
"user": "alice",
"group": "query1"
},
{
"user": "bob",
"group": "query2"
}
],
"cpuQuotaPeriod": "1m"
}
通过测试,上述资源组规格导致两个查询完成--共使用了127m的CPU时间。所有进一步的查询在再次运行前都会阻塞2分钟左右。这个阻塞的时间增加了,因为,每一分钟的cpuQuotaPeriod,每个用户被授予40分钟的惩罚。由于第一分钟的查询超过了80多分钟,所以需要2个cpuQuotaPeriod来使惩罚恢复到零,以便查询可以再次提交。
总结
Presto的资源组实施肯定有一些改进的空间。最明显的是,对于那些在执行前可能不了解其查询成本的临时用户,资源组将严重惩罚他们,直到他们提交非常低的成本查询。然而,这种解决方案将最大限度地减少单个用户在长时间内对集群造成的损害,并且从长远来看,将达到平均水平。总的来说,资源组更适合于依赖于可变输入数据的预定工作负载。一个指定的预定工作不会任意地最终占用一大块资源。对于多个用户/团队之间的资源划分,最好的方法似乎仍然是运行和维护多个隔离的Presto集群。
CPU时间 数据库 数据(计算) Presto(SQL查询引擎) 分布式数据库