帮你快速理解、总结文档立即下载

启发式优化器规则

最近更新时间:2026-07-21 09:53:00

我的收藏
云数据库 PostgreSQL 支持启发式优化器插件 tencentdb_heuristic,本文为您介绍关于 tencentdb_heuristic 的说明。

功能概述

enable_nl2hj:当外层小、内层大且外层无 Param 关联时,惩罚 NestLoop、让 HashJoin 胜出。
enable_index:在满足 SAME / UNIQUE / INCLUDE 等子条件时偏置索引路径(可与 enable_seqscan 一同调节)。
enable_danger_joinkeys:对配置为“danger”的连接列,对 MergeJoin/HashJoin 施加可调惩罚,倾向于回到 NestLoop 或换列。

前提条件

PostgreSQL 18,版本 ≥ v18.4_r1.10。

使用限制

该插件需要通过 shared_preload_libraries 或 LOAD 加载生效。
所有规则均为成本调整 / 候选路径过滤,不改变查询结果集,仅改变 EXPLAIN 形状与执行时间。
主开关 tencentdb_heuristic.enable 关闭时,所有子规则一律失效。

创建插件

检查 shared_preload_libraries 已包含 tencentdb_heuristic:
postgres=> SHOW shared_preload_libraries;
创建扩展:
CREATE EXTENSION tencentdb_heuristic;

SELECT * FROM pg_extension WHERE extname = 'tencentdb_heuristic';
会话级临时加载(仅当前 backend 生效):
LOAD 'tencentdb_heuristic';

参数总览

主开关与子开关

参数
类型
默认
说明
tencentdb_heuristic.enable
bool
off
插件总开关,关闭则所有子规则失效。
tencentdb_heuristic.enable_index
bool
off
开启索引路径启发式偏置。
tencentdb_heuristic.enable_seqscan
bool
on
是否参与 seqscan 与 index 的对比,配合 enable_index 使用。
tencentdb_heuristic.enable_nl2hj
bool
off
开启 NestLoop→HashJoin 惩罚规则。
tencentdb_heuristic.enable_danger_joinkeys
bool
off
对危险 join key 上的 HashJoin/MergeJoin 施加惩罚。

数值调参

参数
类型
默认
说明
tencentdb_heuristic.nl2hj_startup
int
10
外层行数低于该阈值时优先保留 NL;反之考虑改写为 HJ。
tencentdb_heuristic.nl2hj_magnify
int
10
NL→HJ 规则的成本放大因子。
tencentdb_heuristic.nl2hj_sameouter_nl_inner_rows_startup
int
50
sameouter-NL 保护:内层行低于该阈值不改写。
tencentdb_heuristic.nl2hj_sameouter_hj_inner_rows_shutdown
int
500000
sameouter-HJ 保护:内层行高于该阈值不改写。
tencentdb_heuristic.joinkeys_magnify
real
1.0
danger join-key 惩罚系数:< 100为乘因子,≥ 100为附加成本。

字符串子选项

参数
类型
默认
说明
tencentdb_heuristic.danger_joinkeys
text
''
danger join keys 列表,冒号分隔,形如 public.orders(uid,gid):public.users(id)。
tencentdb_heuristic.index_options
text
''
index 规则子选项,逗号分隔(same,unique,include,restrict,funcs,udf,const,cost,startupcost,param,debug 等)。
tencentdb_heuristic.nl2hj_options
text
''
NL2HJ 子选项(samechild,sameouter_withparam,outer_not_scan,pathkey,precheck,violence,cost,debug 等)。

使用示例

danger_joinkeys:让 MergeJoin/HashJoin 退回 NestLoop

当业务判定某些连接列(例如低基数、倾斜严重的 uid)不宜走哈希 / 排序,可将其加入 danger 列表。
先准备两张演示表(orders.uid 故意做成低基数、重度倾斜):
CREATE TABLE public.users (id int PRIMARY KEY, name text);
CREATE TABLE public.orders (id int PRIMARY KEY, uid int NOT NULL, amount numeric);

INSERT INTO public.users
SELECT i, 'u' || i FROM generate_series(1, 100) i;

-- 100 万行订单,但 uid 只有 10 个不同值,且 90% 集中在 uid=1
INSERT INTO public.orders
SELECT i,
CASE WHEN random() < 0.9 THEN 1
ELSE (random() * 9)::int + 2
END,
(random() * 1000)::numeric(10,2)
FROM generate_series(1, 1000000) i;

ANALYZE public.users, public.orders;
然后打开 danger:
LOAD 'tencentdb_heuristic';
SET tencentdb_heuristic.enable = on;
SET tencentdb_heuristic.enable_danger_joinkeys = on;
SET tencentdb_heuristic.danger_joinkeys = 'public.orders(uid)';
SET tencentdb_heuristic.joinkeys_magnify = 1000; -- 强惩罚

EXPLAIN (COSTS OFF)
SELECT * FROM public.orders o JOIN public.users u ON o.uid = u.id;
QUERY PLAN
---------------------------------
Hash Join
Hash Cond: (o.uid = u.id)
-> Seq Scan on orders o
-> Hash
-> Seq Scan on users u
(5 rows)
原本的 MergeJoin/HashJoin 会因 danger 列的额外成本被打压,从而落到 NestLoop。
关闭调试打印可用 SET tencentdb_heuristic.enable_danger_joinkeys_debug = on。

nl2hj:把 NestLoop 改写为 HashJoin

外层行少、内层行多且无参数化时,NestLoop 反而更慢。
先准备两张体积悬殊的演示表(外层5行、内层100万行,等值 join 列均匀分布):
CREATE TABLE small_outer (k int PRIMARY KEY, tag text);
CREATE TABLE big_inner (k int NOT NULL, payload text);

INSERT INTO small_outer
SELECT i, 't' || i FROM generate_series(1, 5) i;

INSERT INTO big_inner
SELECT (random() * 5)::int + 1, md5(i::text)
FROM generate_series(1, 1000000) i;

CREATE INDEX ON big_inner(k); -- 有索引才能让 mainline 先选出 NL,随后被规则改写
ANALYZE small_outer, big_inner;
然后打开 nl2hj:
SET tencentdb_heuristic.enable = on;
SET tencentdb_heuristic.enable_nl2hj = on;
SET tencentdb_heuristic.nl2hj_startup = 10; -- 外层 < 10 行才保留 NL
SET tencentdb_heuristic.nl2hj_magnify = 50;
SET tencentdb_heuristic.nl2hj_options = 'cost,violence,precheck';

EXPLAIN (COSTS OFF)
SELECT * FROM small_outer o, big_inner b WHERE o.k = b.k;
QUERY PLAN
---------------------------------------
Hash Join
Hash Cond: (b.k = o.k)
-> Seq Scan on big_inner b
-> Hash
-> Seq Scan on small_outer o
(5 rows)
NL 路径的成本被放大50倍,HashJoin 胜出。
内层无索引可用的 NL 路径成本乘100,倾向切换到 HashJoin / MergeJoin。

enable_index:偏置索引路径选择

SET tencentdb_heuristic.enable = on;
SET tencentdb_heuristic.enable_index = on;
SET tencentdb_heuristic.enable_seqscan = on;
SET tencentdb_heuristic.index_options = 'same,unique,include,cost';
index_options 中的每个子选项分别控制:
same:命中相同列表达式时优先索引。
unique:命中唯一约束/唯一索引时优先。
include:INCLUDE 列可覆盖查询时优先。
cost:打开索引-顺扫成本对比调整。
debug:打印诊断信息。