Oracle统计信息不准(谓词越界)造成的性能问题

什么是谓词越界?谓词越界其实就是SQL语句的查询条件超出了数据库统计信息所记录的范围。谓词越界会导致Oracle优化器错误的选择SQL语句的执行计划,导致性能问题。

这里举一个简单的例子说明谓词越界导致优化器选择了错误的执行计划。

create table t1 (col1 number);
create index idx_t1 on t1(col1);

begin
  for i in 1..10000 loop
  insert into t1 values (i);
  end loop;
  commit;
end;
/

这里创建了t1表,并在col1列上创建了索引,并向表里写入了10000条数据。提供过对t1表收集统计信息,可以得到目前表t1的谓词情况。

SQL> exec dbms_stats.gather_table_stats(‘SALP‘,‘T1‘);
SQL> select low_value,high_value from dba_tab_col_statistics where table_name=‘T1‘ and owner=‘SALP‘;

LOW_VALUE  HIGH_VALUE
---------- ----------
C102       C302

SQL> var x number;
SQL> exec dbms_stats.convert_raw_value(‘C102‘,:x);

PL/SQL procedure successfully completed.

SQL> select :x from dual;

        :X
----------
         1

SQL> exec dbms_stats.convert_raw_value(‘C302‘,:x);

PL/SQL procedure successfully completed.

SQL> select :x from dual;

        :X
----------
     10000

上面用到了一个系统包,把统计信息表里的上下限裸数据转换成可读的数值。

在谓词范围内的条件查询的执行计划为

explain plan for select * from t1 where col1 between 1 and 10000;
select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1387720244
-------------------------------------------------------------------------------
| Id  | Operation            | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |        | 10000 | 40000 |     7   (0)| 00:00:01 |
|*  1 |  INDEX FAST FULL SCAN| IDX_T1 | 10000 | 40000 |     7   (0)| 00:00:01 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("COL1">=1 AND "COL1"<=10000)
13 rows selected.

因为这里的条件包含了t1表内的所有数据,所以采用多块读且不需要回表的执行计划是最优的(table access full/index fast full scan),这里实际使用的是index fast full scan。

接下来继续向t1表写入数据

begin
  for i in 10001..10000000 loop
  insert into t1 values (i);
  end loop;
  commit;
end;
/

在不重新收集统计信息的情况下,检查表的统计信息

select low_value,high_value from dba_tab_col_statistics where table_name=‘T1‘ and owner=‘SALP‘;

LOW_VALUE  HIGH_VALUE
---------- ----------
C102       C302

现在来进行一次谓词越界的查询,使用谓词条件 col1 between 10001 and 10000000。按道理来说,这种选择表里99.9%数据的语句应该使用多块读且不回表的执行计划(table access full/index fast full scan)。我们来实际试验一下。

SQL> set timing on;
SQL> Select count(*) from t1 where col1 between 10001 and 10000000;

  COUNT(*)
----------
   9990000

Elapsed: 00:00:11.17
SQL> select * from table(dbms_xplan.display_cursor(null,null,‘advanced‘));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID  86kr1tnhns36d, child number 0
-------------------------------------
Select count(*) from t1 where col1 between 10001 and 10000000

Plan hash value: 1970818898

----------------------------------------------------------------------------
| Id  | Operation         | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |        |       |       |     2 (100)|          |
|   1 |  SORT AGGREGATE   |        |     1 |     4 |            |          |
|*  2 |   INDEX RANGE SCAN| IDX_T1 |     1 |     4 |     2   (0)| 00:00:01 |
----------------------------------------------------------------------------

可以看到这条语句执行了11s才出结果,且执行计划选择的是单块读的index range scan,而不是我们期望的多块读不回表的两种执行计划之一且返回的Rows和Bytes出现了严重预估错误。

我们重新为t1表收集一次统计信息,再次执行同样的语句并检查执行计划。

SQL> exec dbms_stats.gather_table_stats(‘SALP‘,‘T1‘);

PL/SQL procedure successfully completed.
Elapsed: 00:00:07.92
SQL> select count(*) from t1 where col1 between 10001 and 10000000;

  COUNT(*)
----------
   9990000

Elapsed: 00:00:00.31
SQL> select * from table(dbms_xplan.display_cursor(null,null,‘advanced‘));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID  g47843nv7gsdq, child number 0
-------------------------------------
select count(*) from t1 where col1 between 10001 and 10000000

Plan hash value: 3724264953

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |       |       |  4434 (100)|          |
|   1 |  SORT AGGREGATE    |      |     1 |     6 |            |          |
|*  2 |   TABLE ACCESS FULL| T1   |  9990K|    57M|  4434   (2)| 00:00:01 |
---------------------------------------------------------------------------

这次走出了我们希望的执行计划(table access full),预估的Rows和Bytes也都正常了,且语句花了310ms就运行完了。

谓词越界一般会发生在什么场景下?

1 临时表

这里指的是业务上的临时表而不是Oracle数据库本身的temporary table。在某些系统中会根据业务条件创建前台表和后台表,数据先进入前台表,处理完毕后,存入后台表,并用delete语句清理前台表的数据,前台表起到一个临时表的作用。我们知道,Oracle自动收集统计信息的默认时间窗口是工作日晚上的22点到凌晨2点,或者周末的早上6点到第二天凌晨2点。在自动收集统计信息窗口内,数据库前台表基本上处于无数据,或者数据量很小的情况,那么产生的统计信息就会和白天实际处理业务数据时有偏差,就有可能发生谓词越界的情况。

2 巨大表

Oracle触发自动收集某个表的统计信息的条件是表中修改的数据量超过该表数据总量的10%,假设一个表每天新增1w条数据,一年后这个表变成了365w条数据,那么这意味着这个表需要再过一个多月才会触发一次自动收集统计信息的作业。那么在这个表上的谓词查询,尤其是时间、序列等自增条件上的查询,就可能发生谓词越界的情况,影响优化器正确选择执行计划。

原文地址:https://www.cnblogs.com/aegis1019/p/9059220.html

时间: 2024-12-17 07:50:16

Oracle统计信息不准(谓词越界)造成的性能问题的相关文章

统计信息不准导致执行计划出错跑不出结果,优化后只要1分钟

一天查看数据库长会话,发现1个sql跑得很慢,1个多小时不出结果,花了点时间把它给优化了. 优化前: SELECT 20131023, "A2"."ORG_ID", COUNT(DISTINCT NLSSORT(CASE "A2"."RES_TYPE" WHEN 'DP' THEN "A2"."RES_CODE" END, 'nls_sort=''BINARY''')), COUNT(D

收集oracle统计信息

优化器统计范围: 表统计: --行数,块数,行平均长度:all_tables:NUM_ROWS,BLOCKS,AVG_ROW_LEN:列统计: --列中唯一值的数量(NDV),NULL值的数量,数据分布:             --DBA_TAB_COLUMNS:NUM_DISTINCT,NUM_NULLS,HISTOGRAM:索引统计:--叶块数量,等级,聚簇因子:             --DBA_INDEXES:LEAF_BLOCKS,CLUSTERING_FACTOR,BLEVEL:

Oracle 统计信息介绍

统计信息自动执行需要以下条件满足: dba_autotask_task 字段status值ENABLED dba_autotask_client 字段status值ENABLED dba_autotask_window_clients 字段AUTOTASK_STATUS值ENABLED OPTIMIZER_STATS值ENABLED WINDOW_ACTIVE值FALSE dba_scheduler_windows 字段ENABLED值TRUE 字段ACTIVE值FALSE 字段DURATION

【练习】ORACLE统计信息--直方图

①创建表tSQL> create table t as select * from dba_objects; Table created. --收集直方图 SQL> exec dbms_stats.gather_table_stats(user,'T'); PL/SQL procedure successfully completed. --此时数据库为表上的所有字段收集了缺省的统计信息,每个列两个Bucket: SQL> col column_name for a30 SQL>

oracle统计信息

create table t2 as select * from dba_objects; 建索引: create index idx_t2 on t2(object_id); 2.收集T2的表所有列和表上所有索引的统计信息: exec dbms_stats.gather_table_stats(ownname=>'SYS',tabname=>'T2',estimate_percent=>100,cascade=>true,degree=>4); degree表示并行度: 3

Oracle 统计信息收集

统计信息收集 1:创建分析表 $ cd $ORACLE_HOME/rdbms/admin $ sqlplus / as sysdba > @utlxplan.sql 2:为了方便,可以创建一个同义表(默认情况下只有sys用户可以使用) SQL> create public synonym plan_tables for plan_table; 3:把这个表的权限给所有人,也可以给指定的人 SQL> grant all on plan_tables to public; 4:创建plust

oracle数据库优化之统计信息

1.统计信息简介 统计信息主要是描述数据库中表,索引的大小,规模,数据分布状况等的一类信息.比如,表的行数,块数,平均每行的大小,索引的leaf blocks,索引字段的行数,不同值的大小等,都属于统计信息.CBO正是根据这些统计信息数据,计算出不同访问路径下,不同join 方式下,各种计划的成本,最后选择出成本最小的计划. 在CBO(基于代价的优化器模式)条件下,SQL语句的执行计划由统计信息来决定,若没有统计信息则会采取动态采样的方式决定执行计划!可以说统计信息关乎sql的执行计划是否正确,

oracle表统计信息的使用

最近开发处理ECIF相关的项目,使用存储过程跑批处理数据.定期跑批数据,每次存储过程跑批使用临时表进行数据整理,再join这张临时表将数据放入目标表,最后清空临时表. 问题:在join处理这步中执行较慢,有时半天不出结果集. 解决方案:更新数据库表统计信息 + 使用多进程执行 多进程在此不多讲.每次往临时表插入数据,数据达到几百万条,存储过程结束之后又清除表,这样会让数据库误以为该临时表数据一致为0,因数据库表统计信息并非实时.更新表统计信息能良好地估计统计数据(尤其是针对较大的分区表),并能获

SQL Server统计信息:问题和解决方式

在网上看到一篇介绍使用统计信息出现的问题已经解决方式,感觉写的很全面. 在自己看的过程中顺便做了翻译. 因为本人英文水平有限,可能中间有一些错误. 假设有哪里有问题欢迎大家批评指正.建议英文好的直接看原文:SQL Server Statistics: Problems and Solutions 正文: SQL Server统计信息协助查询优化器计算执行查询的最优方式. Holger描写叙述了常见的统计信息出错的事情,而且怎样改善 通常你不须要太操心运行SQL查询的方式.他们被传送到查询优化器,