共计 4229 个字符,预计需要花费 11 分钟才能阅读完成。
家喻户晓,TRUNCATE TABLE 是一种疾速清空表内数据的一种形式,与 delete 形式不同,truncate 只产生非常少的 redo 和 undo,就实现了清空表数据并升高表 HWM 的性能。本文次要围绕 TRUNCATE TABLE 的实现原理和 TRUNCATE TABLE 的复原来开展。
首先结构测试环境,并通过 10046 以及 redo dump 去剖析 truncate 的整个操作过程。其中 10046 用于察看 truncate 对于字典基表的操作;redo dump 用于察看 truncate 对于 segment header 以及 L1、L2 位图块的操作。
OS: redhat 6.5
db:11.2.0.4
基于 assm
segment&extent info:
SYS@:>select owner,segment_name,header_file,header_block from dba_segments where segment_name=’TRUNCATE_TABLE’ and owner=’TEST’;
OWNER SEGMENT_NAME HEADER_FILE HEADER_BLOCK
TEST TRUNCATE_TABLE 5 1898
SYS@:>select extent_id,file_id,block_id,blocks from dba_extents where segment_name=’TRUNCATE_TABLE’ and owner=’TEST’ order by 1;
EXTENT_ID FILE_ID BLOCK_ID BLOCKS
0 5 1896 8
1 5 12104 8
2 5 12112 8
3 5 12120 8
4 5 12128 8
5 5 12136 8
6 5 12144 8
7 5 12152 8
8 5 11904 8
9 5 11912 8
10 5 11920 8
11 5 11928 8
12 5 11936 8
13 5 11944 8
14 5 11952 8
15 5 11960 8
16 5 16256 128
17 5 16384 128
18 5 16512 128
19 5 16768 128
20 5 22528 128
21 5 22656 128
22 5 22784 128
23 5 22912 128
24 5 23040 128
25 5 23168 128
26 5 23296 128
27 5 23424 128
通过 10046 和 redo dump 去察看 truncate 操作:
SYS@TEST(test):1>select count(*) from test.truncate_table;
COUNT(*)
113426
SYS@:>alter system flush SHARED_POOL;
System altered.
SYS@:>alter system flush BUFFER_CACHE;
System altered.
SYS@:>alter system switch logfile;
System altered.
SYS@:>select * from v$log;
GROUP# THREAD# SEQUENCE# BYTES BLOCKSIZE MEMBERS ARC STATUS FIRST_CHANGE# FIRST_TIME NEXT_CHANGE# NEXT_TIME
1 1 85 52428800 512 1 NO CURRENT 4116465 21-APR-18 2.8147E+14
2 1 83 52428800 512 1 NO INACTIVE 4092314 20-APR-18 4116301 21-APR-18
3 1 84 52428800 512 1 NO INACTIVE 4116301 21-APR-18 4116465 21-APR-18
SYS@:>oradebug setmypid;
Statement processed.
SYS@:>oradebug tracefile_name
/u01/app/oracle/diag/rdbms/test/test/trace/test_ora_7091.trc
SYS@:>oradebug event 10046 trace name context forever,level 12;
Statement processed.
SYS@:>truncate table test.truncate_table;
Table truncated.
SYS@:>oradebug event 10046 trace name context off;
Statement processed.
SYS@TEST(test):1>alter system dump logfile ‘/u01/app/oracle/oradata/test/redo01.log’;
System altered.
从 10046 trace 里搜出对基表的 dml 操作:
update:
[root@prim1-11g ~]# grep -i “^update” /u01/app/oracle/diag/rdbms/test/test/trace/test_ora_7091.trc
update obj$ set obj#=:4, type#=:5,ctime=:6,mtime=:7,stime=:8,status=:9,dataobj#=:10,flags=:11,oid$=:12,spare1=:13, spare2=:14 where owner#=:1 and name=:2 and namespace=:3 and remoteowner is null and linkname is null and subname is null
update sys.mon_mods$ set inserts = inserts + :ins, updates = updates + :upd, deletes = deletes + :del, flags = (decode(bitand(flags, :flag), :flag, flags, flags + :flag)), drop_segments = drop_segments + :dropseg, timestamp = :time where obj# = :objn
update tab$ set ts#=:2,file#=:3,block#=:4,bobj#=decode(:5,0,null,:5),tab#=decode(:6,0,null,:6),intcols=:7,kernelcols=:8,clucols=decode(:9,0,null,:9),audit$=:10,flags=:11,pctfree$=:12,pctused$=:13,initrans=:14,maxtrans=:15,rowcnt=:16,blkcnt=:17,empcnt=:18,avgspc=:19,chncnt=:20,avgrln=:21,analyzetime=:22,samplesize=:23,cols=:24,property=:25,degree=decode(:26,1,null,:26),instances=decode(:27,1,null,:27),dataobj#=:28,avgspc_flb=:29,flbcnt=:30,trigflag=:31,spare1=:32,spare2=decode(:33,0,null,:33),spare4=:34,spare6=:35 where obj#=:1
update seg$ set type#=:4,blocks=:5,extents=:6,minexts=:7,maxexts=:8,extsize=:9,extpct=:10,user#=:11,iniexts=:12,lists=decode(:13, 65535, NULL, :13),groups=decode(:14, 65535, NULL, :14), cachehint=:15, hwmincr=:16, spare1=DECODE(:17,0,NULL,:17),scanhint=:18, bitmapranges=:19 where ts#=:1 and file#=:2 and block#=:3
delete:
[root@prim1-11g ~]# grep -i “^delete” /u01/app/oracle/diag/rdbms/test/test/trace/test_ora_7091.trc
delete from superobj$ where subobj# = :1
delete from tab_stats$ where obj#=:1
对基表的批改次要是:
批改 obj$,tab$ 的 dataobj#
批改 seg$ 的对应信息如(extents,blocks,hwmincr 等等)
删除 tab_stats$ 对应对象的统计信息
对于 segment header 以及 L1、L2 位图块的操作,只能通过 redo dump 去察看,因为在 logminer 中只会记录数据块的变更,而对于 segment header 和 L1、L2 位图块的操作在 logminer 里只记录操作类型为 internal 或者 unsupported,没有什么有价值的信息。
通过对 redo dump 的剖析,发现 truncate 操作只对 segment header,L2 位图块,第一个 L1 位图块和 HWM block 所属的 L1 位图块进行了批改。
对于 segment header:
批改块的 dataobj#
批改 LHWM 和 HHWM
批改 extent map、aux map 以及 extents 个数
对于 L2 位图块:
删除 L1 ranges
批改 L2 块的 dataobj#
对于第一个 L1 位图块:
批改第一个 L1 块的 dataobj#
set hwm 为 ext# 为 0 的第 3 + 1 个块(即段头块 +1)
对于 HWM block 所属的 L1 位图块:
clear HWM flag
truncate 的本质是在不批改数据块的状况下,通过批改 segment header 的 data_object_id、hwm、extent map、aux map 等信息来实现清空表的目标,其中还波及数据字典基表以及 L1、L2 位图块的批改,所以说 truncate 操作只是存储数据的数据块没有产生任何 redo 和 undo,然而 segment header、位图块、数据字典基表还是会产生 redo 和 undo。
对于作者
李翔宇,云和恩墨西区交付技术顾问,长期服务挪动运营商行业客户,相熟 Oracle 性能优化,故障诊断,非凡复原。
往年的数据技术嘉年华大会上,李翔宇老师将带来题为《在通过案例深刻解析 Oracle 外部原理》的演讲,与大家一起摸索 CBO 和 ASM rebalance 的一些外部机制,精彩不容错过!