在实际的生产环境中,难免会出现一些突发情况,如计划跳变、异常中断、作业长时间执行不结束等,如果已经没有现场,而且也没有工具将当时的作业运行情况记录下来的话,那么事后就要投入更多的人力以及时间成本对错误进行定位和解决,有时还往往定位不到错误出现的地方。为了解决这种情况,GaussDB(DWS)开发了TopSQL功能,对运行中的语句记录(实时TopSQL),对运行完成的语句进行记录(历史TopSQL)。
目前TopSQL功能被用户广泛使用,是性能定位、劣化分析、审计回溯等重要的基石,为用户提供覆盖内存、耗时、IO、网络、空间等多方面的监控能力。 TopSQL可以帮助用户实现下列功能:①确定影响数据库性能的资源最密集的SQL查询;②监控和跟踪SQL查询随时间推移的性能变化;③分析查询执行计划以确定潜在的优化。
TopSQL功能主要通过视图承载,相关视图如下:
| 视图级别 | 实时 历史 | 节点范围 | 查询视图 |
| query级别 perf级别 | 实时 | 当前CN | GS_WLM_SESSION_STATISTICS |
| 所有CN | PGXC_WLM_SESSION_STATISTICS | ||
| 历史 | 当前CN | GS_WLM_SESSION_INFO | |
| 所有CN | PGXC_WLM_SESSION_INFO | ||
| 所有CN(该视图显示经典字段信息) | PGXC_QUERY_INFO | ||
| operator_realtime级别 operator级别 | 实时 | 当前CN | GS_WLM_OPERATOR_STATISTICS |
| 所有CN | PGXC_WLM_OPERATOR_STATISTICS | ||
| 历史 | 当前CN | GS_WLM_OPERATOR_INFO | |
| 所有CN | PGXC_WLM_OPERATOR_INFO |
TopSQL功能默认打开(use_workload_manager,enable_resource_track,enable_resource_record参数均默认为on),GUC参数详细介绍如下:
use_workload_manager(on)
资源管理总开关,TopSQL功能开启该参数需要为on。
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历史视图会记录作业信息。CPU和存储资源充足的场景建议设置为0,可记录更全的业务;在QPS高于100场景,可酌情调大,如1-10s。
说明:该参数可支持单位是毫秒、秒、分钟、小时、天,默认单位为秒,参数设置示例如下:
SET resource_track_duration='0.1s';
SET resource_track_duration=0.1;
SET resource_track_duration='0.01min'; # 由于该参数的默认单位是秒,采用四舍五入的方式,因此该处设置的值为1s,而不是0.6s。
RESOURCE_TRACK_SUBSQL_DURATION(180s)
存储过程内部中子语句转储的最小执行时间。
说明:该参数可支持单位是毫秒、秒、分钟、小时、天,默认单位为秒,参数设置示例如下:
SET resource_track_subsql_duration='0.1s';
SET resource_track_subsql_duration=0.1;
SET resource_track_subsql_duration='0.01min'; # 由于该参数的默认单位是秒,采用四舍五入的方式,因此该处设置的值为1s,而不是0.6s。
RESOURCE_TRACK_COST(0)
设置对当前会话的语句进行资源监控的最小执行代价。
RESOURCE_TRACK_LEVEL(QUERY)
设置当前会话的资源监控的等级,默认为query级别。
RESOURCE_TRACK_APPLICATION
设置TopSQL记录指定客户端下发的语句,目前支持设置的客户端为:gs_rewind、cm_agent、pgxc_clean、gs_clean、gs_running_xacts、OM、wlm。设置多个客户端时,相邻的值需用英文逗号隔开
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"
通过管控面设置TopSQL相关参数:

查询TopSQL相关GUC参数设置的常用SQL:
select name,setting from pg_settings where name like '%resource%';
视图常用分析字段:
| 字段名称 | 字段描述 | 分析出可能的现象 |
A2: block_time较小,而duration值较大,说明用户作业执行时间增加较大原因是自己导致,需要继续分析数据量的变化情况、各DN的执行时间变化。 | ||
并且,query_plan能够直接显示作业的执行计划,对比执行计划是否有变化。 | ||
总结:
因数据量变化,导致作业执行时间增加,可以分析A2/B1/D1/G1,进而确认作业查询的数据表是否有明显的数据量增加;
由于目前历史TopSQL视图字段信息较多,建议使用PGXC_QUERY_INFO视图查看查询经典字段信息,无需手动过滤无关字段。
因其它并发作业抢占,导致作业排队,从而导致作业执行时间增加,可以分析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语句的规格是:
1. 不记录特殊数据定义语句,如:SET、RESET、SHOW、ALTER SESSION SET、SET CONSTRAINTS语句;
2. 记录数据定义语句,例如:执行CREATE、ALTER、DROP、GRANT、REVOKE和VACUUM语句;
3. 记录数据操作语句,例如:
① 执行SELECT、INSERT、UPDATE和DELETE语句。
② 执行explain analyze和explain performance场景。
③ 使用query级别/perf级别视图。
4. 记录函数与存储过程的调用入口语句,当GUC参数enable_track_record_subsql开启的情况下,可记录存储过程的部分内部语句(declare定义语句除外),仅会记录其中下发到DN执行的内部语句,未下发到DN执行的内部语句会被过滤掉;
5. 记录匿名块语句,当GUC参数enable_track_record_subsql开启的情况下,可记录匿名块中的部分内部语句,仅会记录其中下发到DN执行的内部语句,未下发到DN执行的内部语句会被过滤掉。
6. 记录游标语句,当游标并非从缓存中读取数据,而确实触发语句下发到DN上执行的条件下,该游标语句会被记录,并且会进行语句、执行计划增强,但当游标从缓存中读取数据时,不进行记录;当游标语句在匿名块或者函数中使用时,当游标从DN上读取较多数据但不完全使用时,因当前架构限制,无法记录该游标在DN上的监控信息。对于With Hold游标,该语法执行逻辑特殊,会在事务提交阶段执行实际查询动作,当语句在该阶段执行报错时,作业的aborted状态无法反馈到TopSQL历史表中。
7. 重分布过程中的作业不统计。
8. JDBC执行的带占位符语句,通常会补齐参数内容,但如果参数和原语句合起来长度超过64KB,则不记录参数,或者如果是轻量化语句,直接下发到DN上执行,不记录参数。如果普通语句语句超过64KB,在TopSQL的query字段记录中将被截断。
9. 从8.1.3集群版本开始,query、perf级别TopSQL运行时监控功能已完全不影响查询性能,对当前会话的语句进行资源监控的GUC参数resource_track_cost默认值已修改为0,查询TopSQL实时监控视图时,默认会显示所有正在执行的语句。
10. 从8.1.3集群版本开始,对于存储过程中的子语句监控功能,如果在查询TopSQL实时监控视图的会话中,开启控制子语句记录归档功能的GUC参数enable_track_record_subsql,不论业务语句中是否开启子语句监控开关,查询TopSQL实时监控视图的结果都能看到执行语句的子语句运行信息。
11. 关于存储过程中子语句的监控功能即enable_track_record_subsql,8.1.3集群版本中建议不要全面开启,由于没有按时间过滤子语句的功能,全面开启可能会记录过多子语句,导致归档的监控表占用大量磁盘空间;8.1.3集群版本建议仅用于查询实时监控信息,或对个别存储过程业务做定位分析时,仅开启对应会话中的参数。8.2.1版本新增GUC参数resource_track_subsql_duration(默认值为180秒),可以通过执行时间过滤需要归档的子语句,用户可以按需调整该值大小。
12. 由于规格限制,对于未下盘的主语句,TopSQL历史表中的记录会有延时,等待下次作业下发时才会显示在TopSQL历史表中。
13. 从8.2.1.200集群版本开始,新增operator_realtime级别TopSQL运行时监控,提供算子级实时监控的能力,开启此级别的监控可以查询语句的执行计划以及具体执行信息,查询TopSQL算子级实时监控视图时,默认会显示所有正在执行的语句。但是对于存储过程和游标场景,暂时不支持显示算子级实时监控信息。另由于查询所有语句的信息对于CN内存压力较大,为了不影响作业性能,为用户提供查询单个语句的函数pg_stat_get_wlm_realtime_operator_info(queryid),可以通过该函数查询指定语句的算子执行信息。
14. operator_realtime级别TopSQL运行时监控对于CN轻量化和存储过程的情况,暂时不支持。另由于算子执行速度较快的原因,对于算子信息的显示会有一定滞后性。
15. query级别的作业监控和operator的算子监控中的spill_size字段,由于统计维度不同,会有一定差异,query级别监控监控的语句实际下盘文件大小,算子监控的是具体算子在逻辑层IO读写的数据量。
16. 当GUC参数enable_stream_operator设置为off状态时,算子执行信息存在显示不准的情况。
在813版本中,除初始用户外,enable_gtm_free开启且关联队列不管控情况下,用户作业不进入资源管控。此时实时与历史TopSQL均不记录该用户下发的作业。
TopSQL视图warning字段解析:
| warning告警信息分类 | 具体信息 | 解决方法 |
| Early spill | ||
| Spill times is greater than 3 | ||
| Spill on memory adaptive | ||
| Hash table conflict | ||
识别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%';
现网中有概率出现某一个SQL原先执行比较快,后来发现该语句执行时间变长,此时就需要利用TopSQL中的query_plan以及其他资源信息进行分析定位,毋庸置疑,query_plan中的信息越详细,越接近于explain performance,定位过程就更容易。query级别与perf级别的TopSQL差异主要在query_plan,perf级记录更详细执行信息,较query级性能损耗5%以内,perf级别的TopSQL的query_plan字段可显示算子的时间actual_time,memory,rows和buffer等信息。

在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,结果如下:

不同视图从不同维度统计业务在数据库中的运行状态,因为或多或少都包含部分同类字段,可用于后续关联分析,此处给出历史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;
select * from gs_stack('nodename',tid); # 本节点可直接 \! gstack lwtid
作业下发执行后,内核通过打桩记录作业的各项资源信息,如内存、CPU、下盘、IO和网络信息等,作业执行完成后该资源信息后先存在无锁队列中,资源管理后台辅助线程将该数据信息定期转储到dbms_om.gs_wlm_session_info系统表中,后续通过topsql_retention_time定期老化数据。

通常情况下,TopSQL记录的信息较多, 查询时可使用start_time做条件,避免全表查询,且使用limit对结果集大小限制,防止结果集过大导致客户端OOM。
案例1:某客户集群出现系统级性能问题,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进行下线并进行差分优化后,问题解决。
案例2:业务语句性能下降,业务曾经比较快,后来变慢了
因为topsql记录了一些语句的执行情况和资源消耗情况,在定位性能问题的时候非常有帮助,比如周期执行的sql,突然有一天变慢了,我们可以通过分析语句的执行时间和阻塞时间判断是语句被阻塞了(排队等)还是执行的慢了,通过记录的执行计划分析语句为什么慢了,是不是当时的统计信息统计不准,还是没有对表做analyze,也可以看下盘量大小分析是不是下盘量大造成的性能变慢。
步骤1:通过pgxc_wlm_session_info视图确认历史的SQL信息(注:历史topsql视图按天分区,查询时尽量带start_time条件)
select * from pgxc_wlm_session_info where start_time > '2023-08-22 00:00' and start_time < '2023-08-24 00:00' and query ilike '%XXXXXXXXX%' order by start_time;
步骤2:根据作业的sql_hsah信息确定作业的历史执行情况,借助历史TopSQL的资源信息和query_plan字段分析作业性能劣化原因。
select start_time, block_time, duration, sql_hash, warning, max_peak_memory, max_spill_size, query_plan from pgxc_wlm_session_info were start_time > 'xxxx-xx-xx xx:xx' and sql_hash = 'xxx' order by start_time desc limit 10;
找到对应的快慢语句后,对比其执行计划query_plan,发现执行计划跳变严重

步骤3:对相应的表做analyze后,恢复合理计划,语句性能恢复。
统计信息不准,导致计划跳变,是十分常见语句变慢原因,analyze不影响读写,遇见语句变慢可预先做analyze。
analyze dwrdim_dw1.dwr_dim_region_rc_d;
案例3:作业长时间运行不结束
在作业无排队无死锁正常运行期间,发现作业长时间不结束,此时可查看算子级别的实时TopSQL监控,能够看出哪个算子执行时间长,通过算子执行时间和已处理行数等信息,确定是否需要查杀SQL。
步骤1:开启实时算子监控:set resource_track_level = 'operator_realtime';

步骤2:根据需要查杀作业
select * from pg_terminate_backend(xxx); # 入参为pid
案例4:作业整体使用内存较高,需分析算子内存使用情况
当发现作业内存整体使用较高,query级别的TopSQL无法记录算子的实际资源信息,此时可以设置TopSQL的监控级别为perf,query级别与perf级别的TopSQL差异主要在query_plan,perf级记录更详细执行信息,较query级性能损耗5%以内,perf级别的TopSQL的query_plan字段可显示算子的时间actual_time,memory,rows和buffer等信息。
步骤1:开启perf级别TopSQL监控::set resource_track_level = 'perf';
步骤2:作业执行结果后,查询历史TopSQL视图pgxc_wlm_session_info查看各个算子的内存使用情况。

DWS最佳实践 | 行列混存机制:更灵活的查询能力和更高的数据利用效率
戳阅读原文,了解更多






