可以通过查询系统表来找到目前处于活跃状态的SQL:
SELECT * FROM pg_stat_activity WHERE datname='数据库名'
and client_addr = '发起查询的IP地址' order by state_change desc;
有个更好的办法,是安装扩展pg_stat_statements
,此处需要PostgreSql支持,部分版本需要编译安装:
CREATE extension pg_stat_statements;
SELECT pg_stat_reset();
SELECT pg_stat_statements_reset();
等待一段时间后就可以查询慢SQL:
SELECT * FROM pg_stat_statements ORDER BY total_time DESC LIMIT 5;
查询使用Buffer次数最多的SQL:
SELECT * FROM pg_stat_statements ORDER BY shared_blks_hit+shared_blks_read DESC LIMIT 5;
explain
可以看到sql的执行计划(但不会去执行这条sql),explain analyze
或者explain analyse
则可以看到真正执行sql时的执行计划。
对于已经能够确定其性能很慢的sql不建议使用explain analyze
,除非你想慢慢等它执行完再看到对应的执行计划。
PostgreSQL的执行计划会显示出这条SQL的预估成本cost
,需要扫描的数据行数量rows
,扫描方式(是否使用索引等),循环次数loops
等。执行计划中会使用缩减符和->
来表示执行时每一步的先后顺序,缩减最大的就是最早执行的SQL片段。
cost
就是执行对应的SQL片段时所需要的预估成本,包含启动成本和结束成本。不同的扫描方式其启动成本不一定一样,每一步的cost都会包含上一步的成本。
width
表示扫描的数据行宽度,width=0
表示只获取行的位置,没有读取数据;开始读取数据后其值会大于0。
扫描方式常见的有:
通过分析执行计划中的成本,以及扫描方式来决定下一步怎么对SQL进行优化,下面是一些常见的调优方案。
主要分为两个方向:
以上都是为了尽可能减少中间表的数据量,通过执行计划就可以很明显看到表连接的cost大幅降低。
另外,在能使用inner join时尽量不要使用left join,inner join可以过滤掉不少不必要的数据,从而减少中间表的数据量。
公用表表达式(Common Table Expression,简称CTE),对于一个很长很复杂的sql,可以用CTE把一部分sql片段预先查询出来,该sql片段查询的结果可以被整个sql所使用。类似于在代码中抽出一个公共的方法逻辑,方便被其他方法所使用。
CTE不仅提高了可读性,还可以非常有效地提高一条复杂长sql的查询效率,多个CTE之间可以用,
分隔。语法是with <表名> as ()
,如果被CTE定义的表名被调用两次以上,则优化器会自动将预查询的数据放入一个TEMP表中,如果只被调用一次则不会。
但不是所有数据库都有实现这个功能,PostgreSQL和SQL SERVER都有提供。样例如下:
with pre as (
select trim(both '{}' from ch.path) as "preLabelKey",id from cnt_codelist_book_h ch limit 2
), pre2 as (
select regexp_split_to_table(trim(both '{}' from ch.path), '}[^}]*{') as "pre2LabelKey",id from cnt_codelist_book_h ch limit 2
)
select distinct book.path,pre."preLabelKey",pre2."pre2LabelKey" from cnt_codelist_book_h book
inner join pre on pre.id = book.id
inner join pre2 on pre2.id = book.id;
--查询结果如下:
path preLabelKey pre2LabelKey
{lbl.codelist} > {lbl.common.version} lbl.codelist} > {lbl.common.version lbl.codelist
{lbl.codelist} > {lbl.common.version} lbl.codelist} > {lbl.common.version lbl.common.version
注:上述sql中由于别名存在大小写,因此要用双引号包括起来,否则会报错。另外这里仅仅演示语法,因此写的很随意,不喜勿怪。
这个就不详说了,不外乎对查询条件建立索引,注意使用联合索引时的字段顺序,不过PostgreSQL对于联合索引似乎会自动优化查询时的字段顺序。