一, 资源队列
在 Greenplum 的 4.x 版本之后, 加入了资源队列的概念, 其主要作用就是限制用户或者单个 SQL 对资源的消耗. 避免出现消耗过多资源, 影响其他用户或者 SQL 计算. 这里的资源限制主要是指系统内存资源.
二, 使用方法
2.1 创建队列
- Command: CREATE RESOURCE QUEUE
- Description: create a new resource queue for workload management
- Syntax:
- CREATE RESOURCE QUEUE name WITH (queue_attribute=value [, ... ])
- where queue_attribute is:
- ACTIVE_STATEMENTS=integer
- [ MAX_COST=float [COST_OVERCOMMIT={
- TRUE|FALSE
- }] ]
- [ MIN_COST=float ]
- [ PRIORITY={
- MIN|LOW|MEDIUM|HIGH|MAX
- } ]
- [ MEMORY_LIMIT='memory_units' ]
- | MAX_COST=float [ COST_OVERCOMMIT={
- TRUE|FALSE
- } ]
- [ ACTIVE_STATEMENTS=integer ]
- [ MIN_COST=float ]
- [ PRIORITY={
- MIN|LOW|MEDIUM|HIGH|MAX
- } ]
- [ MEMORY_LIMIT='memory_units' ]
参数说明:
a)ACTIVE_STATEMENTS: 指运行同时运行的 SQL 数量, 超过该数量的请求将会排队等待, 默认为 - 1, 表示不受限制.
b)MAX_COST: 该队列允许运行的单个 SQL 最大 COST 值, 默认为 - 1, 表示不受限制..
c)MIN_COST: 低于该 COST 值的 SQL 直接运行, 不受队列资源的限制, 默认为 0.
d)PRIORITY: 队列中任务分配 CPU 资源的优先级, 默认为 MEDIUM.
e)MEMORY_LIMIT: 资源队列内存限制大小, 单位为 kB,MB,GB. 默认为 - 1, 表示不受限制.
备注:
1. 只有 superuser 角色权限的用户才可以创建资源队列
2.ACTIVE_STATEMENTS 与 MAX_COST 必须二选一, 不能都设置为 - 1.
3. 队列名不能为 none, 为保留标识符.
例如:
create resource queue max_res_1_5 with (ACTIVE_STATEMENTS=50, MEMORY_LIMIT='1500MB');
3.2 创建角色
CREATE ROLE max_role LOGIN RESOURCE QUEUE max_res_1_5;
创建一个使用队列 max_res_1_5 的用户.
备注:
1, Role 和 User 的区别: ROLE + LOGIN 权限 = USER.
即 create user 默认设置 with login. 而 create role 默认 with NOLOGIN.
2, 如果创建用户时没有指定队列, 则默认队列为 pg_default.
2.3 修改已有角色的队列
设置用户 xxx 队列为 max_res_1_5:
alter role xxxx resource queue max_res_1_5;
去掉用户 xxx 分配的队列:
alter role xxxx resource queue none;
备注:
1, 普通用户角色也可以操作.
2,none 为特殊队列名, 表明该用户设置为默认的队列 pg_default.
2.4 删除队列
drop resource queue max_res_1_5;
备注:
1, 如果该队列上已有用户, 则会删除失败. 报错信息: ERROR: resource queue "max_res_1_5" is used by at least one role
2, 只能是 superuser 执行该操作, 否则 ERROR: must be superuser to drop resource queues
2.5 队列系统配置参数
resource_select_only: 如果值为 off, 那么 insert,update,delete 也将被资源队列限制. 如果是 on, 那么只有 select,select into,create table as select ,declare cursor 被资源队列限制.
max_resource_queues : 资源队列最大个数.
2.6 队列查询
1, 查询队列配置
SELECT * FROM pg_resqueue_attributes;
2, 查询系统中的队列
select * ,pg_resqueue.oid from pg_resqueue;
3, 查询各用户分配的队列:
SELECT rolname, rsqname FROM pg_roles,gp_toolkit.gp_resqueue_status WHERE pg_roles.rolresqueue=gp_toolkit.gp_resqueue_status.queueid;
三, 队列原理
Greenplum 中的资源队列和 Hadoop 中资源队列实现原理和资源分配方式不太一样. 在 GP 中, 队列主要是对内存资源进行分配和限制. 所有运行在队列上的 SQL 均分资源, 不存在互相抢占的情况.
一个 SQL 运行时, 真正能被分到的内存大小, 受限的因素很多. 和服务器内存, GreenPlum 系统配置等有关.
3.1 内存参数配置
max_statement_mem: 设置每个查询最大使用的内存量, 该参数是防止 statement_mem 参数设置的内存过大导致的内存溢出, 默认为 2GB.
statement_mem: 设置每个查询在 segment 主机中可用的内存, 该参数设置的值不能超过 max_statement_mem 设置的值, 如果配置了资源队列, 则不能超过资源队列设置的值 MEMORY_LIMIT, 默认 125MB. 推荐值计算方法:( gp_vmem_protect_limitGB * .9 ) / max_expected_concurrent_queries .
例如:
gp_vmem_protect_limit 设置为 8192MB (8GB) , 查询的最大并发量为 40, 其中 10% 为缓存空间, 则 statement_mem 计算如下:
(8GB * .9) / 40 = .18GB = 184MB
gp_vmem_protect_limit: 设置 segment 中所有的进程可用的内存, 如果查询所使用的内存超过该内存值, 则查询失败. 该参数为本地参数, 需要对所有的 primary 和 mirror 分别进行设置. 默认为 8192MB.
备注:
1,statement_mem 设置必须小于 max_statement_mem. 否则报错. ERROR: Invalid input for statement_mem. Must be Less than max_statement_mem (2048000 kB). (cdbvars.c:1374).
2, 理论上 statement_mem 也必须小于队列参数 MEMORY_LIMIT. 但是在创建 MEMORY_LIMIT 小于 statement_mem 默认值 125MB 时, 也能够成功, 不会出错. 不过在该队列上运行 SQL 时, 则会报错. ERROR: deadlock detected, locking against self.
思考: 为啥创建队列时, 不检查 MEMORY_LIMIT 和 statement_mem 值的大小关系, 而是在运行时再检查?
statement_mem 是在会话中调整的变量, 用于调整会话中单个 sql 计算在每个 segment 机器上可分配大小, 因此不是一个固定的值, 所以只好把大小检查放到任务运行时.
3.2 内存分配计算过程
当一个查询在资源队列上运行 SQL 计算任务时, 将直接在每个 segment 分配使用固定内存, 假设为 X MB, 即使实际使用的内存没有达到 X.
如果同时设置了 statement_mem 参数, 为 Y MB. 则 SQL 任务可以分配到的内存大小为 MAX(X,Y). 因此会发生实际同时运行 SQL 还未达到 ACTIVE_STATEMENTS, 但是队列可用内存被耗尽的情况, 当出现这种情况的时候, 队列中的其他 SQL 查询会等待. 直到由内存释放.
Greenplum 为 SQL 计算任务分配内存大小计算过程如下图:
1 内存分配要点
1, 当只设置 MAX_COST, 则为 Min((planCost/MAX_COST), 1.0)*MEMORY_LIMIT 和 statement_mem 较大者.
2, 当只设置 ACTIVE_STATEMENTS, 则 MEMORY_LIMIT
/ACTIVE_STATEMENTS 和 statement_mem 较大者.
3, 如果都设置, 则 Min( 1.0/ ACTIVE_STATEMENTS, planCost / MAX_COST)*MEMORY_LIMIT 和 statement_mem 较大者.
4, 没有设置 MEMORY_LIMIT, 则直接为 statement_mem.
上图流程中, 会计算出需要为该 SQL 任务计算出所分配的内存大小 X MB. 并且会再次检查该内存大小必须小于 MEMORY_LIMIT 值, 即 statement_men 也必须小于等于 MEMORY_LIMIT, 否则会报错.
3.3 内存大小模型
通过以上分析和内存分配计算过程的了解, 我们可以清晰的知道各内存参数之间的关系, 以及在 Greenplum 中起到的作用和各个参数需要配置为合适的值, 避免出现内存过多或者过小的情况.
首先 gp_vmem_protect_limit 必须小于服务器物理内存, max_statement_mem 和 MEMORY_LIMIT 必须小于 gp_vmem_protect_limit.
其次 statement_mem 必须小于 Min(max_statement_mem,MEMORY_LIMIT).MEMORY_LIMIT 可以大于 max_statement_mem, 但最好小于 ACTIVE_STATEMENTS*max_statement_mem.
四, 常见问题处理
1, 内存不足
ERROR: insufficient memory reserved for statement (execHHashagg.c:1314) (seg0 slice6 10.0.15.16:40000 pid=13606) (cdbdisp.c:254)
解决办法:
a) 修改队列中 MEMORY_LIMIT 为更大的值.
b) 通过 SET statement_mem='xx MB', 临时提高 sql 可分配的内存值.
来源: https://www.qcloud.com/developer/article/1369185