暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PG14对提升嵌套循环连接的性能的优化

阎书利 2025-01-22
91

PG14里增加了一个可以缓存嵌套循环连接内参数化扫描的结果的功能,用enable_memoize来开启关闭,主要作用是:是否允许查询使用 memoize 来缓存嵌套循环连接内参数化扫描的结果。

什么是memoization

假设y = f(x),memoization意味着我们可以在任何计算中用y替代f(x)。例如,无论你计算多少次UPPER(‘x’),你总会得到’X’。如果这种函数的计算成本很高,并且只有很少的可能输入值,那么维护一个映射所有先前输入值的哈希图并使用它来查找已知(或至少是频繁的)值,而不是再次计算它们,这样的效率会很高。

PostgreSQL-14里的这个enable_memoize选项,可以决定是否缓存嵌套循环连接内参数化扫描的结果,这意味着优化器可以用仅取决于计算输入值的缓存值替换任何计算。相关子查询是一个函数,其输入参数是谓词和对外部查询列的其他引用。因此,相关子查询的结果可以被缓存或记忆。

这有点类似于ORACLE 11g 里引入的一项称为标量子查询缓存(scalar subquery caching)的功能,如果使用标量子查询缓存,ORACLE会将子查询结果缓存在哈希表中,如果后续的记录出现同样的值,优化器通过缓存在哈希表中的值,判断重复值不用重复调用函数,直接使用上次计算结果即可。从而减少调用函数次数,从而达到优化性能的效果。

测试enable_memoize选项

--两个表都有100000 行。 --t.j只有 5 个不同的值,每个值出现 20000 次。 --u.j有 20000 个不同的值,每个值出现 5 次。 CREATE TABLE t AS SELECT i, i % 5 AS j FROM generate_series(1, 100000) AS t(i); CREATE TABLE u AS SELECT i, i % 20000 as j FROM generate_series(1, 100000) AS t(i); CREATE INDEX uj ON u(j);
复制

开启enable_memoize选项后

postgres=# SET enable_memoize = ON; SET postgres=# EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, BUFFERS) postgres-# SELECT * FROM t JOIN u ON t.j = u.j; +-------------------------------------------------------------------------------+ | QUERY PLAN | +-------------------------------------------------------------------------------+ | Nested Loop (actual rows=500000 loops=1) | | Buffers: shared hit=483 | | -> Seq Scan on t (actual rows=100000 loops=1) | | Buffers: shared hit=448 | | -> Memoize (actual rows=5 loops=100000) | | Cache Key: t.j | | Cache Mode: logical | | Hits: 99995 Misses: 5 Evictions: 0 Overflows: 0 Memory Usage: 2kB | | Buffers: shared hit=35 | | -> Index Scan using uj on u (actual rows=5 loops=5) | | Index Cond: (j = t.j) | | Buffers: shared hit=35 | | Planning: | | Buffers: shared hit=6 | | Planning Time: 1.399 ms | | Execution Time: 58.676 ms | +-------------------------------------------------------------------------------+ (16 rows)
复制

关闭enable_memoize选项后

postgres=# SET enable_memoize = OFF; SET postgres=# EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, BUFFERS) SELECT * FROM t JOIN u ON t.j = u.j; +-----------------------------------------------------------+ | QUERY PLAN | +-----------------------------------------------------------+ | Hash Join (actual rows=500000 loops=1) | | Hash Cond: (t.j = u.j) | | Buffers: shared hit=896 | | -> Seq Scan on t (actual rows=100000 loops=1) | | Buffers: shared hit=448 | | -> Hash (actual rows=100000 loops=1) | | Buckets: 131072 Batches: 1 Memory Usage: 4931kB | | Buffers: shared hit=448 | | -> Seq Scan on u (actual rows=100000 loops=1) | | Buffers: shared hit=448 | | Planning: | | Buffers: shared hit=6 | | Planning Time: 0.317 ms | | Execution Time: 60.337 ms | +-----------------------------------------------------------+ (14 rows)
复制

参考文章

https://blog.jooq.org/postgresql-14s-enable_memoize-for-improved-performance-of-nested-loop-joins/

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论