热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

你还在通过临时收集统计信息的方法处理统计信息不准的问题吗?

统计信息中最



      我们都知道统计信息是CBO能从众多可能的执行路径中选出最优执行计划的最重要的依据来源,因此关于统计信息的重要性不言而喻,对此oracle也有自己的自动job,按照一定的规则去维护并更新统计信息,但是在日常的工作中我们经常能遇到由于统计信息不准确而导致的一系列性能问题,这里统计信息不准确的原因有很多,比如自动收集统计信息的job在窗口期没跑完,自动收集统计信息的job压根就跑失败了,特殊的业务逻辑(delete   truncate),谓词越界等,以及统计信息的滞后性等。


       如果我们在生产环境中遇到统计信息不准的情况该怎么做呢?并且此时业务的运行的速度已经受到统计信息的影响了,是直接在跑着业务的同时重新收集相关表甚至全库的统计信息吗?当然不是,很多时候也不能!那我们该怎么做呢?这里比如是日期字段发生了越界,那么我们可以直接修改日期字段的统计信息中的最大值、最小值,来避免日期越界带来的负面影响。




       接下来笔者就通过一个模拟的案例来实际操作。


  • 基础环境及测试数据准备


conn test/test


drop table TB0821;


create table TB0821 ( a number, b varchar2(128), c date );


insert into TB0821 select object_id, object_name, created from user_objects where rownum <1000;


commit;


      由于最大值最小值存储的类型为raw,因此为了更方便的显示,使用dbms_stats.convert_raw_value将raw类型进行转换,创建如下的函数:


create or replace function display_raw(rawval raw, type varchar2)

  return varchar2 is

  cn  number;

  cv  varchar2(32);

  cd  date;

  cnv nvarchar2(32);

  cr  rowid;

  cc  char(32);

begin

  if (type = 'NUMBER') then

    dbms_stats.convert_raw_value(rawval, cn);

    return to_char(cn);

  elsif (type = 'VARCHAR2') then

    dbms_stats.convert_raw_value(rawval, cv);

    return to_char(cv);

  elsif (type = 'DATE') then

    dbms_stats.convert_raw_value(rawval, cd);

    return to_char(cd);

  elsif (type = 'NVARCHAR2') then

    dbms_stats.convert_raw_value(rawval, cnv);

    return to_char(cnv);

  elsif (type = 'ROWID') then

    dbms_stats.convert_raw_value(rawval, cr);

    return to_char(cnv);

  elsif (type = 'CHAR') then

    dbms_stats.convert_raw_value(rawval, cc);

    return to_char(cc);

  else

    return 'UNKNOWN DATATYPE';

  end if;

end;

/


      接下来,在修改列最大值最小值之前,我们先用上面创建的函数去查一下表中目前日期列的最大值和最小值,如下:


col column_name for a30

col low_val for a30

col high_val for a30

col data_type for a30

select

   a.column_name,

   display_raw(a.low_value,b.data_type) as low_val,

   display_raw(a.high_value,b.data_type) as high_val,

   b.data_type

from

   user_tab_col_statistics a, user_tab_cols b

where

   a.table_name='TB0821' and

   a.table_name=b.table_name and

   a.column_name=b.column_name

/



      可以看到统计信息中日期的最小值和最大值分别为20200103和20200821,接下来我们将日期的最小值修改为20200101,最大值修改为20201231


DECLARE

   srec                  DBMS_STATS.STATREC;

   v_distcnt          NUMBER;

   v_density         NUMBER;

   v_nullcnt          NUMBER;

   v_avgclen         NUMBER;

   numvals           DBMS_STATS.NUMARRAY;

   charvals           DBMS_STATS.CHARARRAY;

   datevals           DBMS_STATS.DATEARRAY;

BEGIN                            

   DBMS_STATS.get_column_stats (ownname      => user,

                                tabname      => 'TB0821',

                                colname      => 'C',

                                distcnt      => v_distcnt,

                                density      => v_density,

                                nullcnt      => v_nullcnt,

                                srec         => srec,

                                avgclen      => v_avgclen

                               );   

   datevals := DBMS_STATS.datearray ('20200101', '20201231'); 

   DBMS_STATS.prepare_column_values (srec, datevals);

   DBMS_STATS.set_column_stats (ownname      => user,

                                tabname      => 'TB0821',

                                colname      => 'C',

                                distcnt      => v_distcnt,

                                density      => v_density,

                                nullcnt      => v_nullcnt,

                                srec         => srec,

                                avgclen      => v_avgclen

                               );

   COMMIT;

END;

/


       修改完之后,再次查看日期字段的最大值最小值,如下:



       可以看到日期字段的最大最小值,被成功的修改为20200101和20201231,虽然这个修改看上去很简单,但他的用处还是不小的,比如生产环境中由于统计信息的滞后性导致日期字段谓词越界,这时候如果临时收集相关表的统计信息,那么这些表所涉及到的所有sql都需要硬解析,如果在业务高峰期,这是一个很危险的动作,并且很多因素是不可控的,所以这时候我们只需要将相关表的统计信息中涉及到某些列的最小值最大值做一个修改就可以,以最小的改动量来解决越界的问题。




      以上只演示了修改统计信息中日期字段的最大值,最小值,同样的方法我们也可以修改number型以及字符型的最大值最小值,语句如下:


DECLARE

   srec                  DBMS_STATS.STATREC;

   v_distcnt          NUMBER;

   v_density         NUMBER;

   v_nullcnt          NUMBER;

   v_avgclen         NUMBER;

   numvals           DBMS_STATS.NUMARRAY;

   charvals           DBMS_STATS.CHARARRAY;

   datevals           DBMS_STATS.DATEARRAY;

BEGIN


   -- 数值类型

  

   DBMS_STATS.get_column_stats (ownname      => user,

                                tabname      => 'TB0821',

                                colname      => 'A',

                                distcnt      => v_distcnt,

                                density      => v_density,

                                nullcnt      => v_nullcnt,

                                srec         => srec,

                                avgclen      => v_avgclen

                               );

    

   numvals := DBMS_STATS.numarray (1, 100000);

  

   DBMS_STATS.prepare_column_values (srec, numvals);


   DBMS_STATS.set_column_stats (ownname      => user,

                                tabname      => 'TB0821',

                                colname      => 'A',

                                distcnt      => v_distcnt,

                                density      => v_density,

                                nullcnt      => v_nullcnt,

                                srec         => srec,

                                avgclen      => v_avgclen

                               );

              

   -- 字符类型

                              

   DBMS_STATS.get_column_stats (ownname      => user,

                                tabname      => 'TB0821',

                                colname      => 'B',

                                distcnt      => v_distcnt,

                                density      => v_density,

                                nullcnt      => v_nullcnt,

                                srec         => srec,

                                avgclen      => v_avgclen

                               );

    

   charvals := DBMS_STATS.chararray ('A_MIN', 'Z_MAX');

  

   DBMS_STATS.prepare_column_values (srec, charvals);


   DBMS_STATS.set_column_stats (ownname      => user,

                                tabname      => 'TB0821',

                                colname      => 'B',

                                distcnt      => v_distcnt,

                                density      => v_density,

                                nullcnt      => v_nullcnt,

                                srec         => srec,

                                avgclen      => v_avgclen

                               );

   commit;

end;

/


再来验证一下:


col column_name for a30

col low_val for a30

col high_val for a30

col data_type for a30

select

   a.column_name,

   display_raw(a.low_value,b.data_type) as low_val,

   display_raw(a.high_value,b.data_type) as high_val,

   b.data_type

from

   user_tab_col_statistics a, user_tab_cols b

where

   a.table_name='TB0821' and

   a.table_name=b.table_name and

   a.column_name=b.column_name

/





       总结:上面所说的修改统计信息中最大值最小值的方法,也是有适用场景的,一般情况下就是针对那些单向增长的列,多出现为范围查询的谓词条件,例如小于部分发生了越界,或者等值查询中等于了一个越界的值,这些情况都可以使用此方法。


       最近出差在外,没多少连续的时间写,这次内容虽然很简单,不多文中的几个脚本还是值得大家收藏的。





推荐阅读
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • 本文介绍了Oracle存储过程的基本语法和写法示例,同时还介绍了已命名的系统异常的产生原因。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 总结一下C中string的操作,来自〈CPrimer〉第四版。1.string对象的定义和初始化:strings1;空串strings2(s1);将s2初始 ... [详细]
  • 本文介绍了使用C++Builder实现获取USB优盘序列号的方法,包括相关的代码和说明。通过该方法,可以获取指定盘符的USB优盘序列号,并将其存放在缓冲中。该方法可以在Windows系统中有效地获取USB优盘序列号,并且适用于C++Builder开发环境。 ... [详细]
  • 本文整理了Java中org.apache.solr.common.SolrDocument.setField()方法的一些代码示例,展示了SolrDocum ... [详细]
  • Ihavebeenworkingwithbufferingafileonmylocaldrivetoparseandobtaincertaindata.Forte ... [详细]
  • WPF之Binding初探
      初学wpf,经常被Binding搞晕,以下记录写Binding的基础。首先,盗用张图。这图形象的说明了Binding的机理。对于Binding,意思是数据绑定,基本用法是:1、 ... [详细]
author-avatar
广东工业大学普通话_333
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有