Greenplum是一款广泛应用的开源MPP数据库的产品,兼容PostgreSQL生态,被广泛应用与大数据的存储与分析。
Resource Group是Greenplum的一种资源管理方式。Resource Group由GP5开始被支持,相比于早期的Resource Queue的资源管理方式,具有支持更细粒度的资源管理、 支持组内资源共享等优点。本文会介绍Resource Group的使用方式,从源码角度分析它的实现,以及介绍一些使用中遇到的高频问题。
Resource Group简介
Resource Group是Greenplum的一种资源管理方式,能够细粒度定义对不同数据库角色(用户)的资源使用限制,支持通过SQL语句的方式进行配置。Resource Group支持进行三种类型的资源限制:并发限制、CPU限制、内存限制。
超级用户可以通过SQL在数据库内定义多个资源组,并设置每个资源组的资源限制。在一个数据库中,每个资源组可以关联一个或多个数据库用户,而每个数据库用户只能归属于单个资源组。资源组支持的资源限制配置如下:
| CONCURRENCY | 资源组中允许的最大并发事务数,包括活动和空闲事务。 |
| CPU_RATE_LIMIT | 此资源组可用的CPU资源百分比。 |
| CPUSET | 为该资源组保留的CPU核心数。 |
| MEMORY_LIMIT | 该资源组可用的内存资源百分比。 |
| MEMORY_SHARED_QUOTA | 提交到该资源组的事务之间共享的内存资源百分比。 |
| MEMORY_SPILL_RATIO | 内存密集型事务的内存使用阈值。当事务达到此阈值时,它将溢出到磁盘。 |
CONCURRENCY代表最大并发事务数;CPU_RATE_LIMIT代表以百分比形式限制CPU使用,各个资源组设置的CPU_RATE_LIMIT之和不超过100%;CPUSET代表以CPU核的形式限制CPU,设置具体使用哪几个CPU逻辑核;MEMORY_LIMIT代表资源组最多能占用的集群可用内存的百分比,由MEMORY_LIMIT设定的部分属于资源组的固定份额,而各个资源组MEMORY_LIMIT之和低于100%的部分,被划分所有资源组的共享内存池;MEMORY_SHARED_QUOTA代表资源组内用于各个事务共享的内存比例;MEMORY_SPILL_RATIO代表溢出到磁盘的内存使用阈值。
CREATE RESOURCE GROUP rgroup1 WITH (CONCURRENCY=10, CPU_RATE_LIMIT=20, MEMORY_LIMIT=25, MEMORY_SHARED_QUOTA=20, MEMORY_SPILL_RATIO=20);除了Resource Group,GP早期还支持Resource Queue的资源管理方式, Resource Queue通过指定MEMORY_LIMIT、ACTIVE_STATEMENTS、PRIORITY、MAX_COST字段,配置对于各个资源队列的内存、CPU、并发限制,Resource Group相对Resource queue存在以下优势:
| 特性 | Resource Groups | Resource Queues |
| CPU管理 | 细粒度(Cgroup) | 基于粗粒度的优先级 |
| 内存限制 | 细粒度 | 粗粒度 |
| 并发控制 | 事务级别 | 语句级别 |
| 管理DDL、Utility语句 | 是 | 否 |
| segment级别监控 | 是 | 否 |
| 组内内存共享 | 是 | 否 |
Resource Group通过Master上的并发锁实现对并发的限制,通过cgroup实现对cpu的限制,支持对内存基于vmtracker和cgroup进行两种方式的限制。
Resource Group使用
创建资源组
使用CREATE RESOURCE GROUP命令创建新资源组。
为角色创建资源组时,必须提供CPU_RATE_LIMIT或CPUSET和MEMORY_LIMIT限制值。这些限制标识要分配给此资源组的数据库资源的百分比。例如,要创建名为rgroup1的资源组,其CPU限制为20,内存限制为25:
CREATE RESOURCE GROUP rgroup1 WITH (CPU_RATE_LIMIT=20, MEMORY_LIMIT=25);CPU限制为20由rgroup1分配到的每个角色共享。同样,内存限制为25由rgroup1分配到的每个角色共享。rgroup1使用默认的MEMORY_AUDITOR vmtracker和默认的CONCURRENCY设置为20。
数据库包含两个默认资源组:admin_group和default_group。启用资源组时,将为未明确分配资源组的任何角色分配角色功能的默认组。SUPERUSER角色分配了admin_group,非管理员角色分配了名为default_group的组。
使用以下资源限制创建默认资源组admin_group和default_group:
| 限制类型 | admin_group | default_group |
| CONCURRENCY | 10 | 20 |
| CPU_RATE_LIMIT | 10 | 30 |
| CPUSET | -1 | -1 |
| MEMORY_LIMIT | 10 | 30 |
| MEMORY_SHARED_QUOTA | 50 | 50 |
| MEMORY_SPILL_RATIO | 20 | 20 |
| MEMORY_AUDITOR | vmtracker | vmtracker |
默认资源组admin_group和default_group的CPU_RATE_LIMIT和MEMORY_LIMIT值对分段主机上的总百分比有贡献。
将资源组分配给角色
Greenplum资源组可用于分配给一个或多个角色(用户)。使用CREATE ROLE或ALTER ROLE命令的RESOURCE GROUP子句将资源组分配给数据库角色。如果未为角色指定资源组,则会为角色分配角色功能的默认组。SUPERUSER角色分配了admin_group,非管理员角色分配了名为default_group的组。
使用ALTER ROLE或CREATE ROLE命令将资源组分配给角色。例如:
ALTER ROLE bill RESOURCE GROUP rg_light;
CREATE ROLE mary RESOURCE GROUP exec;用户可以将资源组分配给一个或多个角色。如果已定义角色层次结构,则将资源组分配给父角色不会向下传播到该角色组的成员。
如果要从角色中删除资源组分配并将角色分配为默认组,请将角色的组名称分配更改为NONE。例如:
ALTER ROLE mary RESOURCE GROUP NONE;
修改资源组配置
ALTER RESOURCE GROUP 命令可以修改资源组配置。要更改资源组的限制,请指定组所需的新值。例如:
ALTER RESOURCE GROUP rg_role_light SET CONCURRENCY 7;
ALTER RESOURCE GROUP exec SET MEMORY_LIMIT 25;
ALTER RESOURCE GROUP rgroup1 SET CPUSET '2,4';注意: 用户无法将admin_group的CONCURRENCY值设置或更改为0。
删除资源组
DROP RESOURCE GROUP命令会删除资源组。要删除角色的资源组,不能将该组分配给任何角色,也不能在资源组中激活或等待任何事务。
DROP RESOURCE GROUP exec;
取消资源组中的正在运行或已排队的事务
在某些情况下,用户可能希望取消资源组中的正在运行或排队的事务。例如,用户可能希望删除在资源组队列中等待但尚未执行的查询。或者,用户可能希望停止执行时间太长的正在运行的查询,或者在事务中处于空闲状态并占用其他用户所需的资源组事务插槽的查询。
要取消正在运行或排队的事务,必须首先确定与事务关联的进程ID(pid)。获得进程ID后,可以调用pg_cancel_backend()来结束该进程。
例如,要查看与当前活动或在所有资源组中等待的所有语句关联的进程信息,请运行以下查询。如果查询未返回任何结果,则任何资源组中都没有正在运行或排队的事务。
SELECT rolname, g.rsgname, procpid, waiting, current_query, datname
FROM pg_roles, gp_toolkit.gp_resgroup_status g, pg_stat_activity
WHERE pg_roles.rolresgroup=g.groupid
AND pg_stat_activity.usename=pg_roles.rolname;
示例部分查询输出:
rolname | rsgname | procpid | waiting | current_query | datname
---------+----------+---------+---------+-----------------------+---------
sammy | rg_light | 31861 | f | <IDLE> in transaction | testdb
billy | rg_light | 31905 | t | SELECT * FROM topten; | testdb使用此输出来标识要取消的事务的进程ID(procpid),然后取消该进程。例如,要取消上面示例输出中标识的待处理查询:
SELECT pg_cancel_backend(31905);用户可以在pg_cancel_backend()的第二个参数中提供可选消息,以向用户指示进程被取消的原因。
Resource Group监控
本节主要介绍如何通过系统视图监控集群的资源组,包括各个资源组的配置,各个资源组的资源使用情况等。
以下是资源组相关的监控项的含义:
rsgname | 资源组名 |
groupid | 资源组oid |
cpu | 当前资源组的cpu利用率 |
| memory_used | 当前资源组实际使用的内存总量 |
| memory_available | 当前资源组可用的内存总量 |
| memory_quota_used | 当前资源组固定份额部分实际使用的内存总量 |
| memory_quota_available | 当前资源组固定份额部分可用的内存总量 |
| memory_quota_granted | 当前资源组固定份额部分的总体分配量 |
| memory_shared_used | 当前资源组共享部分实际使用的内存总量 |
| memory_shared_available | 当前资源组共享部分可用的内存总量 |
| memory_shared_granted | 当前资源组共享部分的总体分配量 |
正常情况下,各个资源组监控项有以下关系:
memory_used = memory_quota_used + memory_shared_used
available = memory_quota_granted + memory_shared_granted - memory_used
memory_quota_granted = memory_quota_used + memory_quota_available
memory_shared_granted = memory_shared_used + memory_shared_available
查看资源组限制
gp_resgroup_config 系统视图显示所有的资源组配置。
SELECT * FROM gp_toolkit.gp_resgroup_config;
查看资源组查询状态和CPU/内存使用情况
gp_resgroup_status 系统视图可以查看资源组的状态和活动。该视图显示正在运行和排队的事务数。它还显示资源组的实时CPU和内存使用情况。
SELECT * FROM gp_toolkit.gp_resgroup_status;
查看每个主机的资源组CPU/内存使用情况
通过gp_resgroup_status_per_host 系统视图,用户可以基于每个主机查看资源组的实时CPU和内存使用情况。
SELECT * FROM gp_toolkit.gp_resgroup_status_per_host;
查看每个segment的资源组CPU/内存使用情况
通过gp_resgroup_status_per_segment 系统视图,用户可以按每个segment,每个主机查看资源组的实时CPU和内存使用情况。
SELECT * FROM gp_toolkit.gp_resgroup_status_per_segment;
查看分配给角色的资源组
要查看资源组到角色的分配,请对 pg_roles 和 pg_resgroup系统表执行以下查询:
SELECT rolname, rsgname FROM pg_roles, pg_resgroup
WHERE pg_roles.rolresgroup=pg_resgroup.oid;
查看资源组的运行和待定查询
要查看资源组的运行查询,挂起的查询以及挂起的查询排队的时间,请检查pg_stat_activity系统表:
SELECT current_query, waiting, rsgname, rsgqueueduration
FROM pg_stat_activity;
Resource Group实现分析
Greenplum数据库是MPP架构,整体分为一个或多个Master,以及多个segment,数据在多个segment之间可以随机、哈希、复制分布。在Greenplum中,Resource Group的资源限制级别是事务级别。Resource Group会在各个group内部将资源划分成并发量数目的slot,而每个事务在运行前排队等待获取slot。

如上文所述,Resource Group支持对并发、CPU和内存进行限制,本节会详细介绍对这几类资源进行限制的实现细节,以及介绍对于资源组的监控是如何实现的。
Resource Group的代码主要位于如下的路径和文件中:
src/backend/utils/resgroup
src/backend/utils/ressource_manager
src/backend/commands/resgroupcmds.c
并发限制
Resource Group通过Master上的并发锁实现对并发的限制。用户连接到数据库集群时,首先会连接到Master节点,在开启事务时,会尝试获取slot。
static ResGroupSlotData *
groupGetSlot(ResGroupData *group)
{
ResGroupSlotData *slot;
ResGroupCaps *caps;
int32 slotMemQuota;
Assert(LWLockHeldExclusiveByMe(ResGroupLock));
Assert(Gp_role == GP_ROLE_DISPATCH);
Assert(groupIsNotDropped(group));
caps = &group->caps;
/* First check if the concurrency limit is reached */
if (group->nRunning >= caps->concurrency)
return NULL;
slotMemQuota = groupReserveMemQuota(group);
if (slotMemQuota < 0)
return NULL;
/* Now actually get a free slot */
slot = slotpoolAllocSlot();
Assert(!slotIsInUse(slot));
initSlot(slot, group, slotMemQuota);
group->nRunning++;
return slot;
}Master上通过比较资源组当前运行的事务数group->nRunning,与设定的并发数concurrency,保证限制实际运行的并发数不高于设定值。Greenplum是多进程模型,各个资源组的计数量group->nRunning放在共享内存中,在slot的获取过程中,会获取ResGroupLock类型的排他锁,以保证并发安全。
CPU限制
Resource Group通过cgroup实现对cpu的限制。在resource group创建或者修改时(比如initCpu),数据库会在操作系统cgroup路径下,创建与resource group oid同名的cgroup路径,即做对应挂载。并根据设置的CPU配置,更新对应cgroup子路径下的cpu.cfs_period_us、cpu.cfs_quota_us、cpu.shares文件。
而在开始事务时,数据库会将当前进程关联到对应的cgroup子节点下,具体逻辑如下:
void
ResGroupOps_AssignGroup(Oid group, ResGroupCaps *caps, int pid)
{
bool oldViaCpuset = oldCaps.cpuRateLimit == CPU_RATE_LIMIT_DISABLED;
bool curViaCpuset = caps ? caps->cpuRateLimit == CPU_RATE_LIMIT_DISABLED : false;
/* needn't write to file if the pid has already been written in.
* Unless it has not been writtien or the group has changed or
* cpu control mechanism has changed */
if (IsUnderPostmaster &&
group == currentGroupIdInCGroup &&
caps != NULL &&
oldViaCpuset == curViaCpuset)
return;
writeInt64(group, BASETYPE_GPDB, RESGROUP_COMP_TYPE_CPU,
"cgroup.procs", pid);
writeInt64(group, BASETYPE_GPDB, RESGROUP_COMP_TYPE_CPUACCT,
"cgroup.procs", pid);
if (gp_resource_group_enable_cgroup_cpuset)
{
if (caps == NULL || !curViaCpuset)
{
/* add pid to default group */
writeInt64(DEFAULT_CPUSET_GROUP_ID, BASETYPE_GPDB,
RESGROUP_COMP_TYPE_CPUSET, "cgroup.procs", pid);
}
else
{
writeInt64(group, BASETYPE_GPDB,
RESGROUP_COMP_TYPE_CPUSET, "cgroup.procs", pid);
}
}
/*
* Do not assign the process to cgroup/memory for now.
*/
currentGroupIdInCGroup = group;
if (caps != NULL)
{
oldCaps.cpuRateLimit = caps->cpuRateLimit;
StrNCpy(oldCaps.cpuset, caps->cpuset, sizeof(oldCaps.cpuset));
}
}数据库会将对应进程的pid写进子路径的cgroup.procs文件里,从而利用操作系统的cgroup能力对进程的cpu使用进行限制。
内存限制
支持对内存基于vmtracker和cgroup进行两种方式的限制。在使用cgroup做内存限制的时候,它的应用原理与CPU限制类似:为每个资源组创建一个cgroup子节点,在资源组创建和修改时,修改对应cgroup的内存管理配置文件memory.limit_in_bytes、memory.usage_in_bytes;事务运行时通过pid与对应cgroup关联,支持内存的限制。
而基于vmtracker进行内存限制时,则完全是由数据库本身进行高并发场景下的内存审计和分配,这里以内存统计量的修改为例:
static int32
groupIncMemUsage(ResGroupData *group, ResGroupSlotData *slot, int32 chunks)
{
int32 slotMemUsage; /* the memory current slot has been used */
int32 sharedMemUsage; /* the total shared memory usage,
sum of group share and global share */
int32 globalOveruse = 0; /* the total over used chunks of global share*/
/* Add the chunks to memUsage in slot */
slotMemUsage = pg_atomic_add_fetch_u32((pg_atomic_uint32 *) &slot->memUsage,
chunks);
/* Check whether shared memory should be added */
sharedMemUsage = slotMemUsage - slot->memQuota;
if (sharedMemUsage > 0)
{
/* Decide how many chunks should be counted as shared memory */
int32 deltaSharedMemUsage = Min(sharedMemUsage, chunks);
/* Add these chunks to memSharedUsage in group,
* and record the old value*/
int32 oldSharedUsage = pg_atomic_fetch_add_u32((pg_atomic_uint32 *)
&group->memSharedUsage,
deltaSharedMemUsage);
/* the free space of group share */
int32 oldSharedFree = Max(0, group->memSharedGranted - oldSharedUsage);
/* Calculate the global over used chunks */
int32 deltaGlobalSharedMemUsage = Max(0, deltaSharedMemUsage - oldSharedFree);
/* freeChunks -= deltaGlobalSharedMemUsage and get the new value */
int32 newFreeChunks = pg_atomic_sub_fetch_u32((pg_atomic_uint32 *)
&pResGroupControl->freeChunks,
deltaGlobalSharedMemUsage);
/* calculate the total over used chunks of global share */
globalOveruse = Max(0, 0 - newFreeChunks);
}
/* Add the chunks to memUsage in group */
pg_atomic_add_fetch_u32((pg_atomic_uint32 *) &group->memUsage,
chunks);
return globalOveruse;
}在进行内存限制时,对于某个slot的内存请求,首先会通过原子相加的方式从资源组的固定份额部分获取内存;而如果所需要的内存超过固定份额的内存量,会尝试从资源组内的共享内存部分获取;如果依然无法获取到内存,则会尝试从全局的共享内存获取。如果从全局共享内存依然无法获取到内存,则会返回Out of Memory错误。
Resource Group监控
在进行监控时,QD会把资源组查询query分发到各个segment上,然后再在master进行汇总,返回集群整体的资源利用情况。
Greenplum支持多种对资源组的监控方式,除了直接的资源组监控视图之外,还支持对segment、机器层级的资源组监控。这些不同层级的资源组监控视图往往是通过对系统表和系统函数进行联合查询得到的,比如:
CREATE VIEW gp_toolkit.gp_resgroup_status AS
SELECT r.rsgname, s.*
FROM pg_resgroup_get_status(null) AS s,
pg_resgroup AS r
WHERE s.groupid = r.oid;所以,我们介绍最基本的资源监控函数pg_resgroup_get_status的实现方式。
static void
getResUsage(ResGroupStatCtx *ctx, Oid inGroupId)
{
int64 *usages;
TimestampTz *timestamps;
int i, j;
usages = palloc(sizeof(*usages) * ctx->nGroups);
timestamps = palloc(sizeof(*timestamps) * ctx->nGroups);
for (j = 0; j < ctx->nGroups; j++)
{
ResGroupStat *row = &ctx->groups[j];
Oid groupId = DatumGetObjectId(row->groupId);
usages[j] = ResGroupOps_GetCpuUsage(groupId);
timestamps[j] = GetCurrentTimestamp();
}
if (Gp_role == GP_ROLE_DISPATCH)
{
CdbPgResults cdb_pgresults = {NULL, 0};
StringInfoData buffer;
initStringInfo(&buffer);
appendStringInfo(&buffer,
"SELECT groupid, cpu_usage, memory_usage "
"FROM pg_resgroup_get_status(%d)",
inGroupId);
CdbDispatchCommand(buffer.data, DF_WITH_SNAPSHOT, &cdb_pgresults);
if (cdb_pgresults.numResults == 0)
elog(ERROR, "pg_resgroup_get_status() didn't get back any resource statistic from the segDBs");
for (i = 0; i < cdb_pgresults.numResults; i++)
{
struct pg_result *pg_result = cdb_pgresults.pg_results[i];
/*
* Any error here should have propagated into errbuf, so we shouldn't
* ever see anything other that tuples_ok here. But, check to be
* sure.
*/
if (PQresultStatus(pg_result) != PGRES_TUPLES_OK)
{
cdbdisp_clearCdbPgResults(&cdb_pgresults);
elog(ERROR, "pg_resgroup_get_status(): resultStatus not tuples_Ok");
}
Assert(PQntuples(pg_result) == ctx->nGroups);
for (j = 0; j < ctx->nGroups; j++)
{
const char *result;
ResGroupStat *row = &ctx->groups[j];
Oid groupId = pg_atoi(PQgetvalue(pg_result, j, 0),
sizeof(Oid), 0);
Assert(groupId == row->groupId);
if (row->memUsage->len == 0)
{
Datum d = ResGroupGetStat(groupId, RES_GROUP_STAT_MEM_USAGE);
row->groupId = groupId;
appendStringInfo(row->memUsage, "{\"%d\":%s",
GpIdentity.segindex, DatumGetCString(d));
appendStringInfo(row->cpuUsage, "{");
calcCpuUsage(row->cpuUsage, usages[j], timestamps[j],
ResGroupOps_GetCpuUsage(groupId),
GetCurrentTimestamp());
}
result = PQgetvalue(pg_result, j, 1);
appendStringInfo(row->cpuUsage, ", %s", result);
result = PQgetvalue(pg_result, j, 2);
appendStringInfo(row->memUsage, ", %s", result);
if (i == cdb_pgresults.numResults - 1)
{
appendStringInfoChar(row->cpuUsage, '}');
appendStringInfoChar(row->memUsage, '}');
}
}
}
cdbdisp_clearCdbPgResults(&cdb_pgresults);
}
else
{
pg_usleep(300000);
for (j = 0; j < ctx->nGroups; j++)
{
ResGroupStat *row = &ctx->groups[j];
Oid groupId = DatumGetObjectId(row->groupId);
Datum d = ResGroupGetStat(groupId, RES_GROUP_STAT_MEM_USAGE);
appendStringInfo(row->memUsage, "\"%d\":%s",
GpIdentity.segindex, DatumGetCString(d));
calcCpuUsage(row->cpuUsage, usages[j], timestamps[j],
ResGroupOps_GetCpuUsage(groupId),
GetCurrentTimestamp());
}
}
}结合这段代码我们看到,Master节点(Gp_role == GP_ROLE_DISPATCH)在接收到查询资源状态的SQL之后,首先会将一个相同的状态查询SQL(SELECT groupid, cpu_usage, memory_usage FROM pg_resgroup_get_status),分发给所有的segment节点。Master节点收到各个节点出来的结果之后,会进行排序汇总,然后返回最终结果。
而在各个节点真实计算的时候,对于内存消耗的计算,会返回实时统计的内存统计结果。
而对于cpu的计算,会在一开始先调用ResGroupOps_GetCpuUsage计算一次cpu使用量,通过读取磁盘上cgroup对应节点的cpu统计结果。然后sleep 300000 us,重新调用ResGroupOps_GetCpuUsage再计算一次cpu使用量,通过两次结果的差值返回cpu的统计结果。
static void
calcCpuUsage(StringInfoData *str,
int64 usageBegin, TimestampTz timestampBegin,
int64 usageEnd, TimestampTz timestampEnd)
{
int64 duration;
long secs;
int usecs;
int64 usage;
usage = usageEnd - usageBegin;
TimestampDifference(timestampBegin, timestampEnd, &secs, &usecs);
duration = secs * 1000000 + usecs;
appendStringInfo(str, "\"%d\":%.2f",
GpIdentity.segindex,
ResGroupOps_ConvertCpuUsageToPercent(usage, duration));
}
常见问题
1、Resource Group是如何利用操作系统的cgroup能力的?
答:对于每个资源组,数据库会在系统的cgroup路径下创建一个以资源组oid命名的子路径,即做对应的cgroup挂载。创建资源组或者修改资源组配置的时候,数据库会对应修改对应子节点的cgroup配置。实际执行事务时,数据库会将对应进程的pid写入对应的cgroup路径下,从而纳入cgroup的限制中。
2、Resource Group是如何进行事务级别的资源限制的?
答:数据库对于每个资源组,根据并发限制,将资源划分成多个slot。
对于每个事务,在开启事务(startTransaction)的时候,QD都会尝试获取一个slot。如果获取不到就会一直等待其他事务完成执行并释放slot。
3、Resource Group的各种操作加不加锁,加什么锁?
资源组的创建和修改都会加ResGroupLock类型的exclusive lock,而backend 获取slot来执行事务的时候也会获取ResGroupLock的exclusive lock,所以资源组相关的变更,会与对应资源组内的事务执行相互阻塞。
另外,Resource Group配置修改的时候,还会对相关系统表加AccessExclusiveLock。
4、数据库基于cgroup对资源组进行限制,如果某个数据库节点故障,跨机迁移到其他节点,而cgroup的配置和路径结构无法迁移,会不会导致对应机器上的resource group功能失效?
答:不会。在数据库节点初始化的时候(initPostgres),会进行资源组的检测,如果系统表中的资源组配置在cgroup路径中不存在,会重新创建对应的cgroup挂载。
同理,如果你配置好了ResourceGroup之后,直接把你机器上的对应oid的cgroup子路径删掉,重启下数据库就会恢复正常的状态。
5、为什么资源组的CPU使用率高于配置的CPU_RATE_LIMIT?
答:资源组可能存在CPU抢占的情况:当其他资源组空闲时,忙碌的资源组可以使用比其CPU_RATE_LIMIT更多的CPU。在这种情况下,数据库将空闲资源组的CPU资源分配给更繁忙的资源组。
6、Resource Group在部署上有什么要求?
答:1)安装并开启cgroup;2)cgroup 做好初始化挂载,初始化配置举例:
group gpdb {
perm {
task {
uid = gpadmin;
gid = gpadmin;
}
admin {
uid = gpadmin;
gid = gpadmin;
}
}
cpu {
}
cpuacct {
}
cpuset {
}
memory {
}
}
实际使用时要根据自身情况(系统用户名)修改配置;
3)cpu和cpuset对应的路径必须分开挂载。
总结
资源管理对于数据库集群的多租户管理、资源细粒度分配具有很重要的价值。Resource Group巧妙地基于操作系统的cgroup隔离能力和PostgreSQL本身的基于MemoryContext的内存管理能力,以很少的代码量实现了完备的资源管理功能。在GP5之后,Resource Group主键替代Resource Queue成为主流的资源管理方式,Greenplum社区也把Resource Group当做主要去维护和优化的资源管理方式。
对Greenplum内存管理感兴趣的话,可以参考一下文章:基于MemoryContext的内存管理
对另外一种资源管理方式感兴趣的话,可以参考下文章:Greenplum资源管理——Resource Queue使用和实现分析