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进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。