ITPub博客

首页 > Linux操作系统 > Linux操作系统 > 挥发性表的统计信息问题

挥发性表的统计信息问题

原创 Linux操作系统 作者:redhouser 时间:2012-12-11 15:45:28 0 删除 编辑

问题:
   2012年12月10日生产库出现了大量“cache buffer chain lath”等待事件,在定位问题后,通过收集对公查询结果缓存表的统计信息,问题得到解决。为了避免类似问题,在2012年12月10日晚8时,重新收集缓存表的统计信息、并锁定该统计信息,有望避免类似情况。

通过进一步分析验证,可以得到如下结论:
(1)针对挥发性表(数据量变化剧烈的表),在插入数据较少时收集统计数据,执行计划会选择全表扫描;如果在大量插入收据后,没有及时收集最新的统计数据,全表扫描可能导致整个系统性能下降,参考测试1。
(2)创建索引时会自动收集索引统计信息,而表上的统计信息并没有更新,在表数据量变化剧烈的情况下,表上的统计信息与索引的统计信息不一致也可能导致性能问题,参考测试3。

下面对相关概念验证,并给出统计信息收集策略建议。

SQL> select * from v$version;
 
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE 10.2.0.1.0 Production
TNS for Linux: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production

1,挥发性表的统计信息问题
挥发性表的统计信息很难及时更新,以反映数据的真实情况,可能导致错误的执行计划。
下面的测试,分以下三种情况收集统计信息,测试执行计划的选择:
(1)空表
(2)很少记录的表
(3)插入较多记录的表
测试结果表明,在插入数据较少时,执行计划会选择全表扫描;如果在大量插入收据后,没有及时收集最新的统计数据,全表扫描可能导致整个系统性能下降。

(1)空表
SQL>
SQL> drop table test;
 
Table dropped
SQL> create table test(x int,y char(100));
 
Table created
SQL> create index idx_test_x on test(x);
 
Index created
SQL> exec dbms_stats.gather_table_stats(user,'TEST');
 
PL/SQL procedure successfully completed
SQL> select leaf_blocks,distinct_keys,to_char(last_analyzed,'YYYYMMDDHH24MISS') from user_indexes where index_name='IDX_TEST_X';
 
LEAF_BLOCKS DISTINCT_KEYS TO_CHAR(LAST_ANALYZED,'YYYYMMD
----------- ------------- ------------------------------
          0             0 20200810143337
SQL> select num_rows,blocks,to_char(last_analyzed,'YYYYMMDDHH24MISS') from user_tables where table_name='TEST';
 
  NUM_ROWS     BLOCKS TO_CHAR(LAST_ANALYZED,'YYYYMMD
---------- ---------- ------------------------------
         0          0 20200810143337
 
Commit complete
SQL> explain plan for select * from test where x=10;
 
Explained
SQL> select * from table(dbms_xplan.display);
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 604183338
--------------------------------------------------------------------------------
| Id  | Operation                   | Name       | Rows  | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |            |     1 |   115 |     1   (0)|
|   1 |  TABLE ACCESS BY INDEX ROWID| TEST       |     1 |   115 |     1   (0)|
|*  2 |   INDEX RANGE SCAN          | IDX_TEST_X |     1 |       |     1   (0)|
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("X"=10)
 
14 rows selected
 
SQL>


(2)很少记录的表
Connected to Oracle Database 10g Enterprise Edition Release 10.2.0.1.0
Connected as bnt@DEV95
 
SQL>
SQL> drop table test;
 
Table dropped
SQL> create table test(x int,y char(100));
 
Table created
SQL> create index idx_test_x on test(x);
 
Index created
SQL> insert into test
  2  select mod(rownum,4),'a' from dba_objects where rownum<70;
 
69 rows inserted
SQL> commit;
 
Commit complete
SQL> exec dbms_stats.gather_table_stats(user,'TEST');
 
PL/SQL procedure successfully completed
SQL> select leaf_blocks,distinct_keys,to_char(last_analyzed,'YYYYMMDDHH24MISS') from user_indexes where index_name='IDX_TEST_X';
 
LEAF_BLOCKS DISTINCT_KEYS TO_CHAR(LAST_ANALYZED,'YYYYMMD
----------- ------------- ------------------------------
          1             4 20200810143359
SQL> select num_rows,blocks,to_char(last_analyzed,'YYYYMMDDHH24MISS') from user_tables where table_name='TEST';
 
  NUM_ROWS     BLOCKS TO_CHAR(LAST_ANALYZED,'YYYYMMD
---------- ---------- ------------------------------
        69          5 20200810143359
Commit complete
SQL> explain plan for select y,sum(x) from test where x=2 group by y;
 
Explained
SQL> select * from table(dbms_xplan.display);
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1435881708
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |   103 |     3  (34)| 00:00:01 |
|   1 |  HASH GROUP BY     |      |     1 |   103 |     3  (34)| 00:00:01 |
|*  2 |   TABLE ACCESS FULL| TEST |    17 |  1751 |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("X"=2)
 
14 rows selected
 
(3)插入较多记录的表
Connected to Oracle Database 10g Enterprise Edition Release 10.2.0.1.0
Connected as bnt@DEV95
 
SQL>
SQL> drop table test;
 
Table dropped
SQL> create table test(x int,y char(100));
 
Table created
SQL> create index idx_test_x on test(x);
 
Index created
SQL> insert into test
  2  select mod(rownum,4),'a' from dba_objects;
 
80139 rows inserted
SQL> commit;
 
Commit complete
SQL> exec dbms_stats.gather_table_stats(user,'TEST');
 
PL/SQL procedure successfully completed
SQL> select leaf_blocks,distinct_keys,to_char(last_analyzed,'YYYYMMDDHH24MISS') from user_indexes where index_name='IDX_TEST_X';
 
LEAF_BLOCKS DISTINCT_KEYS TO_CHAR(LAST_ANALYZED,'YYYYMMD
----------- ------------- ------------------------------
        234             4 20200810143433
SQL> select num_rows,blocks,to_char(last_analyzed,'YYYYMMDDHH24MISS') from user_tables where table_name='TEST';
 
  NUM_ROWS     BLOCKS TO_CHAR(LAST_ANALYZED,'YYYYMMD
---------- ---------- ------------------------------
     80077       1252 20200810143433
 
Commit complete
SQL> explain plan for select * from test where x=10;
 
Explained
SQL> select * from table(dbms_xplan.display);
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 604183338
--------------------------------------------------------------------------------
| Id  | Operation                   | Name       | Rows  | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |            |     1 |   103 |     2   (0)|
|   1 |  TABLE ACCESS BY INDEX ROWID| TEST       |     1 |   103 |     2   (0)|
|*  2 |   INDEX RANGE SCAN          | IDX_TEST_X |     1 |       |     1   (0)|
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("X"=10)
 
14 rows selected
 
SQL>

2,挥发性表的统计信息管理策略
对于挥发性表,可以采用收集典型数据量、锁定统计信息的方式解决。
注意事项:
(1)锁定统计信息会导致列的最大、最小值,列的数据分布锁定;而绑定变量可能随时间变化,如交易流水、日期等。需要确保相关sql不依赖于此类统计信息,因此在收集统计信息时不收集直方图。
(2)在后续针对该表的结构变更、sql查询,需要考虑统计信息锁定情况;必要时,需要重新收集统计信息并锁定。

具体操作:

begin
   dbms_stats.gather_table_stats(user,'TEST',method_opt=>'FOR ALL COLUMNS SIZE 1');
end;
/

begin
   dbms_stats.lock_table_stats(user,'TEST');
end;
/

验证:
SQL> select table_name,stattype_locked from user_tab_statistics where table_name='TEST';
 
TABLE_NAME                     STATTYPE_LOCKED
------------------------------ ---------------
TEST                           ALL


锁定后:
(1)针对该表的统计数据更新将失败;
(2)但针对多表的(GATHER_SCHEMA_STATS等)统计信息收集会跳过对该表的统计信息收集;
(3)可以lock当前或空的统计信息(以使用动态采样);
(4)该表上的DDL操作不会该表统计信息。


3,表统计信息与索引统计信息不一致问题
创建索引时会自动收集索引统计信息,并不会更新表上的统计信息。这会导致表上的统计信息与索引的统计信息不一致,在表数据量变化剧烈的情况下,会导致严重的性能问题。

测试:
(1)创建测试表
SQL> drop table test;
 
Table dropped
SQL> create table test(x int);
 
Table created
SQL> insert into test
  2  select rownum from dba_objects where rownum<10;
 
9 rows inserted
SQL> commit;
 
Commit complete
SQL> exec dbms_stats.gather_table_stats(user,'TEST');
 
PL/SQL procedure successfully completed
SQL> select num_rows,blocks,to_char(last_analyzed,'YYYYMMDDHH24MISS') from user_tables where table_name='TEST';
 
  NUM_ROWS     BLOCKS TO_CHAR(LAST_ANALYZED,'YYYYMMD
---------- ---------- ------------------------------
         9          5 20200810110053


(2)大量插入数据后创建索引
SQL>
SQL> insert into test
  2  select rownum from dba_objects;
 
80114 rows inserted
SQL> commit;
 
Commit complete
SQL> create index idx_test_x on test(x);
 
Index created


(3)表和索引统计不一致
SQL> select leaf_blocks,distinct_keys,to_char(last_analyzed,'YYYYMMDDHH24MISS') from user_indexes where index_name='IDX_TEST_X';
 
LEAF_BLOCKS DISTINCT_KEYS TO_CHAR(LAST_ANALYZED,'YYYYMMD
----------- ------------- ------------------------------
        178         80114 20200810111902
SQL> select num_rows,blocks,to_char(last_analyzed,'YYYYMMDDHH24MISS') from user_tables where table_name='TEST';
 
  NUM_ROWS     BLOCKS TO_CHAR(LAST_ANALYZED,'YYYYMMD
---------- ---------- ------------------------------
         9          5 20200810110053
 

(4)执行计划选择了全表扫描,没有选择索引
SQL> explain plan for select * from test where x=10;
 
Explained
SQL> select * from table(dbms_xplan.display);
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1357081020
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |     3 |     2   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TEST |     1 |     3 |     2   (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("X"=10)
 
13 rows selected


(5)重新收集表统计信息后,执行计划选择了索引

SQL>
SQL> exec dbms_stats.gather_table_stats(user,'TEST');
 
PL/SQL procedure successfully completed
SQL> explain plan for select * from test where x=10;
 
Explained
SQL> select * from table(dbms_xplan.display);
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 852839443
-------------------------------------------------------------------------------
| Id  | Operation        | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT |            |     1 |     4 |     1   (0)| 00:00:01 |
|*  1 |  INDEX RANGE SCAN| IDX_TEST_X |     1 |     4 |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("X"=10)
 
13 rows selected

结论:
对于变化剧烈的表,创建/重建索引时,应该重新收集表的统计信息。


4,有多少sql会被awr采样?
在故障分析中,已开始怀疑应用程序中新增SQL导致系统性能下降,理由是该sql_id没有在awr历史采样中出现过。
其实AWR数据只是采样明显消耗系统资源的sql,并且在存储到awr中时只采样部分数据。

比如,在以下系统中,当前sql area中有512个sql,而历史采样中仅372个。在繁忙系统中,仅有部分sql是大量消耗资源的,awr历史采样中保留的sql比例会更低。
SQL>
SQL> select count(distinct sql_id) from v$sql;
 
COUNT(DISTINCTSQL_ID)
---------------------
                  512
SQL> select count(distinct sql_id) from dba_hist_sqlstat;
 
COUNT(DISTINCTSQL_ID)
---------------------
                  372

5,缺省统计信息收集周期
Connected to Oracle Database 10g Enterprise Edition Release 10.2.0.5.0
Connected as bnt@U4_22.188.31.102
 
SQL>
SQL> SELECT program_name,
  2         schedule_name,
  3         schedule_type,
  4         stop_on_window_close,
  5         comments
  6    FROM dba_scheduler_jobs
  7   WHERE job_name = 'GATHER_STATS_JOB';
 
PROGRAM_NAME           SCHEDULE_NAME             SCHEDULE_TYPE STOP_ON_WINDOW_CLOSE COMMENTS
---------------------- ------------------------- ------------- -------------------- --------------------------------------------------------------------------------
GATHER_STATS_PROG      MAINTENANCE_WINDOW_GROUP  WINDOW_GROUP  TRUE                 Oracle defined automatic optimizer statistics collection job
 
SQL>
SQL> select window_group_name,enabled,number_of_windows from dba_scheduler_window_groups;
 
WINDOW_GROUP_NAME              ENABLED NUMBER_OF_WINDOWS
------------------------------ ------- -----------------
MAINTENANCE_WINDOW_GROUP       TRUE                    2
 
SQL>
SQL> select * from dba_scheduler_wingroup_members;
 
WINDOW_GROUP_NAME              WINDOW_NAME
------------------------------ ------------------------------
MAINTENANCE_WINDOW_GROUP       WEEKNIGHT_WINDOW
MAINTENANCE_WINDOW_GROUP       WEEKEND_WINDOW
 
SQL>
SQL> select window_name,repeat_interval,duration from dba_scheduler_windows;
 
WINDOW_NAME       REPEAT_INTERVAL                                                         DURATION
----------------- ----------------------------------------------------------------------- --------------
WEEKNIGHT_WINDOW  freq=daily;byday=MON,TUE,WED,THU,FRI;byhour=22;byminute=0; bysecond=0   +000 08:00:00
WEEKEND_WINDOW    freq=daily;byday=SAT;byhour=0;byminute=0;bysecond=0                     +002 00:00:00

统计数据收集时间分布如下:
    +-------06----------------------22--+  统计数据收集时间(小时)
MON:+wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwcc+         2
TUE:+ccccccccwwwwwwwwwwwwwwwwwwwwwwwwwcc+         8
WED:+ccccccccwwwwwwwwwwwwwwwwwwwwwwwwwcc+         8
THU:+ccccccccwwwwwwwwwwwwwwwwwwwwwwwwwcc+         8
FRI:+ccccccccwwwwwwwwwwwwwwwwwwwwwwwwwcc+         8
SAT:+ccccccccccccccccccccccccccccccccccc+        24
SUN:+ccccccccccccccccccccccccccccccccccc+        24
上图中:
WORKING TIME:w
STATS COLLECT TIME:c


说明:
(1)缺省的收集周期长达:2+4*8+2*24=84小时,而每周的非收集时间为24*7-84=84小时
(2)收集统计信息会在窗口结束时停止(STOP_ON_WINDOW_CLOSE=true)
(3)周六早上00:00 AM开始时间与周五窗口结束时间重叠,STOP_ON_WINDOW_CLOSE=true是否有效尚不确定
(4)周五晚22:00开始的窗口长达50小时,可用于部分大表的数据收集


 


 

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/18922393/viewspace-750908/ ,如需转载,请注明出处,否则将追究法律责任。

上一篇: logon_trigger
请登录后发表评论 登录
全部评论

注册时间:2011-05-26

  • 博文量
    211
  • 访问量
    876684