You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hash Match Join执行卡顿无响应,Nested Loops却秒完成的SQL问题

问题分析与解决方案

核心现象

  • 原查询默认使用Hash Match连接时耗时极长(数小时超时),实时执行计划卡在Hash Match步骤
  • 强制指定inner loop join(嵌套循环)后,查询1秒内完成
  • 移除ISSUER_ID_SIX或ISSUER_ID_BBG任一连接条件,查询也能正常运行
  • 已创建包含连接、筛选属性的覆盖索引,但无效果
  • 表规模:T_MASTER_ISSUER_KEYS约10万行,T_ISSUER_SECURITY_LIST约30万行

可能原因及排查方向

1. 统计信息过期或不准确

SQL优化器选择Hash Join的核心依据是表的统计数据,如果统计信息过时,优化器会错误估算连接后的行数,导致Hash Join分配的内存不足,触发磁盘溢出(Hash Spill to TempDB)——这是Hash Join突然变慢的最常见原因。

  • 执行以下语句更新统计信息:
    UPDATE STATISTICS T_MASTER_ISSUER_KEYS WITH FULLSCAN;
    UPDATE STATISTICS T_ISSUER_SECURITY_LIST WITH FULLSCAN;
    
  • 更新后重新运行原查询,观察执行计划是否变化。

2. VARCHAR列的字符集/排序规则不匹配

连接列虽都是VARCHAR(20),但如果两表的字符集或排序规则不一致,会导致优化器无法有效利用索引,甚至在连接时产生大量隐式转换,大幅增加Hash Join的计算开销。

  • 检查两表连接列的排序规则:
    SELECT COLUMN_NAME, COLLATION_NAME 
    FROM INFORMATION_SCHEMA.COLUMNS 
    WHERE TABLE_NAME IN ('T_MASTER_ISSUER_KEYS', 'T_ISSUER_SECURITY_LIST')
      AND COLUMN_NAME IN ('ISSUER_ID_SIX', 'ISSUER_ID_BBG');
    
  • 如果排序规则不一致,可修改其中一列的排序规则匹配另一列,或在连接时显式指定排序规则(如lst.ISSUER_ID_SIX COLLATE SQL_Latin1_General_CP1_CI_AS = ky.ISSUER_ID_SIX),修改表结构是更彻底的方案。

3. Hash Join的内存配置不足

即使表规模不大,如果服务器最大内存限制过低,或查询的内存授予不足,Hash Join会被迫将中间结果写入TempDB,磁盘IO远慢于内存操作,直接导致查询卡顿。

  • 查看执行计划中Hash Match运算符的Actual Memory Grant和Requested Memory Grant,如果实际内存远小于请求值,说明存在内存压力。
  • 可临时调整查询的内存授予(需谨慎操作),或检查服务器内存配置,确保有足够内存分配给查询。

4. 复合连接条件的索引顺序不合理

虽然创建了覆盖索引,但复合连接条件的索引顺序可能不符合优化器的需求。针对两列连接的场景,建议创建复合索引而非单独的单列索引:

  • 针对T_MASTER_ISSUER_KEYS:
    CREATE NONCLUSTERED INDEX IX_T_MASTER_ISSUER_KEYS_RDE_SIX_BBG 
    ON T_MASTER_ISSUER_KEYS (ISSUER_ID_RDE, ISSUER_ID_SIX, ISSUER_ID_BBG)
    INCLUDE (issuer_id);
    
  • 针对T_ISSUER_SECURITY_LIST:
    CREATE NONCLUSTERED INDEX IX_T_ISSUER_SECURITY_LIST_SIX_BBG 
    ON T_ISSUER_SECURITY_LIST (ISSUER_ID_SIX, ISSUER_ID_BBG);
    
    (注:索引列顺序可根据数据区分度调整,区分度高的列放在前面)

临时解决方案

如果以上排查需要时间,可暂时在查询中强制使用嵌套循环连接,保证业务正常运行:

select ky.issuer_id from T_MASTER_ISSUER_KEYS ky 
inner loop join T_ISSUER_SECURITY_LIST lst on lst.ISSUER_ID_SIX = ky.ISSUER_ID_SIX AND lst.ISSUER_ID_BBG = ky.ISSUER_ID_BBG
WHERE ky.ISSUER_ID_RDE is not null

内容的提问来源于stack exchange,提问作者Jan Drozen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 00:40:20