PostgreSQL 10 - 查询优化 1

本文深入探讨了PostgreSQL查询优化器的工作原理,包括评估join选项(如Nested loops、Hash joins、Merge joins)、转换策略(如内联视图、消除子查询、等式约束)、详尽的搜索以及执行计划的理解。通过示例展示了优化器如何选择最佳执行路径,强调了索引、连接顺序和成本估算的重要性。
摘要由CSDN通过智能技术生成

查询优化器

假设,我们有三张表,一个视图,每张表都有索引:

CREATE TABLE a (aid int, ...); -- 一亿行
CREATE TABLE b (bid int, ...); -- 二亿行
CREATE TABLE c (cid int, ...); -- 三亿行

CREATE INDEX idx_a ON a (aid);
CREATE INDEX idx_b ON b (bid);
CREATE INDEX idx_c ON c (cid);

CREATE VIEW v AS SELECT * FROM a, b WHERE aid = bid;

假设用户想运行下面的查询。优化器会做什么?它的选择是什么?

SELECT *
FROM v, c
WHERE v.aid = c.cid AND cid = 4;

在看真是的优化器如何处理之前,先看看优化器有什么选项。

评估join选项

假设优化器从头开始,计算视图的输出。连接一亿行和两亿行有什么好办法?

Nested loops

一个办法是使用嵌套循环。原理很简单,下面是伪代码:

for x in table1:
    for y in table2:
        if x.field == y.field
            issue row
        else
            keep doing

嵌套循环常用于一方很小,数据不多的时候。而我们的例子,要处理那么多数据,显然不会这样处理。

Hash joins

可以使用下面的策略,解决我们的问题:

← Hash join
    ← Sequentialscan table 1
    ← Sequentialscan table 2

双方都被hash,然后比较hash key,返回join结果。问题是所有的值都需要做hash,并暂存在某处。

Merge joins

最后是归并连接-使用排序列表连接返回值。如果要连接的双方都已经排好序了,系统能从顶部取一些行,如果匹配就返回。所以,最重要的是数据要先排序。计划是这样的:

← Merge join
    ← Sort table 1
        ← Sequential scan table 1
    ← Sort table 2
        ← Sequential scan table 2

连接的一方或者双方可以使用来自计划的底层的排序数据。如果直接访问表,索引是一个很好的选择,但是前提是返回的数据比整个表小很多。否则,我们的开销会增加很多-先读整个索引,再读整个表。如果返回集是表的很大一部分,顺序扫描更高效-尤其是以主键顺序访问的时候。
使用归并连接,可以处理很多数据。但是,数据必须先排序,或者从索引读数据。
排序成本是_O(n * log(n))_。因此,为3亿行数据排序也是没什么吸引力。
这几种连接都支持并行版本。

转换

既然几种连接都不可行。可以考虑使用逻辑转换加速查询。要执行下列几步:

内联视图

SELECT * FROM
    (SELECT * FROM a, b WHERE aid = bid) AS v, c
WHERE v.aid = c.cid AND cid = 4;

该视图是内联的,转换成了子查询。这样,为进一步的优化打开了大门。

消除子查询

SELECT * FROM a, b, c WHERE a.aid = c.cid AND aid = bid AND cid = 4;

消除子查询以后,变成了这样的。现在是一个普通连接。

等式约束

下来增加等式约束(equality constraints)-检测其他约束,连接选项和过滤等。让我们仔细观察一下这个查询:如果aid = cid而且aid = bid,我们知道bid = cid。如果cid = 4,则其他几个也等于4:

SELECT *
FROM a, b, c
WHERE a.aid = c.cid
    AND aid = bid
    AND cid = 4
    AND bid = cid
    AND aid = 4
    AND bid = 4

这几列都可以使用索引,就不使用可怕的视图了。PostgreSQL选择从索引检索几行以后,使用什么连接都可以了。

详尽的搜索

现在,所有的形式转换都结束了,PostgreSQL会执行详尽的搜索。它会试各种可能的计划,使用最廉价的方案。PostgreSQL还要决定最好的连接顺序。原始查询中,连接顺序是A → B和A → C。等式约束以后,可能先连接B → C,然后再连接A。优化器对所有的选项都持开放的态度。

试一试

PostgreSQL可能选择这样的执行计划:

postgres=# explain SELECT * FROM v, c WHERE v.aid = c.cid AND cid = 4;
QUERY PLAN
----------------------------------------------------------------
Nested Loop (cost=1.71..17.78 rows=1 width=12)
    -> Nested Loop (cost=1.14..9.18 rows=1 width=8)
        -> Index Only Scan using idx_a on a
            (cost=0.57..4.58 rows=1 width=4)
            Index Cond: (aid = 4)
        -> Index Only Scan using idx_b on b
            (cost=0.57..4.59 rows=1 width=4)
            Index Cond: (bid = 4)
    -> Index Only Scan using idx_c on c
        (cost=0.57..8.59 rows=1 width=4)
        Index Cond: (cid = 4)
(8 rows)

PostgreSQL会使用三个索引,使用嵌套循环连接数据。

使过程失败

你看到了,PostgreSQL可以为查询提速。PostgreSQL够聪明,但是也需要聪明的用户。用户做了愚蠢的事情,也会影响优化效果。我们先删掉视图:

DRO
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值