【Oracle篇】统计信息和动态采样的深度剖析(第一篇,总共六篇)

💫《博主介绍》:****✨又是一天没白过,我是奈斯,DBA一名✨

💫《擅长领域》:****✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux,也在扩展大数据方向的知识面✌️

💖💖💖大佬们都喜欢静静的看文章,并且也会默默的点赞收藏加关注💖💖💖

兄弟们,沉寂了一段时间后,今天我回来了!!!最近博主在赶项目进度,有几天午饭也没有顾得上吃,不过好在项目也算是顺利上线,终于可以休息一下了,养精蓄锐恢复一下身体。我也想借此机会,与大家分享一个感悟:在快节奏的生活中,大家都在追求着事业的成功与梦想的实现,但请别忘了,在追求的同时,也要学会适时地停下脚步,去品味生活的美好。 希望大家能在工作与生活的天平上,找到那个完美的平衡点**(毕竟身体健康才是革命的本钱😁)** ,既能在职场上挥洒汗水,高效完成每一项任务,也能在闲暇之余,静心享受一杯咖啡的香醇,一本好书的静谧,或是与家人朋友共度的温馨时光 。

如标题所说,今天给大家介绍一下和SQL优化相关的系列------统计信息、SQL执行计划、优化器。众所周知,Oracle优化器在SQL执行过程中扮演着至关重要的角色,它依赖于统计信息来为每个SQL语句制定最优的执行计划。而这些统计信息对于优化器的决策具有决定性的影响。因此,了解和掌握统计信息、执行计划、优化器对于数据库的性能调优至关重要。接下来,我们将探讨统计信息、执行计划、优化器的相关知识,帮助大家更好地理解并优化自己的数据库性能。

还是老规矩为了让大家更容易消化和逐个理解,我将分成六篇文章来进行介绍,以便大家劳逸结合而不至于感觉到阅读枯燥,六篇的内容分别如下:

  • 第一篇:统计信息和动态采样的深度剖析**(当前篇)**
  • 第二篇:搞懂优化器和SQL语句的解析步骤(含执行计划分析)
  • 第三篇:SQL执行计划之访问路径
  • 第四篇:SQL执行计划之多表连接
  • 第五篇:了解SQL Tuning Advisor工具的使用并优化SQL语句
  • 第六篇:SQL性能优化实战(从15秒优化到0.08秒)

目录

一、统计信息

1、Oracle收集统计信息的原理:

2、Oracle统计信息的收集分两种:自动收集和手动收集

[2.1 自动收集:](#2.1 自动收集:)

[2.1.1 Oracle 10g时的自动收集:自动收集维护任务,周一至周五晚上22:00-6:00,周末全天。](#2.1.1 Oracle 10g时的自动收集:自动收集维护任务,周一至周五晚上22:00-6:00,周末全天。)

[2.1.2 11g/12c时的自动收集:自动收集维护任务,周一至周五晚上22:00开始持续4个小时,周末早上6:00开始持续20个小时](#2.1.2 11g/12c时的自动收集:自动收集维护任务,周一至周五晚上22:00开始持续4个小时,周末早上6:00开始持续20个小时)

案例1:11g/12c修改自动收集统计信息的时间,避开高峰期。周一至周五凌晨2开始持续4个小时,周六凌晨2点开始持续20个小时;周七凌晨2点开始持续10个小时

[2.2 手动收集(为什么要手动收集统计信息):](#2.2 手动收集(为什么要手动收集统计信息):)

3、统计信息收集的级别:

4、统计信息收集的内容:

5、动态采样和统计信息的区别:

6、收集统计信息的方法和工具:

[6.1 analyze收集方式(8i、9i,目前兼容)](#6.1 analyze收集方式(8i、9i,目前兼容))

[6.2 dbms_stats包收集方式(10g之后,主推的方式)](#6.2 dbms_stats包收集方式(10g之后,主推的方式))

[6.2.1 dbms_stats包之GATHER_*](#6.2.1 dbms_stats包之GATHER_*)

案例1:dbms_stats.gather_database_stats收集数据库统计信息。多用于升级或迁移后

案例2:dbms_stats.gather_schema_stats收集用户统计信息。

案例3:dbms_stats.gather_table_stats收集表统计信息。对新上线的表手动收集统计信息。Oracle统计信息会晚上自动统计,为了给出真实的执行计划手动收集统计信息,便于下午分析

[6.2.2 dbms_stats包之DELETE_*](#6.2.2 dbms_stats包之DELETE_*)

案例1:删除数据库统计信息(只做了解,生产环境不要操作)

案例2:删除用户统计信息(只做了解,生产环境不要操作)

案例3:删除表统计信息(只做了解,生产环境不要操作)

案例4:删除索引统计信息(只做了解,生产环境不要操作)

[6.2.3 dbms_stats包之LOCK_*/UNLOCK_*](#6.2.3 dbms_stats包之LOCK_/UNLOCK_)

案例1:锁定并解锁表的统计信息

[6.2.4 dbms_stats包之包的重建](#6.2.4 dbms_stats包之包的重建)

案例一:重建dbms_stats包。如果dbms_stats包误删或者出问题,那么通过执行oracle内部4个脚本重建即可

7、数据库默认收集的统计信息:

8、统计信息的保留时间以及发布验证功能:

案例一:查看旧的统计信息默认保留天数,并修改(对于大数据的库频繁收集统计可能导致sysaux表空间增速过快,适当减少旧的统计信息的保留时间。31days---7days):

二、动态采样(当对象还没有统计信息时,那么先通过动态采样技术来选择执行计划,动态采样用于弥补统计信息缺失时使用)

案例1:删除表的统计信息,默认使用动态采样2级别通过优化器给出执行计划

案例2:删除表的统计信息,也不使用动态采样


好了,言归正传,让我们开始今天的内容,show time................

一、统计信息

统计信息(statistic)主要是描述数据库中表、索引的大小、规模、数据分布状况等的一类信息。例如:表的行数,块数,平均每行的大小,索引的leaf blocks,索引字段的行数,不同值的大小等,都属于统计信息。CBO正是根据这些统计信息数据,计算出不同访问路径下,不同join方式下,各种计划的成本,最后选择出成本最小的计划。optimizer 优化器根据统计信息 对每个sql 语句执行 的执行计划 (执行计划受统计信息影响)。

统计信息是存放在数据字典表中的,如tab$,一般可通过察看某些视图来获取统计信息状况,如DBA_TABLES、DBA_INDEXES、DBA_TAB_COL_STATISTICS、DBA_TAB_HISTOGRAMS等。在这些视图中包含表示统计信息的一些字段,这些字段只有搜集过统计信息之后才有值,否则是空的。例如:last_analyzed 字段表示上次统计信息搜集的时间,可以根据这个字段,快速的了解最近一次统计信息搜集的时间。

1、Oracle收集统计信息的原理:

统计信息对于Oracle数据库来说至关重要,尤其是在使用CBO(基于成本的优化器)模式的时候,统计信息包括表的使用块数、空闲块数、平均行长度、统计信息收集时间等。在Oracle 9i数据库中,两种优化器模式RBO和CBO并存,在默认情况下optimizer_mode参数的值是choose,choose不是优化器模式,它表示在分析数据库中的语句时,如果在对象上有统计信息,就是用CBO方式生成执行计划,如果对象上没有统计信息,则使用RBO模式。

从总体上来说,CBO的准确度高于RBO,但是它要求要有统计信息,并且统计信息必须准确,否则Oracle可能会做出错误的判断。所以在Oracle 9i数据库中,我们会自己来规划在什么样的时间采用什么样的策略来收集统计信息。也就是说,Oracle 9i的统计信息收集工作必须通过手工方式来实现。

到了Oracle 10g,默认情况下optimizer_mode=all_rows,也就是采用了CBO的方式,为了保证执行计划的准确,在周一到周五(晚22:00-次日6:00),通过一个job(gather_stat_job)自动收集对象的统计信息。这种自动收集统计信息的方式并不是收集所有对象的统计信息,而是收集没有统计信息的对象和统计信息过旧的对象。

Automatic Statistics Gathering是由Scheduler调度GATHER_STATS_JOB作业来完成的,在GATHER_STATS_JOB作业中则调用DBMS_STATS.GATHER_DATABASE_STATS_JOB_PROC存储过程。GATHER_DATABASE_STATS_JOB_PROC是一个内部的存储过程,基本上跟DBMS_STATS.GATHER_DATABASE_STATS的功能一样,但在其内部有优先顺序的考虑,更新量(变化量)越多的表将会越优先收集统计信息。为对象收集统计信息的条件是之前从来没有收集过的或者是更新的(包括insert,update,delete,truncate)记录数超过当前总记录数10%的表(在Oracle11g中则提供了SET_TABLE_PREFS函数修改10%这个阈值)。记录数的更改量由Oracle数据库自动监控,在初始化参数statistics_level设置为TYPICAL或者ALL时,自动监控即会生效。

2、****Oracle统计信息的收集分两种:****自动收集和手动收集

2.1 自动收集:

Oracle的Automatic Statistics Gathering是通过Scheduler来实现收集和维护的。Job名称是GATHER_STATS_JOB, 该Job收集数据库所有对象的2种统计信息:

(1)Missing statistics(统计信息缺失)

(2)Stale statistics(统计信息陈旧)

该JOB是在数据库创建的时候自动创建,并由Scheduler来管理。Scheduler在maintenance windows open时运行gather job。默认情况下job 会在每天晚上10到早上6点和周末全天开启(10g的收集时间)。该过程首先检测统计信息缺失和陈旧的对象。然后确定优先级,再开始进行统计信息。

Scheduler Job的stop_on_window_close 属性控制GATHER_STATS_JOB 是否继续。该属性默认值为True. 如果该值设置为False,那么GATHER_STATS_JOB 会中断,而没有收集完的对象将在下次启动时继续收集。

Gather_stats_job调用dbms_stats.gather_database_stats_job_proc过程来收集statistics 的信息。该过程收集对象statistics的条件如下:

(1)对象的统计信息之前没有收集过。

(2)当对象有超过10%的rows 被修改,此时对象的统计信息也称为stale statistics。

但是对于高度变化的表在白天的活动期间被TRUNCATE/DROP并重建或者块加载超过本身总大小10%的对象;我们可以将这些表上的统计设置为NULL。

为了决定是否对对象进行监控,Oracle 提供了一个参数STATISTICS_LEVEL。通过设置初始化参数STATISTIC_LEVEL 为TYPICAL 或ALL,就可以自动收集统计信息(默认值为TYPICAL,因此可以随即启用自动收集统计信息的功能)。STATISTIC_LEVEL 参数的值可以激活GATHER_STATS_JOB。

在10g中表监控默认是激活的,如果STATISTICS_LEVEL设置为basic,不仅不能监控表,而且将禁掉如下一些10g的新功能:

(1)ASH(Active Session History)

(2)ASSM(Automatic Shared Memory Management)

(3)AWR(Automatic Workload Repository)

(4)ADDM(Automatic Database Diagnostic Monitor)

当启动对象的监控后,从上次统计信息收集之后的的信息,如inserts,updates,deletes 等,这些改变的信息会记录到user_tab_modifications 视图。

当对象的数据发生改变之后,经过几分钟的延时,这些信息写入到user_tab_modifications视图,然后dbms_stats.flush_database_monitoring_info过程就会发现这些信息,并讲这些信息保存在内存中。

当监控的对象被修改的部分超过10%时,gather_database_stats 或者gather_schema_stats 过程就会去收集这些stale statistics。

2.1.1 Oracle 10g时的自动收集: 自动收集维护任务,周一至周五晚上22:00-6:00,周末全天。

自动收集维护任务,周一至周五晚上22:00-6:00,周末全天

sql 复制代码
SQL> select * from dba_scheduler_jobs where job_name='gather_stats_job';    ---查看自动收集统计信息的状态

关闭及开启自动收集统计信息的两种方法:

sql 复制代码
--方法01 
SQL>
exec dbms_scheduler_disable('sys.gather_stats_job'); 
exec dbms_scheduler_enable('sys.gather_stats_job'); 
      
--方法02 
SQL>
alter system set "_optimizer_autostats_job"=false scope=spfile; 
alter system set "_optimizer_autostats_job"=true scope=spfile;
****2.1.2 11g/12c时的自动收集: 自动收集维护任务,周一至周五晚上22:00开始持续4个小时,周末早上6:00开始持续20个小时

自动收集维护任务,周一至周五晚上22:00开始持续4个小时,周末早上6:00开始持续20个小时

sql 复制代码
from dba_scheduler_windows a, dba_scheduler_wingroup_members b 
where a.window_name = b.window_name 
and b.window_group_name = 'MAINTENANCE_WINDOW_GROUP';         --查看自动收集统计信息的计
MONDAY_WINDOW    freq=daily;byday=MON;byhour=22;byminute=0; bysecond=0	     +000 04:00:00
       星期一                 每周一22:00:00开始                               执行4小时

关闭及开启自动收集统计信息功能:

sql 复制代码
SQL> select * from dba_autotask_client;      ---查看11g/12c进行的自动任务,默认每天进行三个自动任务。status:任务的状态。Enable启动,disable关闭
    ---auto optimizer stats collection(自动统计信息收集)就是每日自动收集统计信息的任务
    ---sql tuning advisor(STA自动优化功能)
   
    
启用自动统计信息任务:
begin
  dbms_auto_task_admin.enable(client_name    => 'auto optimizer stats collection',
                            operation      => null,
                            window_name  => null);
end;
/
    
     
禁止自动统计信息任务:
begin
  dbms_auto_task_admin.DISABLE(client_name    => 'auto optimizer stats collection',
                             operation      => null,
                             window_name  => null);
end;
/
案例1: 11g/12c修改自动收集统计信息的时间 ,避开高峰期 周一至周五凌晨2开始持续4个小时 周六凌 晨2点开始持续20个小时;周七凌晨2点开始持续10个小时

1) ****查看自动收集统计信息任务是否打开 (如果没打开,修改了没用)

sql 复制代码
SQL> select * from dba_autotask_client 
---auto optimizer stats collection(自动统计信息收集)就是每日自动收集统计信息的任务
---sql tuning advisor(STA自动优化功能)

2)****修改自动收集统计信息的时间,需要一天一天修改哦****

sql 复制代码
周一:
begin
  sys.dbms_scheduler.set_attribute(name      => 'sys.monday_window',
                              attribute   => 'repeat_interval',
                              value      => 'freq=daily;byday=mon;byhour=2;byminute=0;bysecond=0');
  sys.dbms_scheduler.set_attribute(name      => 'sys.monday_window',
                              attribute   => 'duration',
                              value      => '0 04:00:00');
end;
/

周二:
begin
  sys.dbms_scheduler.set_attribute(name      => 'sys.tuesday_window',
                              attribute   => 'repeat_interval',
                              value      => 'freq=daily;byday=tue;byhour=2;byminute=0;bysecond=0');
  sys.dbms_scheduler.set_attribute(name      => 'sys.tuesday_window',
                              attribute   => 'duration',
                              value      => '0 04:00:00');
end;
/

周三:
begin
  sys.dbms_scheduler.set_attribute(name      => 'sys.wednesday_window',
                              attribute   => 'repeat_interval',
                              value      => 'freq=daily;byday=wed;byhour=2;byminute=0; bysecond=0');
  sys.dbms_scheduler.set_attribute(name      => 'sys.wednesday_window',
                              attribute   => 'duration',
                              value      => '0 04:00:00');
end;
/

周四:
begin
  sys.dbms_scheduler.set_attribute(name      => 'sys.thursday_window',
                              attribute   => 'repeat_interval',
                              value      => 'freq=daily;byday=thu;byhour=2;byminute=0;bysecond=0');
  sys.dbms_scheduler.set_attribute(name      => 'sys.thursday_window',
                              attribute   => 'duration',
                              value      => '0 04:00:00');
end;
/

周五:
begin
  sys.dbms_scheduler.set_attribute(name      => 'sys.friday_window',
                              attribute   => 'repeat_interval',
                              value      => 'freq=daily;byday=fri;byhour=2;byminute=0;bysecond=0');
  sys.dbms_scheduler.set_attribute(name      => 'sys.friday_window',
                              attribute   => 'duration',
                              value      => '0 04:00:00');
end;
/

周六:
begin
  sys.dbms_scheduler.set_attribute(name      => 'sys.saturday_window',
                              attribute   => 'repeat_interval',
                              value      => 'freq=daily;byday=sat;byhour=2;byminute=0;bysecond=0');
  sys.dbms_scheduler.set_attribute(name      => 'sys.saturday_window',
                              attribute   => 'duration',
                              value      => '0 20:00:00');
end;
/

周七:
begin
  sys.dbms_scheduler.set_attribute(name      => 'sys.sunday_window',
                              attribute   => 'repeat_interval',
                              value      => 'freq=daily;byday=sun;byhour=2;byminute=0;bysecond=0');
  sys.dbms_scheduler.set_attribute(name      => 'sys.sunday_window',
                              attribute   => 'duration',
                              value      => '0 10:00:00');
end;
/

3)查看自动统计信息的收集时间是否修改成功

sql 复制代码
SQL> select a.window_name, a.repeat_interval,a.duration  
      from dba_scheduler_windows a, dba_scheduler_wingroup_members b 
         where a.window_name = b.window_name 
         and b.window_group_name = 'MAINTENANCE_WINDOW_GROUP'; 

2.2 手动收集(为什么要手动收集统计信息):

Oracle自动收集统计信息都是在晚上,那么对新上线的表不能立即更新统计信息,可能导致执行计划不是最优的,给出错误的执行计划。那么这时候就要手动收集统计信息。

关于手动收集的方式在下面的第六小结就会详细介绍哦。

3、统计信息收集的级别:

在CBO(基于代价的优化器模式)条件下,SQL语句的执行计划由统计信息来决定,若没有统计信息则会采取动态采样的方式决定执行计划!可以说统计信息关乎sql的执行计划是否正确,属于sql执行的指导思想,oracle的初始化参数statistics_level控制收集统计信息的级别。

并且调整此参数也会影响AWR报告的输出内容,为TYPICAL时AWR报告将包含一般性能统计信息;为ALL时AWR报告将包含更详细的性能统计信息,以提供更全面的数据库性能分析。

sql 复制代码
SQL> show parameter statistics_level   
STATISTICS_LEVEL:STATISTICS_LEVEL指定数据库和操作系统统计信息的收集级别。Oracle数据库收集这些统计数据的目的多种多样,包括做出自我管理决策。一共有三个值:
    BASIC:收集基本的统计信息
    TYPICAL:收集大部分统计信息,足够诊断99%的性能问题(默认值)
    ALL :收集全部统计信息,包括OS以及sql执行路径方面的一些统计信息。除非遇见严重的性能问题或在一些特殊的性能诊断方面才会用到   

相关视图:

sql 复制代码
SQL> select * from v$statistics_level;      ---收集的统计信息类型,总共为24项。为Typical时收集21项;为all时收集全部24项

4、统计信息收集的内容:

统计信息可以帮助优化器更好地理解数据,从而选择更有效的查询路径。例如,如果一个表只有很少的数据,而一个全表扫描的成本很低,那么优化器可能会选择全表扫描而不是使用索引。相反,如果表的数据量很大,并且使用索引的成本更低,那么优化器可能会选择使用索引。

1)表的统计信息:

表的统计信息用于描述表的详细信息,包括记录数(num_rows)、表块的数量(blocks)、平均行长度(avg_row_len)等典型维度。

2)索引的统计信息:

索引的统计信息描述了索引的详细信息,它包含了索引的层级(blevel)、叶子块数量(leaf_blocks)、聚簇因子(clustering_factor)等典型维度。

1)层级(level): 层级表示从根节点到叶子块的深度,层级被CBO用于计算访问索引叶子块的成本,层级越大,表示从根节点到叶子块所需要访问的数据块的数量就越多,耗费的i/o就会越多,索引访问的成本就会越大。在数据库里如果需要降低索引的层级,需要rebuild才可以。

**2)聚簇因子的含义及重要性:**oracle数据库中,聚簇因子是指按照索引键值排序的索引行和存储于对应表中的数据行的存储顺序的相似程度。oracle数据库按照如下算法计算聚簇因子:

①聚簇因子初始值为1.
②oracle首先定位到目标索引处于最左边的叶子块。
③从最左边叶子块的第一个索引键值所在的索引行开始顺序扫描,在顺序扫描的过程中,oracle会比较当前索引行的rowid和之前那个索引行的rowid,如果这两个rowid并不是指向同一个表块,那么oracle就将聚簇因子的当前值递增1;如果这两个rowid是指向同一个表块,oracle就不改变聚簇因子的值。oracle在比对rowid时并不会回表去访问相应的表块。
④上述的比对过程会持续下去,知道扫描完目标索引的所有索引块的所有索引行。

⑤上述顺序扫描完成后,聚簇因子的当前值就是索引统计信息中的clustering_factor,然后存储在数据字典里。
由以上的过程可知:聚簇因子高的索引走索引范围扫描时比相同条件下聚簇因子低的索引要耗费更多的物理i/o,所以聚簇因子高的索引走索引范围扫描的成本会比相同条件下聚簇因子低的索引走索引范围扫描的成本高。即聚簇因子越小越好。

oracle数据库中,能够降低聚簇因子的唯一方法就是对表中数据按照目标索引的索引键值排序后重新存储。
oracle数据库里,cbo在计算索引范围扫描(index range scan)的成本计算公式入下:

sql 复制代码
(*)IRS COST=I/O COST+CPU COST
(*)I/O COST=INDEX ACCESS I/O COST+TABLE ACCESS I/O COST
(*)index access i/o cost=blevel_celt(#leaf_blocks*ix_sel)
(*)table access i/o cost=celt(clustering_factor*ix_sel_with_filters)

从这个公式可以推断出走索引范围扫描的成本可以近似看作是与聚簇因子成正比。因此,聚簇因子值得大小实际对CBO判断是否走相关索引起着至关重要的作用。

3)列的统计信息:

oracle里列的统计信息用于描述oracle数据库里列的详细信息,包括列的distinct值(num_distinct)、列的null值(num_nulls)得数量、列的最小值(low_value)、列的最大值(high_value)等一些典型维度。可以通过数据字典dba_tab_col_statistics、dba_part_col_statistics和dba_subpart_col_statistics分别查看表、分区表的分区、分区表的子分区的列的统计信息。

1)列的distinct值(上述数据字典中字段num_distinct表示distinct值数量),cbo用num_distinct值来计算目标列做等值查询时的可选择率。

2)上述字典中的字段num_nulls存储的就是目标列的null值数量,cbo用num_null值来评估对目标列施加"is null"或"is not null"条件后的返回结果集cardinality。另外cbo还用num_nulls值来调整对有null值得目标列做等值查询时的可选择率selectivity。对目标列进行等值查询时可选择率计算公式:selectivity=(1/num_distinct)*((num_rows-num_nulls)/num_rows)
3)上述字典的列low_value和high_value值就是目标列的最小值和最大值,cbo通过low_value和high_value来计算目标列进行范围查询时可选择率selectivity的值。

5、动态采样和统计信息的区别:

****统计信息:****statistic(统计信息)会收集数据库中对象的详细信息,通过优化器给出执行计划

****动态采样:****默认2级别的动态采样,采取对象的64个数据块进行分析,通过优化器给出执行计划

6、收集统计信息的方法和工具:

6.1 analyze收集方式(8i、9i,目前兼容)

三大功能:

1、搜集和删除索引、表和簇的统计信息

2、验证表、索引和簇的结构(analyze统计信息方式特有,dbms_stats不支持)

3、鉴定表和簇和行迁移和行链接(analyze统计信息方式特有,dbms_stats不支持)

analyze统计收集,对比于dbms_stats的优势:

1、收集在freelist上的blocks信息

2、检验存储格式的合法性

3、识别表或cluster的行迁移与行链接

analyze命令:

--01.同时收集表,表字段,索引字段(推荐)

analyze table TABLE_NAME compute statistics;

--02.表收集

analyze table TABLE_NAME compute statistics for table;

--03.表字段收集

analyze table TABLE_NAME compute statistics for all columns;

--04.索引收集

analyze table TABLE_NAME compute statistics for all indexes;

--05.索引字段收集

analyze table TABLE_NAME compute statistics for all indexed columns;

--06.同时收集表,表字段,索引

analyze table TABLE_NAME compute statistics for table for all indexes for all columns;

--07.删除统计信息

analyze table TABLE_NAME delete statistics;

****补充:****利用analyze ...validate structure..;语句验证表、索引、聚簇、分区表、临时表的结构。

对于表,该语句会扫描所有的数据块和数据行,以验证表的结构是否跟数据字典中表的定义一样,是否满足定义的各种约束

analyze table TABLE_NAME validate structure; ---分析表的结构

analyze table TABLE_NAME validate structure cascade; ---连带分析索引等结构;

analyze table TABLE_NAME validate structure online | offiline; ---当有DML操作访问该表的时候,online则支持在线验证,offline则不支持。

对于聚簇,跟表原理一样,验证聚簇的数据分布是否跟聚簇的定义一样;

对于临时表,验证该会话阶段临时表和索引的结构是否跟定义的一样;

对于索引,该语句会扫描所有的数据块,以验证是否有坏的数据块存在;

对于分区表,该语句会扫描每一个数据块和数据行,以验证行数据是否分配到正确的分区;

若验证的结构不一样,就会报错。对于该类错误,修正方法是删除、重建;

6.2 dbms_stats包收集方式(10g之后,主推的方式)

DBMS_STATS包(PL/SQL包)收集统计信息,能良好地估量统计数据(尤其是针对较大的分区表),并能取得更好统计效果。最终制订出速度更快的SQL执行计划。

可以收集数据库级别、schema级别及表级别的统计信息。还可以对统计信息删除、锁定、导出、导入等。

dbms_stats与analyze的区别:

dbms_stats是Oracle 9i及后续版本中用于收集统计信息的包,虽然analyze命令也一直可以使用,但是现在已经不推荐使用analyze命令来收集统计信息,而是使用dbms_stats。两者之间有很大的不同,dbms_stats能正确收集分区表的统计信息,也就是说能够收集global statistic,而analyze只能收集最低层次对象的统计信息,然后推导和汇总出高一级对象的统计信息,如果分区表只会收集分区统计信息,然后再汇总出所有分区的统计信息,得到表一级的统计信息。

ps补充global statistic: golbal statistic是指直接从对象本身收集到的统计信息,而不是从下一级对象"推导"和"汇总"出来的统计信息,golbal statistic对于优化器来说非常重要,一个SQL,除非其查询条件限制了数据只在分区上,否则大多数情况下需要golbal statistic才能得到正确的执行计划。有的统计值可以从下一级对象进行汇总后得到,如表的总行数,可以通过各分区的行数相加得到。但有的统计值不能通过下一级对象得到,比如列上的唯一值数量(distinct value)以及密度值(density)。

dbms_stats其实就是一个包,里面包含多可执行的存储过程:

包定义部分:

包主体部分(被隐藏):

dbms_stats收集的相关视图:

sql 复制代码
SQL> select * from dba_tab_statistics;
SQL> select * from dba_tab_col_statistics;
SQL> select * from dba_ind_statistics;

dbms_stas包不仅能够对表进行分析,它还可以对数据库进行分析:

(1)性能数据的收集

(2)性能数据的设置

(3)性能数据的删除

(4)性能数据的备份和恢复

6.2.1 dbms_stats包之GATHER_*

DBMS_STATS.GATHER_* 系列过程可以用来收集统计信息以帮助优化器生成高效的执行计划。以下是一些常用的 DBMS_STATS.GATHER_* 过程及其功能:

|------------------------------------|----------------------------------------|
| DBMS_STATS.GATHER_DATABASE_STATS | 收集整个数据库的统计信息,包括所有表、索引和其他数据库对象的统计信息。 |
| DBMS_STATS.GATHER_SCHEMA_STATS | 收集指定模式(schema)中的所有对象的统计信息。 |
| DBMS_STATS.GATHER_TABLE_STATS | 收集指定表的统计信息。 |
| DBMS_STATS.GATHER_INDEX_STATS | 收集指定索引的统计信息。 |
| DBMS_STATS.GATHER_SYSTEM_STATS | 收集硬件的统计信息。记录I/O寻址速度、I/O 传输速度、CPU 处理速度等 |
| DBMS_STATS.GATHER_DICTIONARY_STATS | 收集数据字典对象的统计信息。 |

案例1:dbms_stats.gather_database_stats收集数据库统计信息。多用于升级或迁移后

包相关参数与语法:

DBMS_STATS.GATHER_DATABASE_STATS (
            estimate_percent  NUMBER  DEFAULT to_estimate_percent_type(get_param('ESTIMATE_PERCENT')),
            block_sample  BOOLEAN  DEFAULT FALSE,
            method_opt  VARCHAR2  DEFAULT get_param('METHOD_OPT'),
            degree  NUMBER  DEFAULT to_degree_type(get_param('DEGREE')),
            granularity  VARCHAR2  DEFAULT GET_PARAM('GRANULARITY'),
            cascade  BOOLEAN  DEFAULT to_cascade_type(get_param('CASCADE')),
            stattab  VARCHAR2  DEFAULT NULL,
            Statid  VARCHAR2  DEFAULT NULL,
            Options  VARCHAR2  DEFAULT 'GATHER',
            Objlist  OUT 	ObjectTab,
            statown  VARCHAR2  DEFAULT NULL,
            force  BOOLEAN  DEFAULT FALSE, 
            gather_sys  BOOLEAN  DEFAULT TRUE,
            no_invalidate  BOOLEAN  DEFAULT to_no_invalidate_type (get_param('NO_INVALIDATE')),
            obj_filter_list  ObjectTab  DEFAULT NULL);

|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| | 描述 |
| estimate_percent | 采样行的百分比,取值范围[0.000001,100],null为全部分析,不采样。 常量:dbms_stats.auto_sample_size是默认值 , 由oracle绝定最佳取采样值。estimate_percent 参数是一种比照新的设计,它答应 Oracle 的dbms_stats 在搜罗统计数据时,自动估量要采样的一个segment的最佳百分比:estimate_percent => dbms_stats.auto_sample_size, 自动统计采样。在使用自动采样时,Oracle 会为一个样本尺寸挑选5到20的百分比的统计。记住统计数据品质越好,CBO做出的选择越好。 |
| block_sapmple | 是否用块采样代替行采样 |
| method_opt | 决定 histograms 直方图信息是怎样被统计的. method_opt 的取值如下: for table ---只统计表的 histograms. for all columns ---统计所有列的 histograms. for all indexed columns: ---统计所有 indexed 列的 histograms. for all indexes ---统计所有索引 for all hidden columns ---统计你看不到列的 histograms for columns <list> size <n>|repeat|auto|skewonly: 统计指定列的histograms。n 的取值范围[1,254]; repeat 上次统计过的 histograms;auto 由oracle决定n的大小;skewonly,Oracle 确定需要收集检查每个索引中每列值的分布。如: method_opt=>'for all columns size skewonly' method_opt=>'for all columns size repeat' method_opt=>'for all columns size auto' |
| Degree | 决定并行度.默认值为null. 分析索引速度加快,根据 cpu 数量来设置,一般在业务空闲的时候 degree 可设为 cpu 数量-1,繁忙的时候就再小点。 |
| granularity | 分区统计的收集可以通过声明参数GRANULARITY。根据将优化的SQL语句,优化器可以选择使用分区统计或全局统计,对于大多数系统这两种统计都是很重要的,Oracle推荐将GRANULARITY设置为AUTO同时收集全部信息。其中Granularity用于定义采集粒度: ALL:采集Global、partition、subpartition等粒度统计信息。 AUTO:根据分区类型,由Oracle确定统计信息采集粒度。 PARTITION:只采集partition粒度统计信息。 SUBPARTITION:只采集subpartition粒度统计信息。 |
| Cascade | 是收集索引的信息.默认为 falase |
| Stattab | 指定要存储统计信息的表 |
| Statid | 如果多个表的统计信息存储在同一个 stattab 中用于进行区分。 |
| Options | 使用4个预设的法子之一,这个选项能把握 Oracle 统计的刷新方法: Gather ------重新分析整个(Schema)。 gather empty ------只分析目前还没有统计的表。 gather stale ------只重新分析修改量超过 10%的表(这些修改包含插入、更新和删除)。 gather auto ------重新分析以前没有统计的对象,以及统计数据过期(变脏)的对象。 注意:使用 gather auto 相似于组合使用 gather stale 和 gather empty。不论 gather stale仍是gather auto,都请求进行监视。假如你施行一个alter table xxx monitoring 命令,Oracle会用dba_tab_modifications 视图来跟踪发生变动的表。这样一来,你就确实地知道,自从上一次剖析统计数据以来,发生了多少次插入、更新和删除操作。 |
| objlist: | 指定对象列表 |
| Statown | 存储统计信息 表的拥有者.以上三个参数若不指定,统计信息会直接更新到数据字典 |
| Force | 即使表锁住了也收集统计信息 |
| gather_sys | 只收集 sys 的对象 |
| no_invalidate | 通过不同的参数配置,可以实现对 Oracle 失效共享游标行为的控制TRUE,FALSE。 如果取值为 true,表示不进行游标失效动作,原有的 shared cursor 保持原有状态。 如果取值为 false,表示将统计量对象相关的所有 cursor 全部失效。如果设置为auto_invalidate,根据官方文档,Oracle 自己决定shared cursor失效动作。从10G开始,Oracle就将auto_invalidate 作为默认的统计量收集行为:select dbms_stats.get_param(pname => 'no_invalidate') from dual; |
| obj_filter_list | 对象筛选器列表 |

1)收集数据库层面的统计信息

sql 复制代码
[oracle@lf01 backup]$ vi status.sql 

begin 
dbms_stats.gather_database_stats; 
end; 
/ 

[oracle@lf01 backup]$ nohup sqlplus / as sysdba @status.sql &    ---因为收集统计信息时间长,所以写个sh后台运行

2)查询所有表、列、索引的统计信息是否被收集:

sql 复制代码
SQL> select table_name,blocks,num_rows,avg_row_len,last_analyzed from dba_tables where owner='user';                    ---迁移后的生产用户
       
SQL> select column_name,num_distinct,num_nulls,histogram,last_analyzed from dba_tab_columns where owner='user';              ---迁移后的生产用户
              
SQL> select index_name,leaf_blocks,clustering_factor,blevel,num_rows,last_analyzed from dba_indexes where owner='user';  ---迁移后的生产用户
案例2:dbms_stats.gather_schema_stats收集用户统计信息。

包相关参数与语法:

sql 复制代码
DBMS_STATS.GATHER_SCHEMA_STATS ( 
            ownname  VARCHAR2, 
            estimate_percent  NUMBER  DEFAULT to_estimate_percent_type (get_param('ESTIMATE_PERCENT')), 
            block_sample  BOOLEAN  DEFAULT FALSE, 
            method_opt   VARCHAR2  DEFAULT get_param('METHOD_OPT'), 
            degree   NUMBER  DEFAULT to_degree_type(get_param('DEGREE')), 
            granularity  VARCHAR2  DEFAULT GET_PARAM('GRANULARITY'),
            cascade  BOOLEAN  DEFAULT to_cascade_type(get_param('CASCADE')), 
            stattab  VARCHAR2  DEFAULT NULL, 
            statid  VARCHAR2  DEFAULT NULL, 
            options  VARCHAR2  DEFAULT 'GATHER', 
            objlist  OUT  ObjectTab, 
            statown  VARCHAR2  DEFAULT NULL, 
            no_invalidate  BOOLEAN  DEFAULT to_no_invalidate_type ( get_param('NO_INVALIDATE')), 
            force  BOOLEAN  DEFAULT FALSE, 
            obj_filter_list  ObjectTab  DEFAULT NULL);

|---------|---------|
| | 描述 |
| ownname | 要分析的拥有者 |
| 其他参数和案例1的dbms_stats.gather_database_stats是相同的,可以参考案例1的 ||

1)收集用户层面的统计信息

sql 复制代码
SQL> begin 
dbms_stats.gather_schema_stats(ownname           => 'user',      ---收集的用户名
                               options           => 'gather auto',     --重新分析以前没有统计的对象,以及统计数据过期(变脏)的对象
                               estimate_percent  => dbms_stats.auto_sample_size,   ---自动统计采样
                               method_opt        => 'for all indexed columns',   ---直方图收集所有索引列
                               degree            => 2);         ---并行度
end;
/

2)查询所有表、列、索引的统计信息是否被收集:

sql 复制代码
SQL> select table_name,blocks,num_rows,avg_row_len,last_analyzed from dba_tables where owner='ITPUX';                      ---查看itpux用户表的统计信息
   
SQL> select column_name,num_distinct,num_nulls,histogram,last_analyzed from dba_tab_columns where owner='ITPUX';                ---查看itpux用户表列的统计信息
                 
SQL> select index_name,leaf_blocks,clustering_factor,blevel,num_rows,last_analyzed from dba_indexes where owner='ITPUX';    ---查看itpux用户索引的统计信息
案例3: dbms_stats.gather_table_stats收集表统计信息。对新上线的表手动收集统计信息。Oracle统计信息会晚上自动统计,为了给出真实的执行计划手动收集统计信息,便于下午分析

包相关参数与语法:

sql 复制代码
DBMS_STATS.GATHER_TABLE_STATS ( 
            ownname  varchar2,
            tabname  varchar2,
            Partname  varchar2  default null,
            estimate_percent  number  default DEFAULT_ESTIMATE_PERCENT,
            block_sample  boolean  default FALSE,
            method_opt  varchar2  default DEFAULT_METHOD_OPT,
            degree  number  default to_degree_type(get_param('DEGREE')),
            granularity  varchar2  default DEFAULT_GRANULARITY,
            cascade  boolean  default DEFAULT_CASCADE,
            stattab  varchar2  default null,
            Statid  varchar2  default null,
            statown  varchar2  default null,
            no_invalidate  boolean  default 
            to_no_invalidate_type(get_param('NO_INVALIDATE')),
            stattype  varchar2  default 'DATA',
            force  boolean  default FALSE);

|----------|--------------------|
| | 描述 |
| ownname | 要分析的拥有者 |
| tabname | 要分析的表名 |
| partname | 分区的名字,只对分区表或分区索引有用 |
| 其他参数和案例1的dbms_stats.gather_database_stats是相同的,可以参考案例1的 ||

1)查看表当前的统计信息情况

sql 复制代码
SQL> select count(*) from table_m11;   ---table_m11有1万行数据
       
SQL> select * from dba_tables where owner='USER' and table_name='TABLE_M11';

Num_rows列记录着表的行数量,oracle每晚会自动收集行数据的信息,然后把数量维护到列中。因为表刚上线,所以统计信息还没有更新

2)如果当天下午对表进行查询了,那么由于统计信息不全导致执行计划不真实,就需要手动收集统计信息

sql 复制代码
SQL> begin 
dbms_stats.gather_table_stats(ownname           =>'user',       ---收集的用户名
                              tabname           =>'table_m11',  ---要分析的表名
                              estimate_percent  => dbms_stats.auto_sample_size,   ---自动统计采样
                              degree            => 2);          ---并行度

end; 
/ 

3)查看表、表的字段、表的索引的统计信息情况

sql 复制代码
SQL> select table_name,blocks,num_rows,avg_row_len,last_analyzed from dba_tables where owner='USER' and table_name='TABLE_M11'; 
sql 复制代码
SQL> select column_name,num_distinct,num_nulls,histogram,last_analyzed from dba_tab_columns where owner='USER' and table_name='TABLE_M11';

num_distinct列记录着表下每个列数据不同值,对信息统计来说非常重要

sql 复制代码
SQL> select index_name,leaf_blocks,clustering_factor,blevel,num_rows,last_analyzed from dba_indexes where owner='USER' and table_name='TABLE_M11';
6.2.2 dbms_stats包之DELETE_*

DBMS_STATS.DELETE_* 过程用于删除数据库中收集的统计信息。你可以使用它来删除特定表、索引或模式的统计信息,以便重新收集或清理不需要的信息。这个过程有助于保持数据库统计信息的准确性和相关性。以下是一些常用的 DBMS_STATS.DELETE_*过程及其功能:

|------------------------------------|-------------------|
| dbms_stats.delete_database_stats | 删除数据库统计信息。 |
| dbms_stats.delete_schema_stats | 删除特定模式中所有对象的统计信息。 |
| dbms_stats.delete_table_stats | 删除特定表的统计信息。 |
| dbms_stats.delete_index_stats | 删除特定索引的统计信息。 |
| dbms_stats.delete_column_stats | 删除特定列统计信息。 |
| dbms_stats.delete_dictionary_stats | 删除特定数据字典统计信息。 |

PS小提示 💥****:** 删除统计信息后,还没有自动收集的时,oracle的执行计划默认根据动态采样来选择执行计划** (动态采样用于弥补统计信息缺失时使用)

sql 复制代码
SQL> set autotrace traceonly
SQL> select * from table_m11;    ---使用动态采样

table_m11有20000行数据,优化器根据动态采样做出执行计划时,误差到23139行

sql 复制代码
SQL> show parameter optimizer_dynamic_sampling    
动态采样2级别。表示没有统计信息时使用动态采样影响执行计划;如果设置为0,表示没有统计信息时也不使用动态采样,那么执行计划误差非常大
案例1:删除数据库统计信息 (只做了解,生产环境不要操作)
sql 复制代码
SQL>
begin
    dbms_stats.delete_database_stats;
end;
/
案例2:删除用户统计信息 (只做了解,生产环境不要操作)
sql 复制代码
SQL>
begin
    dbms_stats.delete_schema_stats(ownname => 'USER');
end;
/
案例3:删除表统计信息 (只做了解,生产环境不要操作)
sql 复制代码
SQL> 
begin
    dbms_stats.delete_table_stats(ownname  => 'USER',
                                  tabname  => 'TABLE_NAME');
end;
/
案例4:删除索引统计信息 (只做了解,生产环境不要操作)
sql 复制代码
SQL>
begin
    dbms_stats.delete_index_stats(ownname   => 'USER',
                                  indname   =>'idx_user_table_field');
end;
/
6.2.3 dbms_stats包之LOCK_*/UNLOCK_*

DBMS_STATS 包提供了用于锁定和解锁统计信息的过程,以防止统计信息在收集过程中被意外修改或删除。这对维护数据的一致性和优化查询性能非常重要。以下是一些常用的 DBMS_STATS.LOCK_*/UNLOCK_*过程及其功能:

|-----------------------------------|-----------|
| 锁定统计信息: ||
| dbms_stats.lock_schema_stats | 锁定用户统计信息 |
| dbms_stats.lock_table_stats | 锁定表统计信息 |
| dbms_stats.lock_partition_stats | 锁定分区表统计信息 |
| 解锁统计信息: ||
| dbms_stats.unlock_schema_stats | 解锁用户统计信息 |
| dbms_stats.unlock_table_stats | 解锁用户统计信息 |
| dbms_stats.unlock_partition_stats | 解锁分区表统计信息 |

查看哪些表的统计信息被锁定 (stattype_locked字段为ALL的表示锁定了表的统计信息,默认stattype_locked字段为空表示可以收集统计信息):

sql 复制代码
select * from dba_ind_statistics where stattype_locked='ALL';
select * from dba_tab_statistics where stattype_locked='ALL'; 

ps小提示💥:oracle默认有部分表的统计信息是锁定的不允许收集

案例1:锁定并解锁表的统计信息

1)锁定分区表的统计信息

sql 复制代码
SQL>
begin
    dbms_stats.lock_table_stats(ownname  => 'USER',
                                tabname  => 'TABLE_L1');   ---分区表
end;
/

SQL> select * from dba_tab_statistics where owner='USER' and stattype_locked='ALL';   ---stattype_locked字段为ALL的表示锁定了表的统计信息,默认stattype_locked字段为空表示可以收集统计信息

2)收集表的统计信息,提交被锁

sql 复制代码
SQL>
begin
    dbms_stats.gather_table_stats(ownname  => 'USER',
                                  tabname  => 'TABLE_L1'); 
end;
/

3)解锁分区表的统计信息

sql 复制代码
SQL>
begin
    dbms_stats.unlock_table_stats(ownname  => 'USER',
                                  tabname  => 'TABLE_L1');  
end;
/

4)继续收集表的统计信息

sql 复制代码
SQL> 
begin
    dbms_stats.gather_table_stats(ownname  => 'USER',
                                  tabname  => 'TABLE_L1'); 
end;
/

SQL> select table_name,blocks,num_rows,avg_row_len,last_analyzed from dba_tables where owner='USER' and table_name='TABLE_L1';  
6.2.4 dbms_stats包之包的重建

如果各位不小心误删除dbms_stats包,或者dbms_stats包自身出现了一些bug,那么Oracle提供重建dbms_stats包的办法

案例一: 重建dbms_stats包。如果dbms_stats包误删或者出问题,那么通过执行oracle内部4个脚本重建即可

1)误删除dbms_stats包

sql 复制代码
SQL> drop package dbms_stats;

2)执行4个脚本重建dbms_stats包

sql 复制代码
SQL>
@?/rdbms/admin/dbmsstat.sql 
@?/rdbms/admin/prvtstas.plb 
@?/rdbms/admin/prvtstai.plb 
@?/rdbms/admin/prvtstat.plb

7、数据库默认收集的统计信息:

Table statistics(表统计信息)

Column statistics(列统计信息)

Index statistics(索引统计信息)

system statistics(系统统计信息)

****Table statistics相关视图:****user_tables、all_tables、dba_tables

sql 复制代码
SQL> select table_name,blocks,num_rows,EMPTY_BLOCKS,avg_row_len,last_analyzed from dba_tables where owner='USER' and table_name='TABLE_M11';               ---以table_m11表为例收集表的统计信息
    Blocks:表占用的块数,可以计算出表多大
    num_rows:表的总行数
    Avg_row_len:行的平均字符长度
    EMPTY_BLOCKS:表中未使用数据块的数量,只有当使用DBMS_STATS包收集表上的统计信息时,才会填充此列。
    last_analyzed:表的统计信息的最后收集时间,sql执行计划受统计信息影响。
    
SQL> select Table_Name,Partition_Name,High_Value,Num_Rows,EMPTY_BLOCKS,last_analyzed from dba_tab_partitions where TABLE_NAME='TABLE_M10';
    
SQL> select Table_Name,Partition_Name,High_Value,Num_Rows,EMPTY_BLOCKS,last_analyzed from dba_tab_subpartitions where TABLE_NAME='TABLE_M10';

****Column statistics相关视图:****user_tab_columns、all_tab_columns、dba_tab_columns

sql 复制代码
SQL> select column_name,num_distinct,num_nulls,histogram,last_analyzed from dba_tab_columns where owner='USER' and table_name='TABLE_M11';         ---以table_m11表为例收集列的统计信息
    num_distinct:表下每个列数据的不同值有多少行
    num_nulls:表下每个列中有空值的数量。0表示没有空值,都有数据
    histogram:直方图。统计报告图,描述数据分布
    last_analyzed:表的统计信息的最后收集时间,sql执行计划受统计信息影响。

****Index statistics相关视图 :****user_indexes、all_indexes、dba_indexes

sql 复制代码
SQL> select index_name,leaf_blocks,clustering_factor,blevel,num_rows,last_analyzed from dba_indexes where owner='USER' and table_name='TABLE_M1';      ---以table_m1表为例收集索引的统计信息

SQL> select t2.table_name,t1.index_name,t1.partition_name,t1.last_analyzed,t1.num_rows,t1.leaf_blocks,t1.status 
       from dba_ind_partitions t1, dba_indexes t2 
       where t1.index_name=t2.index_name and t2.table_name='TABLE_M10';
   leaf_blocks:索引中叶子块数量。
   clustering_factor:集群因子。值越高越差,小于总行数(表的总num_rows数),接近块数最好(表的总blocks数)
   Blevel:b-tree索引等级
   num_rows:索引的总行数
   last_analyzed:表的统计信息的最后收集时间,sql执行计划受统计信息影响。 

****system statistics相关视图:****aux_stats、XKCFIO

sql 复制代码
SQL> select * from sys.aux_stats$;
       
SQL> select * from sys.x$KCFIO;            ---两个表记录系统IO/cpu使用率。数据库做系统统计信息后IO/cpu等信息记录到两张表中

8、统计信息的保留时间以及发布验证功能:

10G当一个表生成新的统计信息时,该表的旧统计信息会保留。如果依赖该新统计信息的查询出现性能问题时,可以恢复旧的来解决可能出现的性能问题。10g、11g中对象旧的统计信息默认的保存时间是31天,统计信息在sysaux表空间有存储开销。

案例一:查看旧的统计信息默认保留天数,并修改 (对于大数据的库频繁收集统计可能导致sysaux表空间增速过快,适当减少旧的统计信息的保留时间。31days---7days):

sql 复制代码
SQL> select dbms_stats.get_stats_history_retention from dual;
sql 复制代码
SQL> select dbms_stats.get_stats_history_availability from dual;    ---最晚可恢复的统计信息。一般是当前时间减去31天

利用DBMS_STATS.ALTER_STATS_HISTORY_RETENTION包更改对象的统计信息保存时间:

sql 复制代码
SQL> begin
       dbms_stats.alter_stats_history_retention(retention =>20);     ---统计信息保留20天
     end;
     /

🎉"呼~,统计信息到这里就结束了,已经2万字了,各位小伙伴可以休息一下眼睛了.........

🐟 摸鱼时间到!不是逃避,是智慧的充电站哦!

  • 🕒 摸鱼1分钟:快速眨眼操,给眼睛做个微型瑜伽,恢复亮晶晶!
  • 🕒 摸鱼5分钟:站起来,转转脖子,扭扭腰,办公室也能跳出广场舞的节奏!
  • 🕒 摸鱼10分钟:想象自己是条深海鱼,在思维的海洋里自由遨游,说不定下一个灵感大鱼就上钩了!

好啦,休息完毕,电量满格!接下来开始动态采样的学习!记得,学习路上,适当摸鱼,效率加倍哦!🚀"

二、动态采样 (当对象还没有统计信息时,那么先通过动态采样技术来选择执行计划,动态采样用于弥补统计信息缺失时使用)

在CBO(基于代价的优化器模式)条件下,SQL语句的执行计划由统计信息来决定,若没有统计信息则会采取动态采样的方式决定执行计划。统计信息关乎sql的执行计划是否正确,属于sql执行的指导思想。

动态采样(Dynamic Sampling)提出是在9iR2,在段(表,索引,分区)没有分析的情况下,为了使CBO优化器得到足够的信息以保证做出正确的执行计划而发明的一种技术。可以把它看做分析手段的一种补充。当段对象没有统计信息时(即没有做分析),动态采样技术可以通过直接从需要分析的对象上收集数据块(采样)来获得CBO需要的统计信息

有动态采样虽然有少许的误差,但是比不采样好的多,最起码执行计划不会很离谱。

动态采样和统计信息的区别:

****统计信息:****statistic(统计信息)会收集数据库中对象的详细信息,通过优化器给出执行计划

****动态采样:****默认2级别的动态采样,采取对象的64个数据块进行分析,通过优化器给出执行计划

动态采样的10个级别:

sql 复制代码
SYS@orcl> show parameter optimizer_dynamic_sampling    ---动态采样2级别。表示没有统计信息时使用动态采样影响执行计划;如果设置为0,表示没有统计信息时也不使用动态采样,那么执行计划误差非常大

Level 0:不做动态分析

Level 1:Oracle对没有分析的表进行动态采样,但需要同时满足以下 4 个条件。

(1)SQL 中至少有一个未分析的表

(2)未分析的表出现在关联查询或者子查询中

(3)未分析的表没有索引

(4)未分析的表占用的数据块要大于动态采样默认的数据块(32 个)

Level 2:对所有的未分析表做分析,动态采样的数据块是默认数据块的2倍,即 64 个。

Level 3:采样的表包含满足 Level 2 定义的所有表,同时包括,那些谓词有可能潜在地需要动态采样的表,这些动态采样的数据块为默认数据块,对没有分析的表,动态采样的默认块为默认数据块的 2 倍, 即 64 个 。

Level 4:采样的表包含满足 Level 3 定义的表,同时还包括一些表,他们包含一个单表的谓词会引用另外的 2 个列或者更多的列;采样的块数是动态采样默认数据块数; 对没有分析的表,动态采样的数据块为默认数据块的 2 倍。

Level 5,6,7,8,9:采样的表包含满足 Level 4 定义的表,同时分别使用动态采样默认数据块的 2,4,8,32,128 倍的数量来做动态分析。

Level 10:采样的表包含满足 Level 9 定义的所有表,同时对表的所有数据进行动态采样。采样的数据块越多,得到的分析数据就越接近与真实,但同时伴随着资源消耗的也越大


当表中的统计信息被删除或过时,数据库优化器在执行查询时无法依赖现有的统计数据来准确评估数据分布和选择性,从而难以直接判断最优的查询执行计划。为了弥补这一不足,优化器通常会采用动态采样的方式来获取实时的数据样本,以辅助生成执行计划,那么让我们通过案例一起了解下:

案例1: 删除表的统计信息,默认使用动态采样2级别通过优化器给出执行计划

sql 复制代码
SQL> show parameter optimizer_dynamic_sampling      ---动态采样2级别。表示没有统计信息时使用动态采样影响执行计划;如果设置为0,表示没有统计信息时也不使用动态采样,那么执行计划误差非常大
sql 复制代码
SQL> begin
        dbms_stats.delete_table_stats(ownname  => 'USER',
                                      tabname  => 'TABLE_M11');
end;
/

SQL> set autotrace traceonly
SQL> select * from table_m11;    ---使用动态采样

table_m11真实有20000行数据,优化器根据动态采样采取64个块做出执行计划时,误差到23139行


那么还有一种极端情况,删除表统计信息的同时,也不使用动态采样,会发生什么情况呢,这么做太不给Oracle面子(Oracle:你会玩,你清高),这种只是测试了解哦,千万不要在生产瞎搞😄

案例2: 删除表的统计信息,也不使用动态采样

sql 复制代码
SQL> alter session set optimizer_dynamic_sampling=0;
SQL> show parameter optimizer_dynamic_sampling             ---动态采样0级别。不做动态分析 
sql 复制代码
SQL> begin
       dbms_stats.delete_table_stats(ownname  => 'USER',
                                     tabname  => 'TABLE_M11');
end;
/
 
ITPUX@orcl> set autotrace traceonly
ITPUX@orcl> select * from table_m11; 

table_m11真实有20000行数据,优化器直接给出执行计划时,误差到51949行。误差的原因是因为高水位导致的分配的段、块没有被回收,那么就以高水位为基准算出块大小;如果采用动态采样,那么会扫描到有些块高水位以下部分是空的,那么扫描的行就变少了


两眼发昏,洋洋洒洒2万7千余字,这篇文章到这里就算结束了, 构思了1天,撰写了5小时,关于统计信息这部分涉及到理论,所以我的这篇文章可能并不完美,但是已经非常用心在整理了,真的虚了,需要一根士力架做回自己,那各位小伙伴们就慢慢消化吧,记得一键三连哦。

相关推荐
激流丶26 分钟前
【Mysql 底层原理】MySQL 查询优化器的工作原理:如何生成最优执行计划
数据库·mysql·explain·执行计划
脱了格子衬衫39 分钟前
linux安装ansible
linux·运维·ansible
dogplays41 分钟前
sqoop import将Oracle数据加载至hive,数据量变少,只能导入一个mapper的数据量
hive·oracle·sqoop
雷神乐乐42 分钟前
Sqoop学习
数据库·sqoop
小丑西瓜6661 小时前
MySQL库操作
linux·服务器·数据库·mysql
谦谦均1 小时前
PostgreSQL序列:创建、管理与高效应用指南
数据库·postgresql
荒川之神1 小时前
RHEL/CENTOS 7 ORACLE 19C-RAC安装(纯命令版)
服务器·数据库·oracle
ZWZhangYu1 小时前
【MyBatis源码】深入分析TypeHandler原理和源码
数据库·oracle·mybatis
黑龙江亿林等保2 小时前
云ECS服务器:哈尔滨三级等保的云计算解决方案
运维·服务器·云计算
sagima_sdu2 小时前
Python 操作 Neo4J,Python 库 Py2Neo
python·oracle·neo4j