背景
数据库运维过程中,dba都比较关注sql的执行时间。研发在数据库应用开发上,速度慢的sql比比皆是,很多速度很慢都是sql写得不好,效率不高,执行过程中也会造成数据库的负载过大。比如无用的去重,无效的条件,不必要的子查询,sql用不上的索引。而对于这些不符合要求的sql,首先要去把这些sql找出来。
措施
pg数据库提供了统计信息的功能来查找,下面介绍两种办法:
- 修改日志参数,记录超过指定时间的sql,以及当时的执行时间
- 通过pg_stat_statements统计
修改日志参数
log_min_duration_statement:从log中找出执行超过一定时间的sql。这个参数是设置执行最小多长时间的sql输出到log,例如输出执行超过3秒的sql,可以设置log_min_duration_statement = 3s。这个参数设置为-1是无效,默认为-1。设置为0是输出所有的sql,但这样会增加服务器的负担,一般不要设置太低的值。
auto_explain功能:在Postgresql8.4版本后新增了该功能。默认这个功能是不可用的,需要配置如下。这样系统在执行的时候遇到超过2秒的SQL的话,,会自动把sql输出到log,这样看log就更容易找到问题点了。
shared_preload_libraries = 'auto_explain'
custom_variable_classes = 'auto_explain' #PostgreSQL9.2版本后此参数已取消,不需要设置
auto_explain.log_min_duration = 2s
实际log查看:
< 2019-11-12 15:40:30.284 CST > 日志: 执行时间: 3048.397 ms 语句: SELECT * FROM indexedresult WHERE searchtitle LIKE '%好%' ORDER BY searchtitle
加载pg_stat_statements模块
pg_stat_statements模块提供了一种方法,用于跟踪所有由服务器执行的sql语句的统计,例如,语句总调用次数,总执行时间,从内存中读取的块数,从磁盘读取的块数等信息。在添加或删除模块pg_stat_statements模块时,需要额外的共享内存,所以必须重启数据库。pg_stat_statements模块加载会消耗部分的内存,可以通过pg_stat_statements.max * track_activity_query_size来计算。这个值是比较小的,假如pg_stat_statements.max 值为 10000, track_activity_query_size值为4096,也就消耗了40M内存。
参考配置如下:
shared_preload_libraries = 'pg_stat_statements '
track_activity_query_size = 4096 #SQL文本的最大大小,4K
custom_variable_classes = 'pg_stat_statements ' #PostgreSQL9.2版本后此参数已取消,不需要设置
pg_stat_statements.max = 10000 #跟踪模块中的语句的最大数目
pg_stat_statements.track = all
参数配置好后,需要重启数据库。注意该模块是区分数据库的,不是全局共享的。如果无法查询,可能需要在对应的数据库下面加载pg_stat_statements模块。运行如下语句:
CREATE EXTENSION pg_stat_statements;
配置好pg_stat_statements模块后,经过一段时间的运行,我们就可以通过pg_stat_statements视图来统计效率低的SQL,语句如下:
--查询语句总调用次数大于10次,平均运行时间倒序的SQL
SELECT t.userid,
t.dbid,
t.query || ';',
t.calls,
t.total_time,
t.rows,
t.total_time / t.calls
FROM pg_stat_statements t
WHERE (t.calls IS NOT NULL OR t.calls <> 0)
AND t.query !~ '^COPY|<insufficient privilege'
AND t.calls > 10
ORDER BY 7 DESC;
总结
上面两种方法可以根据实际业务需要来进行,比如仅仅需要查看问题sql的情况可以用第一种,如果需要深入分析sql的类别,执行次数,执行时间等更丰富的信息可以采用第二种方式,根据调用的次数来设计缓存也是非常重要的依据。