喝绿茶的猫 2018-11-20
作者 | digoal
背 景
Greenplum(GPDB) 是一个重计算和重资源的 MPP 数据库,可谓有多少资源就能消耗多少资源,带来的好处是处理速度变快了,坏处就是容易用超。
CPU、网络、硬盘用超的话,关系不大,因为大不了就是到硬件瓶颈,但是内存用超的话会带来较大的负面影响,例如操作系统 OOM 用户进程,导致数据库崩溃等。
如果要达到非常强壮的稳定性,Greenplum 内存的配置非常关键。(当然代码本身的质量也很关键)
如何避免 OOM
OOM 指遇到了进程申请内存不足的错误。例如:
哪些情况会引发 OOM
导致数据库 OOM 报错的原因可能有:
如何避免 OOM
1、调整 QUERY,使之需要更少的内存。
2、使用资源队列(resource queue,Greenplum 控制资源的一种手段),限制并发 QUERY 数。降低集群内同时运行的 QUERY 数,从而减少系统整体的内存资源的使用。
3、减少单个主机部署的 SEGMENT 数量,例如有 128G 内存的主机,部署 16 个和部署 8 个 SEGMENT 节点,每个节点能使用的内存相差了一倍。
4、增加单台主机的内存数。
5、设置数据库参数 gp_vmem_protect_limit,限制单个 SEGMENT 可以使用的 VMEM 上限。单个主机的内存以及部署多少个 SEGMENT 决定了平均单个 SEGMENT 最多可以使用多少内存。
6、对于某些对内存使用量不可预知的 SQL,通过在会话中设置 statement_mem 参数,限制单条 SQL 对内存的使用,从而避免单条 SQL 把内存吃光的情况。
7、也可以在库级别设置 statement_mem 参数。对这个数据库的所有会话生效。
8、使用资源队列,限制这个资源组的内存使用上限,将数据库用户加入资源组,控制这些用户共同使用内存的上限。
如何配置内存相关参数
正确的配置(操作系统、数据库参数、资源队列管理)可以有效降低 OOM 发生的概率。
接下来我们分析一下 操作系统内核配置、数据库配置,看看如何让数据库尽量避免 OOM。
操作系统内核参数
数据库参数
gp_vmem_protect_limit
控制每个 SEGMENT 上,所有进程可以申请到的最大内存。如果这个值太高,可能触发系统的 OOM 或者更严重的问题。如果设置太低,则可能导致系统有足够内存的情况下,SQL 却无法运行。下边会说明设置公式。
runaway_detector_activation_percent
这个参数默认为 90,是一个百分比值。当任一 SEGMENT 使用的内存超过 (runaway_detector_activation_percent*gp_vmem_protect_limit/100) 时,主动 terminate QUERY,防止 OOM。
terminate 的顺序从使用最多内存的 QUERY 依次开始,直到内存降低到 (runaway_detector_activation_percent*gp_vmem_protect_limit/100) 以下。通过 gp_toolkit.session_level_memory_consumption 视图可以观察每个会话的内存使用情况,以及 runaway 的信息。
statement_mem
默认为 125MB。设置单条 SQL 最多可以申请的内存,当超过这个内存时,写 spill file 文件。
建议的设置为单个 SEGMENT 的保护内存乘以 0.9 除以期望的最大 SQL 并行运行的值。
注意 1,statement_mem 在会话中设置,如果当前并行度很低,某个会话需要 RUN 一条需要大量内存的 QUERY,可以在会话中设置一下。
注意 2,statement_mem 比较适合低并发的环境对内存的使用控制。对于高并发的环境,如果使用 statement_mem 来控制内存,你会发现每条 QUERY 可以使用的内存极少,不利于高并发情况下少量对内存需求多的 QUERY 的性能。建议高并发的情况下,使用资源队列 (resource queue) 来控制内存的使用上限。
gp_workfile_limit_files_per_query
限制每个 QUERY 可以使用的最大 spill 文件数(当 QUERY 申请的内存超过 statement_mem 的限制时,使用 spill file(workfiles),类似操作系统的 swap 空间)。当使用的 spill file 超过限制时,QUERY 会被 terminate。默认为 0,表示无限制。
gp_workfile_compress_algorithm
设置 spill file 的压缩算法。有效值如下:
设置压缩,CPU 换空间,或 CPU 换 IO 能力。当磁盘紧张、磁盘 spill file 有写入瓶颈时可以设置压缩。
内存参数计算例子
gp_vmem,gp_vmem_protect_limit,vm.overcommit_ratio,设置举例:
环境如下:
1、首先计算给 GPDB 的总内存(给操作系统保留 "7.5G + 5% 内存" 的余量,算出整个系统给应用软件的实际可用内存),然后(实际可用内存除以 1.7 的经验系数)
2、计算 overcommit_ratio,用到了一个经验系数 0.026。
3、计算每个 SEGMENT 的内存使用上线保护参数:gp_vmem_protect_limit,除以挂掉一台节点后单台节点需要运行的 primary 数。
资源队列的使用
Greenplum resource queue 可以用来限制“并发的 QUERY 数、总的内存使用”。当 QUERY 运行时,会添加到对应的队列中,使用的资源将记录到对应的队列中,对应队列的资源控制限制对该队列内的所有会话起作用。
Greenplum 资源队列控制资源的思想和 Linux 的 CGROUP 非常类似。
一、创建资源队列的语法
1、ACTIVE_STATEMENTS,允许同时运行(active 状态)的 SQL 数。 -1 不限。
2、MEMORY_LIMIT 'memory_units KB, MB or GB', 设置资源队列中所有 SQL 允许的最大内存使用量。 -1 不限(但是受前面提到的数据库或系统参数限制,触发 OOM 错误。)。
SQL 的内存使用限制不仅受资源队列限制,同时还受到参数限制:
3、 MAX_COST float,设置为浮点或指数((for example 100.0),(for example 1e+2)),-1 不限制。这表示资源组允许同时执行的 QUERY 加起来的 COST 上限。COST 是 SQL 执行计划中的总成本。
4、COST_OVERCOMMIT boolean,当系统空闲时,是否允许 (TRUE) 超过 max_cost 的限制。
5、MIN_COST float,资源超限时,是需要排队的,但是,当 QUERY 的成本低于 min_cost 时,不需要排队,直接运行。(也就是说小查询,就让他跑吧。)
6、PRIORITY={MIN|LOW|MEDIUM|HIGH|MAX},指当前资源队列的优先级,当资源紧张时,优先将 CPU 资源分配给高优先级的资源队列。(处于高优先级的资源队列中的 SQL,可以获得更高优先级的 CPU 资源)。建议将实时性要求高的查询对应的用户分配到高优先级的资源队列中。类似 LINUX CGROUP 中的 CPU 资源组,real time task 和普通 task 的时间片策略。
二、修改资源队列限制
举例如下:
三、如何将用户放到资源队列中
举例如下:
四、资源队列相关参数
1、gp_resqueue_memory_policy,资源队列的内存管理策略,前面讲了用法。
2、gp_resqueue_priority,是否使用资源队列的优先级。ON 使用,OFF 不使用。不使用资源队列优先级时,所有队列公平对待。
3、gp_resqueue_priority_cpucores_per_segment,每个 SEGMENT 可以使用的 CPU 核数,例如 8 核的机器,跑了 2 个 PRIMARY SEGMENT,则配置为 4。master 上面如果没有其他节点,配置为 8。当发生 CPU 抢占时,优先级高的资源组中运行的 SQL,优先分配 CPU 资源。
4、gp_resqueue_priority_sweeper_interval,CPU 时间片统计间隔,SQL 执行时,计算它的 share 值(根据优先级以及计算 gp_resqueue_priority_cpucores_per_segment 出来)。越小越频繁,优先级设置带来的效果越好,但是 overhead 越大。
五、建议的资源队列使用方法
1、GPDB 默认的资源队列为 pg_default,如果不创建队列,那么所有的用户都会被指定给 pg_default。这是非常不建议的。
建议的做法是为每个用户创建一个资源队列。因为通常一个数据库用户对应一个业务。不同的数据库用户可能对应不同的业务或者使用者,例如业务用户、分析师用户、开发者、DBA 等。
2、超级用户发起的 SQL 请求不受资源队列的限制,仅仅受前面讲到的参数的限制。因此如果要使用 resource queue 来限制资源的使用,那么就不建议业务使用超级用户来执行 QUERY。
3、ACTIVE_STATEMENTS 表示资源队列中,允许同时执行的 SQL。(注意当 QUERY 的成本低于 min_cost 时,不需要排队,直接运行。)
4、MEMORY_LIMIT,设置资源队列中所有 SQL 允许的最大内存使用量。前面讲了突破方法,statement_mem 设置的优先级更高,可以突破 resource queue 的限制。注意所有资源队列的内存加起来不要超过 gp_vmem_protect_limit 的限制。
5、通过配置资源队列的优先级,可以区分不同的业务。例如出报表的业务优先级最高,其次是普通业务,其次是分析师。这样的情况,我们可以创建 3 个资源队列,分别使用 MEDIUM|HIGH|MAX 的优先级。
6、如果每个时间段的资源需求不一样,可以写一个 CRONTAB 任务,定时的调整资源队列的限制。
例如白天分析师的优先级更高,晚上处理报表的队列优先级更高。目前 Greenplum 还不支持按时间段来设置资源限制,所以只能外部部署任务,alter resource queue 来实现。
7、通过 gp_toolkit 提供的视图,可以观察资源队列的资源使用。
参考
http://greenplum.org/docs/best_practices/workloads.html
作者介绍
digoal(德哥,周正中),阿里云数据库内核技术架构师,PostgreSQL 中国社区发起人之一、常委、兼任社区大学校长,PostgreSQL 中国社区杭州分会会长,DBA+ 社群联合发起人,DBGeeK 联盟专家团成员。