诊断性能问题

当你发现查询性能问题的时候,可以从以下几个方面入手来加以诊断:

  • 检查集群的状态: 查看是不是有死掉的HDFS DataNode或者OushuDB segment,方法如下:
1
2
3
4
5
6
7
# 检查是否有DataNode死掉,使用命令

hadoop dfsadmin -report

# 检查是否有OushuDB segment死掉,可以使用语句查看:

select * from gp_segment_configuration;
  • 检查节点上是不是有坏盘。查看硬件的性能是不是跟期待的一样。可以通过”hawq checkperf”命令检查网络,磁盘速度。磁盘速度以及网络速度经常是问题所在。
  • 查看Explain Analyze查询的结果,找出慢的操作,从而推测出问题的地方
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
I. 有时候有些操作不能在内存中完成,比如HashJoin,HashAggregate, 这时候在Explain Analyze的输出中会看到产生的spill文件信息。
外存算法通常会比内存算法要慢很多。如下例所示:

->  Hash Join  (cost=7349612.80..1269920578.75 rows=16726776002 width=0)
    Hash Cond: b.d020 = a.d020
    Rows out:  Avg 26902546221.0 rows x 6 workers.  Max/Last(seg0:hawq2/seg0:hawq2) 40017551225/40017551225 rows with 20528/20528 ms to first row, 4273919/4273919 ms to end, start offset by 28/28 ms.
    Executor memory:  450570K bytes avg, 450570K bytes max (seg5:hawq2).
    Work_mem used:  206760K bytes avg, 207884K bytes max (seg0:hawq2). Workfile: (6 spilling, 0 reused)
    Work_mem wanted: 1633616K bytes avg, 1638375K bytes max (seg4:hawq2) to lessen workfile I/O affecting 6 workers.
    (seg0)   Initial batch 0:
    (seg0)     Wrote 1003408K bytes to inner workfile.
    (seg0)     Wrote 1805360K bytes to outer workfile.
    ...
    (seg4)     Read 2006596K bytes from inner workfile: 286657K avg x 7 nonempty batches, 289127K max.
    (seg4)     Read 3594787K bytes from outer workfile: 513541K avg x 7 nonempty batches, 527403K max.
    (seg4)   Hash chain length 6.7 avg, 33890 max, using 6269397 of 16777688 buckets.

这时可以尝试通过改变资源队列来给分配更大的virtual segment来给查询更多的内存。有如下两种方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 方法1: 使用hawq_rm_stmt_vseg_memory和hawq_rm_stmt_nvseg来强制资源管理器分配固定个数固定内存大小的virtual segments。
# 例如下面的例子强制分配6个大小为2GB大小的virtual segments。
# 这个方法只建议对个别语句使用,默认的256M virtual segment应该可以满足大部分应用场景。
# 注意:这两个值需要配合使用,只有hawq_rm_stmt_nvseg设置为非0值时这两个参数才生效。

set hawq_rm_stmt_vseg_memory = '2GB';
set hawq_rm_stmt_nvseg = 6; # 针对Hash分布的表请设置此值为指定表的Bucket Number数。

# 方法2: 使用Alter resource queue命令来修改resource 队列的VSEG_RESOURCE_QUOTA值。
# 下面的例子更改pg_default资源队列的VSEG_RESOURCE_QUOTA值,使其分配的每个virtual segment使用2GB内存。
# 请注意我们不建议把pg_default的VSEG_RESOURCE_QUOTA改的过大。这样会浪费较多内存。

ALTER RESOURCE QUEUE pg_default with (VSEG_RESOURCE_QUOTA='mem:2GB');
II. 如果我们Explain Analyze的结果表明大部分cost在motion操作,可以考虑使用Hash分布的表,并通过适当选择分布Key来减少motion操作。
  • 看一看优化器的估计值(例如输出行数)是不是接近真实值,如果不接近真实值,优化器可能产生不优化的查询计划。如果不接近真实值,需要查找原因,可能是该表在加载后没有analyze,也可能是统计数据不准。如果不准的话,可以通过下面的几个方法来改进统计数据收集的准确率,或者修改参数,或者调整查询
  • 查看是否选择谓词被尽量下推到了查询计划的底层,如果查询计划没有很好的估计出谓词的选择率,可以通过analyze相关列来得到统计数据。也可以尝试调整where子句中的谓词顺序
  • 看一看优化器有没有选择到最优的连接顺序。能够消除大量行的连接应该先执行,从而使得后面的连接可以处理少一些元组。如果优化器没有选出优化的顺序,尝试设置join_collapse_limit = 1,并且使用显式的连接语法来强制连接顺序。你也可以尝试收集更多的相关连接列的统计数据来帮助优化器做出更好选择。
  • 尝试设置default_statistics_target到100或者更大的值,来得到更加精确的统计值
  • 看一看优化器是不是选择了Hash聚集或者Hash连接,通常情况下,Hash聚集和Hash连接要比其他聚集和连接算法要快。让优化器去选择Hash聚集或者Hash连接,往往需要有足够的内存。可以尝试调整资源队列来给查询分配更多资源
  • 如果我们使用Hash分布的表,查看分布是否产生了倾斜(skew),如果产生了倾斜,需要选取合适的Hash分布key。检查倾斜的方法为:
1
2
3
4
SELECT gp_segment_id, COUNT(*)
FROM your_table_name
GROUP BY gp_segment_id
ORDER BY gp_segment_id;
  • 检查查询中涉及到的表是否通过Analyze收集过统计数据
  • 检查数据局部性统计:查看是不是data locality ratio很低,如果低的话,需要找出具体原因。比如有可能是HDFS没有配置好等。检查HDFS有没有配置好可以查看OushuDB segment上的日志里面是不是产生了大量的Exception。
  • 检查查询及资源队列状态,看看查询是不是在等待资源,而不是真正在执行
1
2
3
4
5
6
7
# pg_stat_activity视图给出了现在系统里面正在运行的所有查询及状态

select * from pg_stat_activity;

# pg_resqueue_status视图给出了现在系统内部资源队列的状态

select * from pg_resqueue_status;
  • 检查是否产生了元数据膨胀(bloat)。OushuDB针对元数据采用Heap格式存储,Heap格式存储采用了多版本(MVCC)机制。所以如果出现元数据表的频繁更改,比如频繁创建和删除表,而又长期没有对元数据表进行vacuum操作,元数据表可能出现膨胀,元数据表膨胀可能带来查询效率急剧下降,这是因为访问元数据的时间过长的缘故。检查膨胀的方法为,查看元数据表的行数X和该元数据表所有版本的行数BX,如果BX远大于X,则该表出现了膨胀。例如下面的例子检查到pg_class表的BX为417,而X为369。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
postgres=# set gp_select_invisible=true;
SET
postgres=# select count(*) from pg_class;
 count
-------
   417
(1 row)

postgres=# set gp_select_invisible=false;
SET
postgres=# select count(*) from pg_class;
 count
-------
   369
(1 row)

如果发现元数据表出现了膨胀,解决办法为先做vacuum操作,然后再对该表做reindex。另外,用户需要定期(例如,每天)对元数据表进行vacuum操作,避免出现元数据膨胀问题,对OushuDB的日常维护操作请参见OushuDB日常运维。

1
2
3
4
5
6
postgres=# vacuum full pg_class;
NOTICE:  'VACUUM FULL' is not safe for large tables and has been known to yield unpredictable runtimes.
HINT:  Use 'VACUUM' instead.
VACUUM
postgres=# reindex table pg_class;
REINDEX

如果一个表有SERIAL或者BIGSERIAL列(自增列),那么在加载数据时,master的Sequence Server往往会有瓶颈。那是因为所有的Segment会向Master节点的Sequence Server询问下一个值。每一个值都询问会带来很大瓶颈。可以使用下面语句使得Segment每次获取10000个值。

1
2
3
4
5
6
7
postgres=# CREATE TABLE tname (colname BIGSERIAL);
NOTICE:  CREATE TABLE will create implicit sequence "tname_colname_seq" for serial column "tname.colname"
CREATE TABLE

postgres=# alter sequence tname_colname_seq cache 10000;

ALTER SEQUENCE