USE_NL hint 不生效的三大原因:优化器估算NL成本高于HASH JOIN;未配LEADING hint指定驱动表顺序;被驱动表缺少索引导致全表扫描。必须搭配LEADING使用,并确保统计信息准确、索引合理、直方图完善。

为什么USE_NL hint 有时不生效
直接写 /*+ USE_NL(t1 t2) */ 却发现执行计划仍是 HASH JOIN,常见原因有三个:一是优化器判定 nested loops 的预估 cost 高于 hash join,而 hint 只指定连接算法,不强制驱动表顺序;二是缺少 LEADING hint 明确外层表,CBO 仍可能把大表选为驱动表;三是目标列上缺失索引,导致内层表被迫走全表扫描,优化器自动弃用 nested loops。这种情况下,USE_NL 实际被忽略,EXPLAIN PLAN 中会显示 “hint rejected” 或 plan hash value 不变。
必须配对使用的两个 hint:LEADING + USE_NL
单独 USE_NL 不保证 nested loops 被采用,真正控制执行路径的是驱动顺序。正确做法是显式指定谁驱动谁:
-
/*+ LEADING(orders) USE_NL(order_details) */—— 强制 orders 为驱动表,orders → order_details 用 nested loops - 若写成
/*+ LEADING(order_details) USE_NL(orders) */,则 order_details 成外层表,但其数据量大,很可能触发大量重复扫描,性能反而更差 - 注意表别名需与查询中一致;如果用了别名如
o和od,hint 必须写/*+ LEADING(o) USE_NL(od) */
USE_NL 的实际代价门槛在哪
CBO 是否接受 USE_NL,取决于它对 nested loops 总 cost 的重估算。这个重估依赖三类统计信息:
- 驱动表的
NUM_ROWS:越小越好,USE_NL才有优势 - 被驱动表连接列上的索引
CLUSTERING_FACTOR:越接近NUM_ROWS(即数据离散),索引效率越高;若接近NUM_BLOCKS(即聚簇好),nested loops 随机 I/O 少,CBO 更愿意采纳 - 连接列的
HISTOGRAM:没有直方图时,CBO 默认按均匀分布估算基数,容易低估选择性,导致误判 nested loops 成本偏高;对倾斜数据,收集FOR COLUMNS SIZE AUTO直方图可显著提升 hint 接受率
验证 hint 是否真正起效的三个检查点
不能只看执行计划里有没有 NESTED LOOPS 这个 Operation,还要确认它是否按预期工作:
- 查
PLAN_TABLE中该步骤的OBJECT_NAME和ACCESS_PREDICATES:确保内层表走的是INDEX RANGE SCAN或UNIQUE SCAN,而非FULL TABLE SCAN - 对比
ROWS列的预估值和实际执行的A-Rows(用DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST')获取):若预估 10 行、实际返回 10 万行,说明统计信息严重失真,hint 可能只是“带病上岗” - 观察
STARTS值:对 nested loops,它应等于驱动表输出行数;若STARTS = 1但ROWS > 1,说明内层表被物化或缓存,实际不是真正的 nested loops 行为
嵌套循环的优化本质不是“让 CBO 用 nested loops”,而是“让 CBO 认为 nested loops 是唯一合理的选择”。hint 是扳手,但扳手拧不动锈死的螺栓——统计信息不准、索引缺失、数据倾斜,这些才是卡住执行计划的真实瓶颈。











