Skip to content

Database - 34. page

Oracle full table scan 的跟踪监视

最近一个客户的数据库反应不定时的性能问题,经过跟踪发现是一系列的全表扫相关SQL,其中还碰到了比较诡异的全表扫都是顺序读的情况.和周亮以前也讨论过此类情况只是当时这方面的事情并没有太多的眉目,这次跟踪发现了是和undo一致性读有关,还存在大量的row chains.当然这里不讨论这个case,这个case可能会在新书中描述.这个文章不描述具体的原理,只是作为一个对数据库全表扫相关信息收集诊断建议.

在跟踪某一特定的事件时候,有时候没有监控系统或者其他相关捕获信息的手段,则诊断起来比较耗时间,比如跟踪系统中全表扫的情况,默认情况下是只能获取当时时刻具体的情况,这里介绍通过AWR记录中来获取相关的信息,由于AWR从v$sql里面获取的sql是有条件限制的,因此该方法并不能补全数据库中所有的全表扫相关sql(其他情况也是),因为awr收集sql的情况是以statistics_level 的设置为依赖,typical(top 30)或者all(top 100)收集的top sql信息不同.

 

1.周期的全表扫信息

col c1 heading "Day|Hour" format a20
col c2 heading "Full TABLE scan|Count" format 999,999
BREAK ON c1 skip 2
BREAK ON c2 skip 2
SELECT TO_CHAR(sn.begin_interval_time,'hh24') c1,
COUNT(1) c2
FROM dba_hist_sql_plan p,
dba_hist_sqlstat s,
dba_hist_snapshot sn,
dba_segments o
WHERE p.object_owner <> 'SYS'
AND p.object_owner = o.owner
AND p.object_name = o.segment_name
AND o.blocks > 1000
AND p.operation LIKE '%TABLE ACCESS%'
AND p.options LIKE '%FULL%'
AND p.sql_id = s.sql_id
AND s.snap_id = sn.snap_id
GROUP BY TO_CHAR(sn.begin_interval_time,'hh24')
ORDER BY 1;

查询结果类似如下:

Large Full-table scans Per Snapshot Period
Begin
 Interval FTS
 time Count
 -------------------- --------
 04-10-18 11 4
 04-10-21 17 1
 04-10-21 23 2
 04-10-22 15 2
 04-10-22 16 2
 04-10-22 23 2
 04-10-24 00 2

2.每小时的全表扫信息

col c1 heading "Day|Hour" format a20
col c2 heading "Full TABLE scan|Count" format 999,999
BREAK ON c1 skip 2
BREAK ON c2 skip 2
SELECT TO_CHAR(sn.begin_interval_time,'hh24') c1,
  COUNT(1) c2
FROM dba_hist_sql_plan p,
  dba_hist_sqlstat s,
  dba_hist_snapshot sn,
  dba_segments o
WHERE  p.object_owner <> 'SYS'
AND p.object_owner                                    = o.owner
AND p.object_name                                     = o.segment_name
AND o.blocks                                          > 1000
AND p.operation LIKE '%TABLE ACCESS%'
AND p.options LIKE '%FULL%'
AND p.sql_id  = s.sql_id
AND s.snap_id = sn.snap_id
GROUP BY TO_CHAR(sn.begin_interval_time,'hh24')
ORDER BY 1;

查询结果类似如下:

Large Table Full-table scans
Averages per Hour

Day                       FTS
Hour                    Count
-------------------- --------
00                          4
10                          2
11                          4
12                         23
13                         16
14                          6
15                         17
16                         10
17                         17
18                         21
19                          1
23                          6

3.基于星期的全表扫信息

col c1 heading "Week|Day" format a20
col c2 heading "Full TABLE scan|Count" format 999,999
BREAK ON c1 skip 2
BREAK ON c2 skip 2
SELECT TO_CHAR(sn.begin_interval_time,'day') c1,
  COUNT(1) c2
FROM dba_hist_sql_plan p,
  dba_hist_sqlstat s,
  dba_hist_snapshot sn,
  dba_segments o
WHERE  p.object_owner <> 'SYS'
AND p.object_owner                                    = o.owner
AND p.object_name                                     = o.segment_name
AND o.blocks                                          > 1000
AND p.operation LIKE '%TABLE ACCESS%'
AND p.options LIKE '%FULL%'
AND p.sql_id  = s.sql_id
AND s.snap_id = sn.snap_id
GROUP BY TO_CHAR(sn.begin_interval_time,'day')
ORDER BY 1;

查询结果类似如下:

Week                      FTS
Day                     Count
-------------------- --------
sunday                      2
monday                     19
tuesday                    31
wednesday                  34
thursday                   27
friday                     15
Saturday                    2

以上脚本作者为Donald K. Burleson.
我对此脚本做了些变更,除了生成html格式外额外加入索引类的信息,变更的脚本未在blog上发布.

使用MDATA恢复Oracle中truncate的数据

创建测试表并模拟truncate的场景:

SQL> create table trutab as select * from t1;

Table created.

SQL> select count(*) from trutab;

  COUNT(*)
----------
     10000

SQL> commit
  2  ;

Commit complete.

SQL>
SQL>
SQL> truncate table trutab;

Table truncated.

SQL> select count(*) from trutab;

  COUNT(*)
----------
         0

SQL> select segment_name,tablespace_name from dba_segments where segment_name='TRUTAB' and owner='LUDA';

SEGMENT_NAME                             TABLESPACE_NAME
---------------------------------------- ----------------------------------------
TRUTAB                                   USERS

加载mdata

MDATA>reload dict
Start reload dict,Mon Jan 12 13:46:45 CST 2015
load BOOTSTRAP$ success.
load TAB$ success.
load COL$ success.
load OBJ$ success.
load USER$ success.
load PROPS$ success.
load TABPART$ success.
load TABSUBPART$ success.
load IND$ success.
End reload dict,Mon Jan 12 13:46:51 CST 2015,reload success.
MDATA>list table luda
OWNER                         TABLE
---------------               --------------
LUDA                          T2
LUDA                          T1
LUDA                          TRUTAB
owner:LUDA has 3  rows selected.

通过scan的方式扫描被truncate的表所在的tablespace

MDATA>unload table luda.trutab
unload schema:LUDA;tab:TRUTAB
tabName:LUDA.TRUTAB,dic_obj:com.olm.b.H@f0ca71,getFileid:4,getBlockid:195
0 rows unloaded
MDATA>scan extent tablespace 4 datafile 4
scan extent start: Mon Jan 12 13:48:14 CST 2015
scanning extent...
scanning extent finished.
scan extent completed: Mon Jan 12 13:48:15 CST 2015
MDATA>unload table luda.trutab
unload schema:LUDA;tab:TRUTAB
tabName:LUDA.TRUTAB,dic_obj:com.olm.b.H@26e5d8,getFileid:4,getBlockid:195
0 rows unloaded
MDATA>scan extent tablespace 4 datafile 4 auto
scan extent [tablespace <ts #> [datafile <rfile #>] ] [object <data_object_data>] parallel [parallel_degree]

scan做操完成后,对该表执行unload操作

MDATA>unload table luda.trutab object auto
Auto mode truncated table.
unload schema:LUDA;tab:TRUTAB
tabName:LUDA.TRUTAB,dic_obj:com.olm.b.H@cd1365,getFileid:4,getBlockid:195
10000 rows unloaded
MDATA>

导出恢复的数据后,通过sqlload工具将导出的数据恢复到trutab表中:

[oracle@DB01 data]$ sqlldr luda/luda control=LUDA.TRUTAB.ctl

SQL*Loader: Release 10.2.0.4.0 - Production on Mon Jan 12 13:52:44 2015

Copyright (c) 1982, 2007, Oracle.  All rights reserved.

Commit point reached - logical record count 2501
Commit point reached - logical record count 5002
Commit point reached - logical record count 7503
Commit point reached - logical record count 10000

恢复完成后验证恢复:

[oracle@DB01 data]$ sqlplus '/as sysdba'

SQL*Plus: Release 10.2.0.4.0 - Production on Mon Jan 12 13:52:54 2015

Copyright (c) 1982, 2007, Oracle.  All Rights Reserved.


Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> select count(*) from luda.trutab;

  COUNT(*)
----------
     10000

至此truncate的恢复已经完成。提示一个小技巧,mdata的scan功能支持并行模式

使用MDATA恢复Oracle误删除(deleted)的数据

构建测试的环境并模拟删除数据:

SQL> create user luda identified by luda default tablespace users;

User created.

SQL> grant dba to luda;

Grant succeeded.

SQL> alter system checkpoint;

System altered.

SQL>
SQL> conn luda/luda
Connected.
SQL> create table t1 as select * from dba_objects where rownum < 10001;

Table created.

SQL> create table t2 as select object_id,object_name from dba_objects where rownum < 100;

Table created.

SQL> commit;

Commit complete.

SQL> alter system checkpoint;

System altered.

#rows before delete from luda.t2
SQL> select count(*) from t2;

  COUNT(*)
----------
        99

#delete 90rows from luda

SQL> delete from luda.t2 where rownum < 91;

90 rows deleted.

SQL> commit;

Commit complete.

SQL> select count(*) from luda.t2;

  COUNT(*)
----------
         9

SQL> alter system checkpoint;

System altered.

加载mdata

#using mdata unload data that have deleted from luda.t2
MDATA>reload dict
Start reload dict,Sun Jan 11 00:01:50 CST 2015
SQLException: State:   X0X95 Severity: 30000
Operation 'TRUNCATE TABLE' cannot be performed on object 'IDUL_OBJ' because there is an open ResultSet dependent on that object.
load BOOTSTRAP$ success.
load TAB$ success.
load COL$ success.
load OBJ$ success.
load USER$ success.
load PROPS$ success.
load TABPART$ success.
load TABSUBPART$ success.
load IND$ success.
End reload dict,Sun Jan 11 00:01:56 CST 2015,reload success.

通过unload的功能恢复出delete数据

MDATA>unload table luda.t2 only deleted data
load only deleted data,schema:LUDA;tab:T2
tabName:LUDA.T2,dic_obj:com.olm.b.H@2d6636,getFileid:4,getBlockid:187
90 rows unloaded

退出mdata切换到恢复出数据的目录,这时候将恢复出的数据使用sqlloadl恢复到表中

#then u can find the unload data at /oracle/mdata/data
[root@DB01 data]# ls -lFrt
-rw-r--r-- 1 oracle oinstall    1042 Jan 11 00:02 LUDA.T2.dat
-rw-r--r-- 1 oracle oinstall     204 Jan 11 00:02 LUDA.T2.ctl
...

这里介绍2个delete的功能
第一种是unload表数据时候包含被删除数据

using mdata unload luda.t2's data contrain deleted data
MDATA>unload table luda.t2 contain deleted data
unload schema:LUDA;tab:T2 contain deleted data
tabName:LUDA.T2,dic_obj:com.olm.b.H@a0b1e1,getFileid:4,getBlockid:187
99 rows unloaded

第二种是unload表数据库时候不包含被删除数据

# using mdata unload luda.t2's data is not contrain deleted data
MDATA>unload table luda.t2
unload schema:LUDA;tab:T2
tabName:LUDA.T2,dic_obj:com.olm.b.H@1c76b8d,getFileid:4,getBlockid:187
9 rows unloaded