sor*_*ell 3 postgresql optimization execution-plan postgresql-12
我正在对我公司的一些 SQL 进行一些性能基准测试,将 PG10 与 PG12 进行比较。我们在代码中使用了很多CTE,而 PG12 并没有对 CTE 进行原生优化,因此 PG10 和 PG12 之间的性能是相同的。
我的下一个实验是将NOT MATERIALIZED指令添加到 CTE,结果令人震惊:它大大缩短了查询时间(在某些情况下将它们减半)。
我在这里读到这MATERIALIZED是 PG12 之前的默认功能。该功能会将 CTE 的所有内容写入一个临时位置。
所以我的问题主要是NOT MATERIALIZED:
NOT MATERIALIZED功能对幕后的数据MATERIALIZED有何作用? NOT MATERIALIZED在重构我们的代码库之前,我应该注意哪些副作用?文档中对此进行了很好的解释。
WITH 查询的一个有用特性是,它们通常在每次执行父查询时仅评估一次,即使父查询或同级 WITH 查询多次引用它们。因此,可以将在多个地方需要的昂贵计算放在 WITH 查询中以避免冗余工作。另一个可能的应用是防止对具有副作用的函数进行不必要的多次评估。
到目前为止,很好,但是:
然而,这个硬币的另一面是优化器无法将限制从父查询下推到多引用 WITH 查询,因为这可能会影响 WITH 查询输出的所有使用,而它应该只影响一个。多重引用的 WITH 查询将被评估为写入的,而不会抑制父查询之后可能丢弃的行。
因此,正如给出的示例中所指出的,如果您有这样的查询:
WITH w AS (
SELECT * FROM big_table -- big_table has an INDEX on a field called key!
)
SELECT * FROM w AS w1
JOIN w AS w2 ON w1.key = w2.ref -- w is called TWICE, so DEFAULT is MATERIALIZED
-- PostgreSQL can't take advantage of big_table.key
WHERE w2.key = 123;
Run Code Online (Sandbox Code Playgroud)
所以,在这种情况下:
WITH 查询将被具体化,生成 big_table 的临时副本,然后与自身连接——没有任何索引的好处
最好有:
WITH w AS NOT MATERIALIZED (
SELECT * FROM big_table
)
SELECT * FROM w AS w1 JOIN w AS w2 ON w1.key = w2.ref
WHERE w2.key = 123;
Run Code Online (Sandbox Code Playgroud)
所以,优化器可以“折叠”的CTE查询“进入”主查询和利用的INDEX在key现场big_table!
关于。对DEFAULT的NOT MATERIALIZED:
但是,如果 WITH 查询是非递归且无副作用的(即,它是一个不包含 volatile 函数的 SELECT),则可以将其折叠到父查询中,从而允许对两个查询级别进行联合优化。默认情况下,如果父查询只引用一次 WITH 查询,而不是多次引用 WITH 查询,则会发生这种情况。
所以DEFAULT是NOT MATERIALIZED如果:
the_query IS NOT recursive
AND the_query is_side_effect_free
AND the_query is_run_only_once
Run Code Online (Sandbox Code Playgroud)
否则你必须告诉 PostgreSQL 使用NOT MATERIALIZED.
我看到的唯一小问题是需要进行测试以查看是否
NOT MATERIALIZED有改进?我可以看到根据表大小、选择的字段以及 CTE 中使用的字段和表的索引,两者之间的平衡会发生变化的情况——换句话说,知识和经验是无可替代的。DBA 还没死呢!:-)
| 归档时间: |
|
| 查看次数: |
1439 次 |
| 最近记录: |