何时(以及何时不)使用提示
依靠 PostgreSQL 基于成本的规划器
表大小、列数据分布等的统计信息。
规划器成本设置如
random_page_cost和cpu_tuple_cost服务器配置参数,例如
enable_seqscan、work_mem和effective_cache_size
Postgres 规划器问题的根本原因
CREATE STATISTICS,可确保系统捕获有关数据分布的当前信息。work_mem、random_page_cost和等设置effective_cache_size对规划器做出的决策有重大影响,但它们通常设置为默认值,这可能会导致糟糕的查询计划。优化这些设置可以解决许多查询性能挑战,而无需引入提示。当规划器的成本模型与硬件和数据的实际情况很好地匹配时,它通常会得出更好的计划。当提示有帮助时
将 Oracle 提示映射到 pg_hint_plan
强制使用特定索引或连接方法(例如嵌套循环)
启用或禁用并行执行
覆盖其他计划选择
这些提示可能非常直接:“在此表上使用索引 X”或“使用嵌套循环连接来连接表 A 和 B”。当数据库优化器无法自行选择最佳计划或您需要跨不同实例保持一致的性能时,这种级别的控制有时至关重要。
当您决定在 Postgres 中复制 Oracle 提示时,您可能会寻找直接等效项。pg_hint_plan 支持许多(但不是全部)类似 Oracle 的提示。pg_hint_plan 主要控制扫描方法、连接方法、连接顺序和查询并行性。Oracle 的许多用于重写查询、星型转换、动态采样和专用缓存的高级提示在 Postgres 中根本无法使用或不适用。
相反,在 Postgres 中,您通常通过调整规划器 GUC(如enable_hashjoin、enable_nestloop)、重写查询、使用 CTE 关键字实现查询的部分内容MATERIALIZED或使用推动 Postgres 规划器的索引/约束来实现类似的行为。
让我们回顾一些常见的情况,并将它们从 Oracle 数据库提示映射到 pg_hint_plan 语法或其他 Postgres 替代方案。
访问路径(或索引)提示
| 预言提示 | pg_hint_plan 等效项 | 笔记 |
|---|---|---|
FULL(table)强制全表扫描 | SeqScan(table) | 强制 Postgres 对命名表使用顺序扫描(在 Oracle 上称为全表扫描)。 |
INDEX(table [index])强制索引扫描 | IndexScan(table [index]) 或者 IndexOnlyScan(table [index]) 或者 BitmapScan(table [index]) | pg_hint_plan 对常规索引扫描、仅索引扫描或位图索引扫描有单独的提示。 |
INDEX_FFS(table index)快速全索引扫描 | 没有直接等价物。IndexOnlyScan是近似的。 | 如果所有筛选和返回的列都已编入索引,Postgres 可以使用 IndexOnlyScan 从索引中回答查询。但是,Postgres 有时仍会检查表以验证已删除行的可见性(此功能无法关闭)。 |
INDEX_DESC(table [index])反向索引扫描 | IndexScanORDER BY ... DESC在查询本身中有一个。 | pg_hint_plan 不能直接强制执行降序索引扫描;您通常依赖查询顺序或具有正确排序顺序的索引。 |
NO_INDEX(table [index])禁止索引 | 无同等物。 | 没有相当于禁止单个索引的操作。 |
INDEX_JOIN(table)使用索引连接 | 无同等物。 | PostgreSQL 没有像 Oracle 那样的直接的“索引连接”概念。 |
SELECT /*+ INDEX(table1 idx_table1_col) */
col1, col2
FROM table1
WHERE col1 = 'something'
ORDER BY col2 LIMIT 1;在带有 pg_hint_plan 的 PostgreSQL 中,您可以将其转换为:
/*+
IndexScan(table1 idx_table1_col)
*/
SELECT col1, col2
FROM table1
WHERE col1 = 'something'
ORDER BY col2 LIMIT 1;连接操作提示
| 预言提示 | pg_hint_plan 等效项 | 笔记 |
|---|---|---|
USE_NL(table1 table2)使用嵌套循环 | NestLoop(table1 table2) | 强制两个命名表之间进行嵌套循环连接。 |
USE_HASH(table1 table2)使用哈希连接 | HashJoin(table1 table2) | 强制两个命名表之间进行哈希连接。 |
USE_MERGE(table1 table2)使用排序合并连接 | MergeJoin(table1 table2) | 强制在两个命名表之间进行合并连接。 |
USE_NL_WITH_INDEX(t1 idx1) | NestLoop(table1 table2)+ IndexScan(table1 index1)+Leading((table2 table1)) | 为了执行 Postgres 所称的参数化索引扫描,提示必须强制 NestedLoop、连接顺序(通过 Leading)和使用正确的索引。请注意,Leading 提示需要使用额外的括号来强制排序。列出的第一个表是外表,然后是内表(即索引扫描所在的表)。 |
NO_USE_NL(t1 [t2...])NO_USE_MERGE(t1 [t2...])NO_USE_HASH(t1 [t2...]) | NoNestLoop(t1 t2 [t3...])NoMergeJoin(t1 t2 [t3...])NoHashJoin(t1 t2 [t3...]) | pg_hint_plans 指示 PostgreSQL 的查询规划器不要对列出的表(需要包含内表和外表)使用嵌套循环/合并/哈希连接,而 Oracle 提示告诉优化器不要对每个指定的表(其中它是连接的内部表)使用嵌套循环/合并/哈希连接。 |
加入顺序提示
| 预言提示 | pg_hint_plan 等效项 | 笔记 |
|---|---|---|
ORDERED按 FROM 子句中的表顺序进行连接 | Set(join_collapse_limit 1) | 在 Postgres 中,将设置join_collapse_limit设为“1”将强制 Postgres 按照查询中列出的顺序连接表。您可以SET在运行查询之前通过 pg_hint_plan 或常规命令设置此项。请参阅Postgres 文档中的示例。 |
LEADING(t1 t2 ... tN) | Leading(t1 t2 ... tN)Leading(((t1 t2) t3)) | pg_hint_plan 支持Leading(...)修复连接顺序。您可以按照所需的连接顺序列出多个表。使用语法并在每对周围添加附加括号来指定哪个表用作内表或外表。 |
并行/并行度提示
| 预言提示 | pg_hint_plan 等效项 | 笔记 |
|---|---|---|
PARALLEL(table, n)平行度 n | Parallel(table n hard) | pg_hint_plan 默认(“软”)仅设置配置的最大工作线程数(max_parallel_workers_per_gather),但如果成本不利于它,则不会强制执行并行计划。您可以通过将第三个参数指定为 来强制执行并行计划hard,这与 Oracle 在指定特定并行度时的行为一致。 |
NO_PARALLEL(table)禁止并行 | Parallel(table 0) | 当表值设置为零时,pg_hint_plan 会抑制并行执行。 |
/*+
Parallel(sales 4)
*/
SELECT ...查询转换和子查询提示
Oracle 有许多控制查询转换的提示(例如取消嵌套子查询、合并视图、星型转换等)。pg_hint_plan 没有提供这些转换的直接等效项;PostgreSQL 的规划器转换通常不是基于提示的,而是由自动控制或由 GUC 参数控制。
| 预言提示 | pg_hint_plan 等效项 | 笔记 |
|---|---|---|
UNNEST / NO_UNNEST | 没有任何 | PostgreSQL 会自动决定子查询是否嵌套(横向连接、子查询展平等),而 pg_hint_plan 无法影响这一点。但是,可以使用带有关键字的 CTE 重写查询NOT MATERIALIZED,其行为类似于 Oracle 的UNNEST,或者MATERIALIZED其行为类似于NO_UNNEST。请参阅 Postgres 文档。 |
MERGE/NO_MERGE | 没有任何 | 在 Postgres 中,视图会自动内联,就像它们是子查询一样;没有细粒度的提示来控制这一点。 |
PUSH_SUBQ/NO_PUSH_SUBQ | 没有任何 | 无法直接控制子查询的执行pg_hint_plan。 |
STAR_TRANSFORMATION/NO_STAR_TRANSFORMATION | 没有任何 | Oracle 针对数据仓库模式的星型转换在 Postgres 中没有直接对应项。 |
FACT/NO_FACT | 没有任何 | Oracle 将这些用于星型模式;不适用于 Postgres。 |
结果缓存和其他专门提示
| 预言提示 | pg_hint_plan 等效项 | 笔记 |
|---|---|---|
RESULT_CACHE/NO_RESULT_CACHE | 没有任何 | PostgreSQL 没有像 Oracle 那样的内置查询结果缓存。 |
OPT_PARAM(...) | Set(...) | Postgres 参数通常在会话级别设置(“SET”命令)或通过 pg_hint_plan 中的“Set”提示设置。请注意,Oracle 和 Postgres 之间可以设置的参数有所不同。 |
DYNAMIC_SAMPLING(...) | 没有任何 | Postgres 统计系统基于查询执行之外的表的单独分析来工作,并且不具有动态采样的等效性。 |
QB_NAME | 没有任何 | pg_hint_plan 不提供与 Oracle 的查询块功能等效的提示。 |
PUSH_PRED/NO_PUSH_PRED | 没有任何 | Postgres 根据子查询的启发式方法自动处理谓词下推;没有直接提示。 |
USE_CONCAT | 没有任何 | Oracle 使用它来强制将OR子句扩展为UNION ALL查询。Postgres 不支持自动执行此转换,需要手动重写查询。 |
NO_QUERY_TRANSFORMATION | 没有任何 | Postgres 在规划过程中的转换不能通过提示关闭或修改。 |
额外的 pg_hint_plan 功能(没有 Oracle 等效功能)
Rows(table1 table2 [ n ])table1 and table 2:告诉规划器假设返回行之间存在连接n(替换或调整统计得出的估计值),影响连接顺序和计划选择。Memoize(table1 table2)/ NoMemoize(table1 table2):影响是否将 Memoize 功能应用于给定的连接表。Memoize 有时会导致 Postgres 规划器成本下降,因此“NoMemoize”提示可用于避免可能偏向嵌套循环连接的查询计划。
调试 pg_hint_plan 提示的最佳实践
指定多个提示注释(如果有多个提示,则必须在一个/*+ ... */注释中全部指定它们)
使用不正确的 pg_hint_plan 语法(例如NestedLoop代替NestLoop)
规划器没有可行的路径来使用提示(例如,因为请求的索引不能用于给定的表达式)
查询中重复使用的表名没有唯一的别名(在这种情况下,您需要为每个表分配一个别名)
分区表的提示必须针对分区表父级,而不是子级
没有指定名称的子查询(即不是 CTE)只能在某些情况下提示
pg_hint_plan.print_debug标志。这将为您提供如下输出:SET pg_hint_plan.debug_print = true;
/*+ NestedLoop(table1 table2) */ EXPLAIN SELECT * FROM …;INFO: pg_hint_plan: hint syntax error at or near "NestedLoop".
DETAIL: Unrecognized hint keyword "NestedLoop".
QUERY PLAN
----------------------------------------------------------------------------------------------------
…此外,您可以通过将客户端日志级别(client_min_messages)提升至来显示有关提示使用情况的更详细输出LOG,这将告诉您哪些提示被成功使用:
SET client_min_messages = LOG;
/*+ NestLoop(table1 table2) IndexScan(table3) */ EXPLAIN SELECT * FROM table1 JOIN table2
ON (table2_id = table2.id) WHERE table1_id = '123';LOG: pg_hint_plan:
used hint:
NestLoop(table1 table2)
not used hint:
IndexScan(table3)
duplication hint:
error hint:
QUERY PLAN
----------------------------------------------------------------------------------------------
...您可以在pg_hint_plan 文档中找到需要考虑的其他方面。
使用 pganalyze 测试查询提示

结论
#PG证书#PG考试#PostgresQL培训#PostgresQL考试#PostgresQL认证




