You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Oracle 19c大表固定查询性能优化咨询(无法修改程序及表结构)

Oracle 19c大表固定查询性能优化咨询(无法修改程序及表结构)

嗨,针对你遇到的4亿行大表的固定查询性能波动问题,我来梳理几个不用修改程序和表结构就能操作的数据库层面优化方向,都是Oracle 19c里实用的技巧:

  • 确认函数索引是否被正确选用
    你已经创建了UPPER(COL3)的函数索引PIPTABLE1_2,这本来是最匹配你查询条件的索引,但有时候执行计划可能因为统计信息过时或者执行计划摇摆没选它。

    • 先检查当前查询的执行计划,确认是否用到了这个索引:
      EXPLAIN PLAN FOR SELECT COL1, COL2 FROM TABLE1 WHERE upper(COL3) = upper('abc');
      SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
      
    • 如果没用到,大概率是统计信息不准确,建议重新收集表和关联索引的统计信息:
      EXEC DBMS_STATS.GATHER_TABLE_STATS(
          OWNNAME => 'DATABASE1',
          TABNAME => 'PTABLE1',
          ESTIMATE_PERCENT => DBMS_STATS.AUTO_SAMPLE_SIZE,
          CASCADE => TRUE
      );
      
      19c的自动采样逻辑已经很智能,CASCADE => TRUE会同步收集索引的统计信息,让优化器能做出更准确的判断。
  • 固定执行计划避免性能波动
    你提到查询有时候快有时候慢(周六快,工作日慢),这种情况很可能是执行计划摇摆导致的。可以用SQL Plan Baseline把最优的执行计划固定下来:

    • 先找到这个查询对应的SQL_ID:
      SELECT SQL_ID, SQL_FULLTEXT 
      FROM V$SQL 
      WHERE SQL_FULLTEXT LIKE '%SELECT COL1, COL2 FROM TABLE1 WHERE upper(COL3) = upper(''abc'')%';
      
    • 加载当前最优的执行计划到基线:
      DECLARE
          l_plans_loaded PLS_INTEGER;
      BEGIN
          l_plans_loaded := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(SQL_ID => '你的SQL_ID');
      END;
      /
      
    • 最后确认基线是否生效:
      SELECT SQL_HANDLE, PLAN_NAME, ENABLED, ACCEPTED 
      FROM DBA_SQL_PLAN_BASELINES 
      WHERE SQL_TEXT LIKE '%SELECT COL1, COL2 FROM TABLE1 WHERE upper(COL3) = upper(''abc'')%';
      
      这样Oracle就会强制使用你加载的这个最优计划,不会再出现执行计划突变导致的慢查询。
  • 维护索引物理状态减少碎片
    大表的索引用久了会产生碎片,导致索引读取效率下降。可以先检查索引的碎片和聚集因子情况:

    SELECT INDEX_NAME, LEAF_BLOCKS, AVG_LEAF_BLOCKS_PER_KEY, CLUSTERING_FACTOR
    FROM DBA_INDEXES 
    WHERE OWNER='DATABASE1' AND INDEX_NAME IN ('PIPTABLE1_2', 'PIPTABLE1_3');
    

    如果CLUSTERING_FACTOR数值接近表的总行数,说明索引叶子块和表数据块的顺序差异很大,回表时会频繁读磁盘。这种情况下可以重建函数索引(用ONLINE模式不影响业务):

    ALTER INDEX "DATABASE1"."PIPTABLE1_2" REBUILD ONLINE;
    
  • 启用结果缓存复用查询结果
    你的查询是固定的,结果只有5行,非常适合用Oracle的结果缓存特性,直接把查询结果存在内存里,下次执行直接取缓存:

    • 如果数据库的result_cache_modeMANUAL,你可以通过SQL Profile给这个查询添加结果缓存提示(不用修改程序)。先找到SQL_ID后,执行:
      DECLARE
          l_sql_profile VARCHAR2(100);
      BEGIN
          l_sql_profile := DBMS_SQLTUNE.CREATE_SQL_PROFILE(
              SQL_ID => '你的SQL_ID',
              NAME => 'PROFILE_PTABLE1_QUERY',
              DESCRIPTION => 'Add result cache hint for fixed query',
              CATEGORY => 'DEFAULT',
              HINTS => '/*+ RESULT_CACHE */'
          );
      END;
      /
      

    这样后续执行这个查询时,会优先从结果缓存取数据,性能会有质的提升(前提是COL3对应的数据不会频繁更新)。

  • 优化缓存策略减少磁盘IO
    如果慢查询是因为数据不在Buffer Cache里,需要从磁盘读取,可以尝试:

    • 检查Buffer Cache命中率:
      SELECT 1 - (PHYSICAL_READS / (DB_BLOCK_GETS + CONSISTENT_GETS)) AS BUFFER_CACHE_HIT_RATIO
      FROM V$BUFFER_POOL_STATISTICS 
      WHERE NAME = 'DEFAULT';
      
      如果命中率低于90%,可以联系DBA考虑调整SGA大小;或者把这个表放到KEEP缓存池,让常用数据常驻内存:
      ALTER TABLE "DATABASE1"."PTABLE1" STORAGE (BUFFER_POOL KEEP);
      

备注:内容来源于stack exchange,提问作者canonier

火山引擎 最新活动