资源监控作业级监控之TopSQL
1、TopSQL简介
在实际的生产环境中,难免会出现一些突发情况,如计划跳变、异常中断、作业长时间执行不结束等,如果已经没有现场,而且也没有工具将当时的作业运行情况记录下来的话,那么事后就要投入更多的人力以及时间成本对错误进行定位和解决,有时还往往定位不到错误出现的地方。为了解决这种情况,GaussDB(DWS)开发了TopSQL功能,对运行中的语句记录(实时TopSQL),对运行完成的语句进行记录(历史TopSQL)。
目前TopSQL功能被用户广泛使用,是性能定位、劣化分析、审计回溯等重要的基石,为用户提供覆盖内存、耗时、IO、网络、空间等多方面的监控能力。 TopSQL可以帮助用户实现下列功能:①确定影响数据库性能的资源最密集的SQL查询;②监控和跟踪SQL查询随时间推移的性能变化;③分析查询执行计划以确定潜在的优化。
2、TopSQL相关视图与GUC参数
TopSQL功能主要通过视图承载,相关视图如下:
视图级别 | 实时 / 历史 | 节点范围 | 查询视图 |
query级别 / perf级别 | 实时 | 当前CN | GS_WLM_SESSION_STATISTICS |
所有CN | PGXC_WLM_SESSION_STATISTICS | ||
历史 | 当前CN | GS_WLM_SESSION_INFO | |
所有CN | PGXC_WLM_SESSION_INFO | ||
operator_realtime级别 / operator级别 | 实时 | 当前CN | GS_WLM_OPERATOR_STATISTICS |
所有CN | PGXC_WLM_OPERATOR_STATISTICS | ||
历史 | 当前CN | GS_WLM_OPERATOR_INFO | |
所有CN | PGXC_WLM_OPERATOR_INFO |
GUC参数:
-
ENABLE_RESOURCE_TRACK (ON)
是否开启监控功能,实时TopSQL的总开关,关闭之后实时TopSQL将不再进行记录,更不会在历史TopSQL中出现。
-
ENABLE_RESOURCE_RECORD(ON)
设置是否开启资源监控记录归档功能。开启时,对于执行结束的记录,会分别被归档到相应的INFO视图,CN和DN都需要设置上。
-
ENABLE_TRACK_RECORD_SUBSQL(ON)
控制是否记录存储过程、匿名块内部语句(默认为on)(建议820及以上版本开启,TopSQL记录该子语句的前提是:子语句下推到DN执行;子语句执行时间超过resource_track_subsql_duration,目前TopSQL只能记录第一层循环的子语句,多层嵌套循环的子语句不会记录)。
- RESOURCE_TRACK_DURATION(60s)
设置实时TopSQL中记录的语句执行结束后进行历史信息转存的最小执行时间,该时间记录值的判断是包含了排队时间和运行时间,当排队时间+运行时间 > RESOURCE_TRACK_DURATION时,TopSQL历史视图会记录作业信息。
- RESOURCE_TRACK_SUBSQL_DURATION(180s)
存储过程内部语句转储的最小执行时间。
-
RESOURCE_TRACK_COST(0)
设置对当前会话的语句进行资源监控的最小执行代价。
-
RESOURCE_TRACK_LEVEL(QUERY)
设置当前会话的资源监控的等级,默认为query级别。
-
TOPSQL_RETENTION_TIME(30)
历史TopSQL中GS_WLM_SESSION_INFO和GS_WLM_OPERATOR_INFO表中数据的保存时间,单位为天(历史TopSQL视图的数据实际是存储在postgres数据库中的dbms_om.gs_wlm_session_info系统表上,该表通过start_time进行分区,每天一个分区,通过参数topsql_retention_time配置默认保留30个分区即30天的记录,定期对pgxc_wlm_session_info的分区进行清理、创建,hash分布且分布键为queryid。如判断该表占用空间较多,可调低该参数,最多30min后表空间可下降)。
- SESSION_HISTORY_MEMORY(100MB)
设置历史查询视图的内存大小,如果语句运行过程中出现"TopSQL lfq is full, failed to save queryid"报错信息,可通过如下SQL查询TopSQL无锁队列总共的内存和已使用的内存。
select * from pgxc_total_memory_detail where nodename like 'cn_%' and memorytype like '%topsql%' order by 1,2;
如果发现某个CN节点topsql_used_memory大于或者即将大于max_topsql_memory,除调大该GUC参数外,可在该CN执行如下SQL,进行手动转储:
CALL pg_catalog.create_wlm_session_info(1);
通过GUC命令设置参数:
gs_guc reload -Z coordinator -Z -N all -I all -c "enable_resource_track=on"
gs_guc reload -Z coordinator -Z -N all -I all -c "resource_track_level=query"
gs_guc reload -Z coordinator -Z -N all -I all -c "enable_resource_record=on"
gs_guc reload -Z coordinator -Z -N all -I all -c "resource_track_duration=0"
gs_guc reload -Z coordinator -Z -N all -I all -c "resource_track_cost=0"
视图常用分析字段:
字段名称 | 字段描述 | 分析出可能的现象 |
block_time | 语句执行前的阻塞时间,包含:语句解析、语句优化时间,以及作业排队时间。 |
A1: block_time较大,而duration值并无明显变化,说明用户作业受其它作业影响,在真正开始执行前进行了较长时间的排队,下一步需要接着查看本数据表,统计起始时间小于start_time、结束时间大于finish_time的作业数量。 A2: block_time较小,而duration值较大,说明用户作业执行时间增加较大原因是自己导致,需要继续分析数据量的变化情况、各DN的执行时间变化。 |
start_time | 语句开始执行时间戳。 | |
finish_time | 语句执行结束时间戳。 | |
duration | 语句执行时间长度。 | |
status | 语句执行的结束状态,正常为finished,异常为aborted。 | 可以查看作业是否正常结束,如果异常,还会有异常原因。 |
abort_info |
语句执行结束状态为aborted时显示异常信息。 | |
min_peak_memory |
语句在所有DN上的最小内存峰值,单位MB。 |
B1: 对于同一个查询,可对比前后几次的内存消耗情况,内存消耗平均值能够反映出数据表的数据量是否有变化,memory_skew_percent值能够侧面反映出相关数据表在各DN上的数据分布是否有倾斜。 并且,query_plan能够直接显示作业的执行计划,对比执行计划是否有变化。 |
max_peak_memory |
语句在所有DN上的最大内存峰值,单位MB。 | |
average_peak_memory |
语句执行过程中的内存使用平均值,单位MB。 |
|
memory_skew_percent |
语句各DN间的内存使用倾斜率。 | |
min_spill_size | 若发生下盘,所有DN上下盘的最小数据量,单位MB。 | C1: 对有大量下盘的查询有显著帮助信息,当下盘量剧增的时候,通常是表数据量有大幅增加,或者是执行计划有问题导致的,结合query_plan能进一步分析,spill_skew_percent可以查看作业是否有严重数据倾斜。 |
max_spill_size |
若发生下盘,所有DN上下盘的最大数据量,单位MB。 | |
average_spill_siz |
若发生下盘,所有DN上下盘的平均数据量,单位MB。 | |
spill_skew_percent |
若发生下盘,DN间下盘倾斜率。 | |
min_dn_time |
语句在所有DN上的最小执行时间,单位ms。 | D1: DN上的执行时间,结合duration数据,如果一个查询的DN执行时间有严重倾斜,那就需要考虑数据表的分区、分布列是否设置合适;不合理的分区、分布列,可能会导致本应分散到多个DN的执行任务被集中到个别DN上执行,执行时间必然大大增加。 |
max_dn_time |
语句在所有DN上的最大执行时间,单位ms。 | |
average_dn_time |
语句在所有DN上的平均执行时间,单位ms。 |
|
dntime_skew_percent |
语句在各DN间的执行时间倾斜率。 |
|
min_cpu_time |
语句在所有DN上的最小CPU时间,单位ms。 | E1: CPU执行时间是分配给改作业的实际执行时间,当duration有明显增加,而平均CPU执行时间无明显变化时,很可能的一个原因是作业执行期间,有多个其它计算密集型作业同时段执行,因CPU抢占的原因,拉长了该作业的执行时长。 |
max_cpu_time |
语句在所有DN上的最大CPU时间,单位ms。 | |
total_cpu_time |
语句在所有DN上的CPU总时间,单位ms。 |
|
cpu_skew_percent | 语句在DN间的CPU时间倾斜率。 | |
min_peak_iops |
语句在所有DN上的每秒最小IO峰值。 | F1: IO是变化最莫测的一个资源,一个作业在数据量不变、内存消耗无变化、CPU执行时间无变化、下盘量无变化的情况下,偏偏duration增加了,那最可能的原因是IO的原因。IO有点独特的是,往往IOPS变小反而反应了作业受其它作业影响,IO跑步起来,拖长了作业执行时间;其它属性通常相反,如:内存、CPU、下盘量,这些值变小通常意味着作业执行变快了。 |
max_peak_iops |
语句在所有DN上的每秒最大IO峰值。 | |
average_peak_iops |
语句在所有DN上的每秒平均IO峰值。 | |
iops_skew_percent |
语句在DN间的IO倾斜率。 | |
query_plan | 语句的执行计划。 | G1: 作业执行计划是否有变化。 |
unique_sql_id | 标识一类语句 | H1: 如果某类语句可能占用较多内存或者CPU资源,需要查杀,可利用gs_append_blocklist(unique_sql_id int8)或者gs_append_blocklist(sql_hash text)函数将该类语句加入黑名单,后续该类语句下发将不再执行。 |
sql_hash |
总结:
-
因数据量变化,导致作业执行时间增加,可以分析A2/B1/D1/G1,进而确认作业查询的数据表是否有明显的数据量增加;
-
因其它并发作业抢占,导致作业排队,从而导致作业执行时间增加,可以分析A1/B1/D1,进而查看作业执行的同时期是否有大量并发作业在执行;
-
因其它作业而产生的CPU抢占,导致作业执行时间增加,可以分析A2/D1/E1,进而查看作业执行的同时期是否有大量并发作业在执行;
-
因其它作业而产生的IO抢占,导致作业执行时间增加,可以分析A2/F1,进而查看作业执行的同时期是否有大量并发作业在执行;
- 禁止某一类语句执行,可参考H1。
值得注意的是,发生资源争抢时,可能会出现并发症,即CPU、IO抢占,作业排队现象都会发生,针对并发症问题,可以逐步分析解决,比如:第一步,调整作业执行顺序,减少并发作业数量,减少阻塞时间;第二步,定位出同时段执行的典型计算密集型、存储密集型作业,先移动到其它时间段执行,减少对本作业的影响;第三步,在无其他作业明显干预的情况下,做进一步分析,
注意事项:
- TopSQL不记录白名单和内部语句,但可正常记录超户下发的语句信息,可记录定时任务。
- query和perf级别的topsql监控主要差异点在于query_plan字段,相比于query级别的算子信息,perf级别的query_plan字段增加算子的实际信息,如算子实际内存峰值,内存自动扩展信息,CU,Buffers等统计信息。
- query和perf级别实时TopSQL和历史TopSQL的start_time字段信息含义不一致,实时TopSQL中的start_time表示的是作业下发的时间,历史TopSQL中的start_time表示的是作业真正开始运行的时间。
- 查询历历史TopSQL Query,perf以及算子级别数据时,仅能通过postgres数据库进行访问。
- 实时TopSQL中能够记录的SQL语句的规格是:
- 不记录特殊数据定义语句,如:SET、RESET、SHOW、ALTER SESSION SET、SET CONSTRAINTS语句;
- 记录数据定义语句,例如:执行CREATE、ALTER、DROP、GRANT、REVOKE和VACUUM语句;
- 记录数据操作语句,例如:
- 执行SELECT、INSERT、UPDATE和DELETE语句。
- 执行explain analyze和explain performance场景。
- 使用query级别/perf级别视图。
- 记录函数与存储过程的调用入口语句,当GUC参数enable_track_record_subsql开启的情况下,可记录存储过程的部分内部语句(declare定义语句除外),仅会记录其中下发到DN执行的内部语句,未下发到DN执行的内部语句会被过滤掉;
- 记录匿名块语句,当GUC参数enable_track_record_subsql开启的情况下,可记录匿名块中的部分内部语句,仅会记录其中下发到DN执行的内部语句,未下发到DN执行的内部语句会被过滤掉;
- 记录游标语句,当游标并非从缓存中读取数据,而确实触发语句下发到DN上执行的条件下,该游标语句会被记录,并且会进行语句、执行计划增强,但当游标从缓存中读取数据时,不进行记录;当游标语句在匿名块或者函数中使用时,当游标从DN上读取较多数据但不完全使用时,因当前架构限制,无法记录该游标在DN上的监控信息。对于With Hold游标,该语法执行逻辑特殊,会在事务提交阶段执行实际查询动作,当语句在该阶段执行报错时,作业的aborted状态无法反馈到TopSQL历史表中。
- 重分布过程中的作业不统计;
- JDBC执行的带占位符语句,通常会补齐参数内容,但如果参数和原语句合起来长度超过64KB,则不记录参数,或者如果是轻量化语句,直接下发到DN上执行,不记录参数。
- 从8.1.3集群版本开始,query、perf级别TopSQL运行时监控功能已完全不影响查询性能,对当前会话的语句进行资源监控的GUC参数resource_track_cost默认值已修改为0,查询TopSQL实时监控视图时,默认会显示所有正在执行的语句。
- 从8.1.3集群版本开始,对于存储过程中的子语句监控功能,如果在查询TopSQL实时监控视图的会话中,开启控制子语句记录归档功能的GUC参数enable_track_record_subsql,不论业务语句中是否开启子语句监控开关,查询TopSQL实时监控视图的结果都能看到执行语句的子语句运行信息。
- 关于存储过程中子语句的监控功能即enable_track_record_subsql,8.1.3集群版本中建议不要全面开启,由于没有按时间过滤子语句的功能,全面开启可能会记录过多子语句,导致归档的监控表占用大量磁盘空间;8.1.3集群版本建议仅用于查询实时监控信息,或对个别存储过程业务做定位分析时,仅开启对应会话中的参数。8.2.1版本新增GUC参数resource_track_subsql_duration(默认值为180秒),可以通过执行时间过滤需要归档的子语句,用户可以按需调整该值大小。
- 由于规格限制,对于未下盘的主语句,TopSQL历史表中的记录会有延时,等待下次作业下发时才会显示在TopSQL历史表中。
- 从8.2.1.200集群版本开始,新增operator_realtime级别TopSQL运行时监控,提供算子级实时监控的能力,开启此级别的监控可以查询语句的执行计划以及具体执行信息,查询TopSQL算子级实时监控视图时,默认会显示所有正在执行的语句。但是对于存储过程和游标场景,暂时不支持显示算子级实时监控信息。另由于查询所有语句的信息对于CN内存压力较大,为了不影响作业性能,为用户提供查询单个语句的函数pg_stat_get_wlm_realtime_operator_info(queryid),可以通过该函数查询指定语句的算子执行信息。该版本暂时不支持算子历史信息查询。
- operator_realtime级别TopSQL运行时监控对于CN轻量化和存储过程的情况,暂时不支持。另由于算子执行速度较快的原因,对于算子信息的显示会有一定滞后性。
- query级别的作业监控和operator的算子监控中的spill_size字段,由于统计维度不同,会有一定差异,query级别监控监控的语句实际下盘文件大小,算子监控的是具体算子在逻辑层IO读写的数据量。
- 当GUC参数enable_stream_operator设置为off状态时,算子执行信息存在显示不准的情况。
3、TopSQL原理介绍
作业下发执行后,内核通过打桩记录作业的各项资源信息,如内存、CPU、下盘、IO和网络信息等,作业执行完成后该资源信息后先存在无锁队列中,资源管理后台辅助线程将该数据信息定期转储到dbms_om.gs_wlm_session_info系统表中,后续通过topsql_retention_time定期老化数据。
4、通过TopSQL识别烂SQL
- 识别stream数量多的语句
select *,(length(query_plan) - length(replace(query_plan, 'Streaming', ''))) / length('Streaming') as stream_count FROM pgxc_wlm_session_info ORDER BY stream_count DESC limit 100;
- 识别内存占用高的语句
select * from pgxc_wlm_session_info where start_time > 'xxxx-xx-xx' and start_time < 'xxxx-xx-xx' order by max_peak_memory desc limit 100;
- 识别需要优化的语句
select * from pgxc_wlm_session_info where start_time > 'xxxx-xx-xx' and start_time < 'xxxx-xx-xx' and warning is not null order by duration desc limit 100;
- 识别执行时间长的语句
select * from pgxc_wlm_session_info where start_time > 'xxxx-xx-xx' and start_time < 'xxxx-xx-xx' order by duration desc;
- 识别不下推的语句
select * from pgxc_wlm_session_info where start_time > 'xxxx-xx-xx' and start_time < 'xxxx-xx-xx' and warning like ‘%can not be shipped%’ order by max_peak_memory desc;
- 识别高CPU语句
select * from pgxc_wlm_session_info where start_time > 'xxxx-xx-xx' and start_time < 'xxxx-xx-xx' order by max_cpu_time desc;
- 识别下盘量大的语句
select * from pgxc_wlm_session_info where start_time > 'xxxx-xx-xx' and start_time < 'xxxx-xx-xx' order by max_cpu_time desc;
- 识别未做analyze的语句
select * from pgxc_wlm_session_info where start_time > 'xxxx-xx-xx' and start_time < 'xxxx-xx-xx' and warning ilike '%statistics%';
5、算子级TopSQL监控
在SQL执行过程中,有些用户更希望能对算子执行进度进行监控,对于长时间运行的SQL,能够看出哪个算子执行时间长,通过算子执行时间和已处理行数等信息,确定是否需要查杀SQL,此时需要将RESOURCE_TRACK_LEVEL参数设置为OPERATOR。
算子监控可以将SQL执行过程的算子监控数据以可视化的方式呈现出来,以便用户更加直观地了解算子的运行情况和性能表现。算子监控主要有以下价值:
- 提升用户体验:通过可视化的方式呈现算子执行信息,用户可以更加直观地了解算子的运行情况和性能。
- 性能优化:通过对算子监控数据的可视化分析,可以发现算子运行中的瓶颈和问题,从而及时进行优化和调整,提高算子的运行效率。
- 故障排查:通过对算子监控数据的可视化分析,可以及时发现算子运行中的问题和异常,从而及时进行修复和维护,提高SQL的可维护性。
- 提高算子的可扩展性:通过对算子监控数据的可视化分析,可以发现算子运行中的瓶颈和问题,从而及时进行优化和调整,提高算子的可扩展性,为后续的业务发展提供支持。
OPERATOR算子监控和QUERY/PERF语句监控功能类似,均包含实时和历史二种形态,包含静态和动态二类信息:
- 语句静态信息是语句在真正执行前就已经由优化器生成的信息,如执行计划plan_node_name,queryid,预估行数estimated rows等信息。可用来分析生成的执行计划是否合适。
- 语句动态信息是语句在执行器中执行过程中所占用的资源信息,如算子执行进度progress、内存peak_memory、算子下盘spill_size、网络net_size、磁盘IO(read_bytes、write_bytes),CPU(cpu_time)等不同DN的实时的信息记录。可用来分析语句执行过程中的进度和资源消耗情况,通过该字段可以分析出语句在运行是消耗较久的在什么地方,便于后续优化。
功能展示:我们下发某个查询,在另一个会话中查询实时算子视图pgxc_wlm_operator_statistics,结果如下:
6、TopSQL与其他视图交互
不同视图从不同维度统计业务在数据库中的运行状态,因为或多或少都包含部分同类字段,可用于后续关联分析,此处给出历史TopSQL视图常交互的其他视图:
使用示例:如果某个语句在历史TopSQL 历史pgxc_wlm_session_info视图中显示以往该作业很快就能正常运行完成,但是此时查杀pgxc_stat_activity发现该语句运行很长时间没有结束,如下图所示,则此时可能有异常,可利用以下步骤进行定位:
常用定位过程如下所示:
步骤1:查询pgxc_stat_activity活跃视图,找出长时间运行不结束的异常作业
select coorname, usename, client_addr, now()-query_start as dur, state, enqueue, waiting, pid, query_id, substr(query,1,150) FROM pgxc_stat_activity WHERE usename not in ('omm','Ruby') and state = 'active' ORDER BY dur DESC limit 100;
步骤2:根据步骤1得到的query信息,查询历史TopSQL视图,比较以往和现在的运行情况,如果以前运行时间很快,但当前语句运行时间很长未结束,则可考虑该语句已发生异常(历史topsql视图按天分区,查询时尽量带start_time条件)
select * from pgxc_wlm_session_info where start_time > '2024-08-22 00:00' and start_time < '2024-08-24 00:00' and query ilike '%XXXXXXXXX%' order by start_time;
步骤3:根据步骤1得到的query_id查询等待视图pgxc_thread_wait_status,得到作业线程号lwtid,并打出作业堆栈进行分析
select * from pgxc_thread_wait_status where query_id = xxx;
gstack xxx
7、TopSQL实践
某客户集群出现系统级性能问题,CPU持续飚高,业务受阻。
通过TopSQL历史视图查询到有10+业务SQL存在stream数超过100,判断为CPU高的原因:
postgres=# select *,(length(query_plan) - length(replace(query_plan, 'Streaming', ''))) / length('Streaming') as stream_count FROM pgxc_wlm_session_info ORDER BY stream_count DESC limit 100;
针对此业务SQL进行下线并进行差分优化后,问题解决。
8、参考文献
- GaussDB for DWS 负载管理核心技术解密二: 白话历史资源监视-云社区-华为云 (huaweicloud.com)
- GaussDB(DWS)监控工具指南(一)作业级监控TopSQL-云社区-华为云 (huaweicloud.com)
- GaussDB(DWS)监控工具指南(四)算子级监控【绽放吧!GaussDB(DWS)云原生数仓】-云社区-华为云 (huaweicloud.com)
- 点赞
- 收藏
- 关注作者
评论(0)