七叶笔记 » 数据库 » Postgresql的select优化操作(快了200倍)

Postgresql的select优化操作(快了200倍)

意在找出AS01-L113站位最近一条有效的记录,而这条sql有足够的index支持,但耗时高达2.7s。

F7查一下,居然包含f96_result_type的这支索引也参与运算, 这就出事了, 因为f96_result_type有相同值的记录极其多,从图3也可以看出, 这也是主要耗费时间的环节。

通过数据分析, f96_mgtbarcd值相同的记录数很少, 所以我们可以将顺序改一下,先用f96_mgtbarcd作为过滤器生成一个子集后, 再从这个子集里面做f96_result_type等过滤, 跑了一下,13ms, 足足快了这样就快200多倍,如下:

ps:我用的工具是 pgAdmin自带的,F7, Shift-F7

补充:PostgreSql查询优化之根据执行计划优化SQL

1、执行计划路径选择

postgresql查询规划过程中,查询请求的不同执行方案是通过建立不同的路径来表达的,在生成许多符合条件的路径之后,要从中选择出代价最小的路径(基于成本运算),把它转化为一个计划,传递给执行器执行,规划器的核心工作就是生成多条路径,然后从中找出最优的那一条。

1.1代价评估

评估路径优劣的依据是用系统表pg_statistic中的统计信息估算出来的不同路径的代价(cost),PostgreSQL估计计划成本的方式:基于统计信息估计计划中各个节点的成本。PostgreSQL会分析各个表来获取一个统计信息样本(这个操作通常是由autovacuum这个守护进程周期性的执行analyze,来收集这些统计信息,然后保存到pg_statistic和pg_class里面)。

1.2用于估算代价的参数postgresql.conf

也可以使用 show all的方式查看

1.3 路径的选择

首先更新统计信息vacuum analyze t_jcxxgl_tjaj,许多时候可能因为统计信息的不准确导致了不正常的执行计划--执行计划。

如上,d_slrq是有索引的,但是执行计划中并没有走索引,为什么呢?我们继续往下看。

d_slrq上面有btree索引,但是查看执行计划并没有走索引,这是为什么呢?

代价计算:

一个路径的估算由三部分组成:启动代价(startup cost),总代价(totalcost),执行结果的排序方式(pathkeys)

代价估算公式:

总代价=启动代价+I/O代价+CPU代价(cost=S+P+W*T)

P:执行时要访问的页面数,反应磁盘的I/O次数

T:表示在执行时所要访问的元组数,反映了cpu开销

W:表示磁盘I/O代价和CPU开销建的权重因子

统计信息:

统计信息的其中一部分是每个表和索引中项的总数,以及每个表和索引占用的磁盘块数。这些信息保存在pg_class表的reltuples和relpages列中。我们可以这样查询相关信息:

total_cost = 1(seq_page_cost)*8(磁盘总页数)+0.01(cpu_tuple_cost)*141(表的总记录数)+0.0025(cpu_operation_cost)*141(表的总记录数)=9.7625

可以看到走索引的cost=13.90比全表扫描cost=9.76要大。所以上面没有关闭全表扫描的时候,根据成本代价,执行计划走的全表扫描。在表较小的情况下,全表扫描比索引扫描更有效, index scan 至少要发生两次I/O,一次是读取索引块,一次是读取数据块。

2、一个SQL优化实例

2.1慢SQL:

慢sql耗时:7s

先过下这个sql是干什么的、首先dbxx和dbaj的一个join连接然后dbaj.c_ajbh要包含在zbaj表里面,做了个排序,取了15条记录、大概就这样。

Sql有个缺点就是我不知道查询的字段是从那个表里面取的、建议加上表别名.字段。

查看该sql的表的数据量:

t_zhld_db :1311

t_zhld_ajdbxx :341296

t_zhld_zbajxx :1027619

执行计划:

执行计划解读:

1:第27->21行,通过索引i_tzhldzbajxx_zblx_dbzt过滤表t_zhld_zbajxx的数据,然后根据过滤条件(c_gy)::text = '2550'::text过滤最终返回8605条数据

2:第17->15行,根据条件过滤t_zhld_db表的数据,最终返回了14条数据

3:第20->19行,对表t_zhld_zbajxx做group by的操作

4:第13->11行,全表扫描t_zhld_ajdbxx 最终返回了8605条数据

5:第08行,根据t_zhld_ajdbxx返回的8605条结果集作为驱动表和t_zhld_db的结果集(14条)做嵌套循环,t_zhld_db的结果集被循环了8605次。然后过滤掉了其中的111865条记录,那么最终将得到(8605*14-111865) = 8605

6:第07->05行,根据第08和18行返回的结果集最终做了Nested Loop Semi Join,第18行的4303条结果集被循环了8605次,(4303*8605-37018710)=8605

7: 第04->02行,对最终的8605条记录进行排序

8:第01行,limit最终获取15条记录

整个执行计划中耗时最长的地方在05行Nested Loop Semi Join,actual time=277.794..88932.662, 表db_zxzhld.t_zhld_db dbxx和db_zxzhld.t_zhld_ajdbxx均是全表扫描

2.2具体优化步骤

查看索引页并没有索引,创建c_ajbh,c_dbbh等逻辑外键的索引

创建d_larq,c_ajbh的排序索引:

创建索引后执行计划有了改变,原来的dbaj表和dbxx表先做nestedloop变成了zbaj和dbaj表先做了nestedloop join,总的cost也从36328.68降到了12802.87,

执行计划

执行的时间还是要4s左右仍然不满足需求,并且没有使用上I_T_ZHLD_AJDBXX_m6这个索引。

2.3等价改写SQL(1)

等价改写:将排序条件加入db_zxzhld.t_zhld_ajdbxx让其先排序,再和t_zhld_db表连接。

修改后sql:

再次查看执行计划:

这一次可以看出,ajdbxx和zbajxx表做了hash semi join 消除了nestedloop,cost降到了3231.97。并且使用上了i_t_zhld_ajdbxx_m6子查询中in的结果集有一万多条数据。

继续尝试使用exists等价改写in,看能否有更好的结果

2.4等价改写SQL(2)

等价改写:将in替换为exists:

再次查看执行计划:

可以看出使用exist效果更好,最终cost 2547.17

(1).少了t_zhld_zbajxx表的group by操作:Sort Key: t_zhld_ajdbxx.d_larq, t_zhld_ajdbxx.c_ajbh。(这一步是因为使用了索引中的排序)

(2).少了分组的操作:Group Key: t_zhld_zbajxx.c_ajbh。

第(2)为什么这个查询消除了t_zhld_zbajxx表的group by操作呢?

原因是exists替换了distinct的功能,一旦满足条件则立刻返回。所以使用exists的时候子查询可以直接去掉distinct。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持七叶笔记。如有错误或未考虑完全的地方,望不吝赐教。

相关文章