create or replace procedure delBigTab ( p_TableName in varchar2, p_Condition in varchar2, p_Count in varchar2 ) as pragma autonomous_transaction; n_delete number:=0; begin while 1=1 loop EXECUTE IMMEDIATE 'delete from '||p_TableName||' where '||p_Condition||' and rownum <= :rn' USING p_Count; if SQL%NOTFOUND then exit; else n_delete:=n_delete + SQL%ROWCOUNT; end if; commit; end loop; commit; DBMS_OUTPUT.PUT_LINE('Finished!'); DBMS_OUTPUT.PUT_LINE('Totally '||to_char(n_delete)||' records deleted!'); end;以下是删除过程及时间:
SQL> create or replace procedure delBigTab 2 ( 3 p_TableName in varchar2, 4 p_Condition in varchar2, 5 p_Count in varchar2 6 ) 7 as 8 pragma autonomous_transaction; 9 n_delete number:=0; 10 begin 11 while 1=1 loop 12 EXECUTE IMMEDIATE 13 'delete from '||p_TableName||' where '||p_Condition||' and rownum <= :rn' 14 USING p_Count; 15 if SQL%NOTFOUND then 16 exit; 17 else 18 n_delete:=n_delete + SQL%ROWCOUNT; 19 end if; 20 commit; 21 end loop; 22 commit; 23 DBMS_OUTPUT.PUT_LINE('Finished!'); 24 DBMS_OUTPUT.PUT_LINE('Totally '||to_char(n_delete)||' records deleted!'); 25 end; 26 / Procedure created. SQL> set timing on SQL> select min(NUMDLFLOGGUID) from HS_DLF_DOWNLOG_HISTORY; MIN(NUMDLFLOGGUID) ------------------ 11000000 Elapsed: 00:00:00.23 SQL> exec delBigTab('HS_DLF_DOWNLOG_HISTORY','NUMDLFLOGGUID < 11100000','10000'); PL/SQL procedure successfully completed. Elapsed: 00:00:18.54 SQL> select min(NUMDLFLOGGUID) from HS_DLF_DOWNLOG_HISTORY; MIN(NUMDLFLOGGUID) ------------------ 11100000 Elapsed: 00:00:00.18 SQL> set serveroutput on SQL> exec delBigTab('HS_DLF_DOWNLOG_HISTORY','NUMDLFLOGGUID < 11200000','10000'); Finished! Totally 96936 records deleted! PL/SQL procedure successfully completed. Elapsed: 00:00:18.61 10万记录大约19s SQL> exec delBigTab('HS_DLF_DOWNLOG_HISTORY','NUMDLFLOGGUID < 11300000','10000'); Finished! Totally 100000 records deleted! PL/SQL procedure successfully completed. Elapsed: 00:00:18.62 SQL> exec delBigTab('HS_DLF_DOWNLOG_HISTORY','NUMDLFLOGGUID < 11400000','10000'); Finished! Totally 100000 records deleted! PL/SQL procedure successfully completed. Elapsed: 00:00:18.85 SQL> SQL> exec delBigTab('HS_DLF_DOWNLOG_HISTORY','NUMDLFLOGGUID < 13000000','10000'); Finished! Totally 1000000 records deleted! PL/SQL procedure successfully completed. Elapsed: 00:03:13.87 100万记录大约3分钟 SQL> exec delBigTab('HS_DLF_DOWNLOG_HISTORY','NUMDLFLOGGUID < 20000000','10000'); Finished! Totally 6999977 records deleted! PL/SQL procedure successfully completed. Elapsed: 00:27:24.69 700万大约27分钟 以上过程仅供参考.
执行drop table 报错:ORA-00054: 资源正忙,要求指定 NOWAIT 解决: 通过v$session,v$locked_object查看谁锁定了资源 select t1.sid, t1.serial#, t1.username, t1.logon_time from v$session t1 , v$locked_object t2 where t1.sid = t2.session_id order by t1.logon_time 通过alter system kill session 'sid, serial#'把session kill掉 alter system kill session '10,34' |
今天在外地升级一个系统,在执行一个存储过程的时候,由于时间较长就把它中止了。
然后考虑增加索引,但是增加的时候提示:ORA-00054: 资源正忙,要求指定 NOWAIT
在晚上找资料,发现是由于刚才中止过程的时候,由于有表还没有解除锁定,造成无法处理增加索引的请求。
方法如下:
1、select status, sid , serial# from v$session; --查询所有session
--如果发现有session是inactive状态,则KILL之
alter system kill session '
2、采用重启服务亦可完成。
标签: oracle数据库阻塞结果集进行死锁it |
分类: ora错误分析 |
问题如下:
SQL> conn scott/tiger@vm_database Connected to Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 Connected as scott
。。。。。。
SQL> drop table student2; drop table student2 ORA-00054: 资源正忙, 但指定以 NOWAIT 方式获取资源, 或者超时失效 =========================================================
解决方法如下:
=========================================================
SQL> select session_id from v$locked_object; SESSION_ID ---------- 142
SQL> SELECT sid, serial#, username, osuser FROM v$session where sid = 142; SID SERIAL# USERNAME OSUSER ---------- ---------- ------------------------------ ------------------------------ 142 38 SCOTT LILWEN
SQL> ALTER SYSTEM KILL SESSION '142,38'; System altered SQL> conn scott/tiger@vm_database Connected to Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 Connected as scott SQL> drop table student2; Table dropped
Oracle数据库的锁类型 根据保护的对象不同,Oracle数据库锁可以分为以下几大类:DML锁(data locks,数据锁),用于保护数据的完整性;DDL锁(dictionary locks,字典锁),用于保护数据库对象的结构,如表、索引等的结构定义;内部锁和闩(internal locks and latches),保护数据库的内部结构。 DML锁的目的在于保证并发情况下的数据完整性,。在Oracle数据库中,DML锁主要包括TM锁和TX锁,其中TM锁称为表级锁,TX锁称为事务锁或行级锁。 当Oracle 执行DML语句时,系统自动在所要操作的表上申请TM类型的锁。当TM锁获得后,系统再自动申请TX类型的锁,并将实际锁定的数据行的锁标志位进行置位。 这样在事务加锁前检查TX锁相容性时就不用再逐行检查锁标志,而只需检查TM锁模式的相容性即可,大大提高了系统的效率。TM锁包括了SS、SX、S、X 等多种模式,在数据库中用0-6来表示。不同的SQL操作产生不同类型的TM锁。 在数据行上只有X锁(排他锁)。在 Oracle数据库中,当一个事务首次发起一个DML语句时就获得一个TX锁,该锁保持到事务被提交或回滚。当两个或多个会话在表的同一条记录上执行 DML语句时,第一个会话在该条记录上加锁,其他的会话处于等待状态。当第一个会话提交后,TX锁被释放,其他会话才可以加锁。 当Oracle数据库发生TX锁等待时,如果不及时处理常常会引起Oracle数据库挂起,或导致死锁的发生,产生ORA-60的错误。这些现象都会对实际应用产生极大的危害,如长时间未响应,大量事务失败等。 悲观封锁和乐观封锁 一、悲观封锁 锁在用户修改之前就发挥作用: Select ..for update(nowait) Select * from tab1 for update 用户发出这条命令之后,oracle将会对返回集中的数据建立行级封锁,以防止其他用户的修改。 如果此时其他用户对上面返回结果集的数据进行dml或ddl操作都会返回一个错误信息或发生阻塞。 1:对返回结果集进行update或delete操作会发生阻塞。 2:对该表进行ddl操作将会报: Ora-00054:resource busy and acquire with nowait specified. 原因分析 此时Oracle已经对返回的结果集上加了排它的行级锁,所有其他对这些数据进行的修改或删除操作都必须等待这个锁的释放,产生的外在现象就是其他的操作将发生阻塞,这个这个操作commit或rollback. 同样这个查询的事务将会对该表加表级锁,不允许对该表的任何ddl操作,否则将会报出 ora-00054错误::resource busy and acquire with nowait specified. 二、乐观封锁 乐观的认为数据在select出来到update进取并提交的这段时间数据不会被更改。这里面有一种潜在的危险就是由于被选出的结果集并没有被锁定,是存在一种可能被其他用户更改的可能。因此Oracle仍然建议是用悲观封锁,因为这样会更安全。 阻塞 定义: 当一个会话保持另一个会话正在请求的资源上的锁定时,就会发生阻塞。被阻塞的会话将一直挂起,直到持有锁的会话放弃锁定的资源为止。4个常见的dml语句会产生阻塞 INSERT UPDATE DELETE SELECT…FOR UPDATE INSERT Insert发生阻塞的唯一情况就是用户拥有一个建有主键约束的表。当2个的会话同时试图向表中插入相同的数据时,其中的一个会话将被阻塞,直到另外一个会话提交或会滚。一个会话提交时,另一个会话将收到主键重复的错误。回滚时,被阻塞的会话将继续执行。 UPDATE 和DELETE当执行Update和delete操作的数据行已经被另外的会话锁定时,将会发生阻塞,直到另一个会话提交或会滚。 Select …for update 当一个用户发出select..for update的错作准备对返回的结果集进行修改时,如果结果集已经被另一个会话锁定,就是发生阻塞。需要等另一个会话结束之后才可继续执行。可以通过发出 select… for update nowait的语句来避免发生阻塞,如果资源已经被另一个会话锁定,则会返回以下错误: Ora-00054:resource busy and acquire with nowait specified. 死锁-deadlock 定义:当两个用户希望持有对方的资源时就会发生死锁. 即两个用户互相等待对方释放资源时,oracle认定为产生了死锁,在这种情况下,将以牺牲一个用户作为代价,另一个用户继续执行,牺牲的用户的事务将回滚. 例子: 1:用户1对A表进行Update,没有提交。 2:用户2对B表进行Update,没有提交。 此时双反不存在资源共享的问题。 3:如果用户2此时对A表作update,则会发生阻塞,需要等到用户一的事物结束。 4:如果此时用户1又对B表作update,则产生死锁。此时Oracle会选择其中一个用户进行会滚,使另一个用户继续执行操作。 起因: Oracle的死锁问题实际上很少见,如果发生,基本上都是不正确的程序设计造成的,经过调整后,基本上都会避免死锁的发生。 DML锁分类表 表1 Oracle的TM锁类型 锁模式 锁描述 解释 SQL操作 0 none 1 NULL 空 Select 2 SS(Row-S) 行级共享锁,其他对象只能查询这些数据行 Select for update、Lock for update、Lock row share 3 SX(Row-X) 行级排它锁,在提交前不允许做DML操作 Insert、Update、Delete、Lock row share 4 S(Share) 共享锁 Create index、Lock share 5 SSX(S/Row-X) 共享行级排它锁 Lock share row exclusive 6 X(Exclusive) 排它锁 Alter table、Drop able、Drop index、Truncate table 、Lock exclusive 1.关于V$lock表和相关视图的说明 Column Datatype Description ADDR RAW(4 | 8) Address of lock state object KADDR RAW(4 | 8) Address of lock SID NUMBER Identifier for session holding or acquiring the lock TYPE VARCHAR2(2) Type of user or system lock The locks on the user types are obtained by user applications. Any process that is blocking others is likely to be holding one of these locks. The user type locks are: TM - DML enqueue TX - Transaction enqueue UL - User supplied --我们主要关注TX和TM两种类型的锁 --UL锁用户自己定义的,一般很少会定义,基本不用关注 --其它均为系统锁,会很快自动释放,不用关注 ID1 NUMBER Lock identifier #1 (depends on type) ID2 NUMBER Lock identifier #2 (depends on type) ---当lock type 为TM时,id1为DML-locked object的object_id ---当lock type 为TX时,id1为usn+slot,而id2为seq。 --当lock type为其它时,不用关注 LMODE NUMBER Lock mode in which the session holds the lock: 0 - none 1 - null (NULL) 2 - row-S (SS) 3 - row-X (SX) 4 - share (S) 5 - S/Row-X (SSX) 6 - exclusive (X) --大于0时表示当前会话以某种模式占有该锁,等于0时表示当前会话正在等待该锁资源,即表示该会话被阻塞。 --往往在发生TX锁时,伴随着TM锁,比如一个sid=9会话拥有一个TM锁,一般会拥有一个或几个TX锁,但他们的id1和id2是不同的,请注意 REQUEST NUMBER Lock mode in which the process requests the lock: 0 - none 1 - null (NULL) 2 - row-S (SS) 3 - row-X (SX) 4 - share (S) 5 - S/Row-X (SSX) 6 - exclusive (X) --大于0时,表示当前会话被阻塞,其它会话占有改锁的模式 CTIME NUMBER Time since current mode was granted BLOCK NUMBER The lock is blocking another lock 0, 'Not Blocking', 1, 'Blocking', 2, 'Global', --该锁是否阻塞了另外一个锁 2.其它相关视图说明 视图名 描述 主要字段说明 v$session 查询会话的信息和锁的信息。 sid,serial#:表示会话信息。 program:表示会话的应用程序信息。 row_wait_obj#:表示等待的对象,和dba_objects中的object_id相对应。 lockwait :该会话等待的锁的地址,与v$lock的kaddr对应. v$session_wait 查询等待的会话信息。 sid:表示持有锁的会话信息。 Seconds_in_wait:表示等待持续的时间信息 Event:表示会话等待的事件,锁等于enqueue dba_locks 对v$lock的格式化视图。 Session_id:和v$lock中的Sid对应。 Lock_type:和v$lock中的type对应。 Lock_ID1: 和v$lock中的ID1对应。 Mode_held,mode_requested:和v$lock中 的lmode,request相对应。 v$locked_object 只包含DML的锁信息,包括回滚段和会话信息。 Xidusn,xidslot,xidsqn:表示回滚段信息。和 v$transaction相关联。 Object_id:表示被锁对象标识。 Session_id:表示持有锁的会话信息。 Locked_mode:表示会话等待的锁模式的信 息,和v$lock中的lmode一致。 1.查询数据库中的锁 select * from v$lock; select * from v$lock where block=1; 2.查询被锁的对象 select * from v$locked_object; 3.查询阻塞 查被阻塞的会话 select * from v$lock where lmode=0 and type in ('TM','TX'); 查阻塞别的会话锁 select * from v$lock where lmode>0 and type in ('TM','TX'); 4.查询数据库正在等待锁的进程 select * from v$session where lockwait is not null; 5.查询会话之间锁等待的关系 select a.sid holdsid,b.sid waitsid,a.type,a.id1,a.id2,a.ctime from v$lock a,v$lock b where a.id1=b.id1 and a.id2=b.id2 and a.block=1 and b.block=0; 6.查询锁等待事件 select * from v$session_wait where event='enqueue'; 解决方案: select session_id from v$locked_object; --首先得到被锁对象的session_id SELECT sid, serial#, username, osuser FROM v$session where sid = session_id; --通过上面得到的session_id去取得v$session的sid和serial#,然后对该进程进行终止。 ALTER SYSTEM KILL SESSION 'sid,serial'; example: ALTER SYSTEM KILL SESSION '13, 8'; 本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/owen_008/archive/2009/09/05/4522158.aspxhttp://blog.csdn.net/lopper/article/details/5911306
http://blog.sina.com.cn/s/blog_54eeb5d90100ntgc.html
[原]Oracle删除大表并回收空间的过程
近日在查询某项日志的时候,发现查询非常缓慢,根据以往的经验这是由于某个日志表过大引起的,为了加快查询,决定将大部分的历史数据迁移到另外一个表中,本文主要记录删除这个大表的过程,就解决问题而言还有很多方法,但是本文侧重点在于如何处理大数据量删除的操作,及其善后工作。
首先看看我们要做数据迁移的表所占的空间:
SQL> select segment_name , bytes/1048576 MB 2 from user_segments 3 order by bytes 4 / SEGMENT_NAME MB -------------------------------------------------- ---------- TAB_NODE_PARAM .375 SYS_LOB0000053047C00004$$ .4375 PK_TAB_FLOW_HIS 1 PK_TAB_NODE_HIS 2 TAB_TASK_HIS 2 TAB_FLOW_HIS 4 TAB_NODE_HIS 17 PK_TAB_APPEXCHANGE_LOG 152 TAB_APPEXCHANGE_LOG 768看看历史数据大概所占的空间比例:
SQL> select count(*) from tab_appexchange_log 2 where receive_time >= to_date('2009-01-01','yyyy-mm-dd') 3 union all 4 select count(*) from tab_appexchange_log 5 where receive_time < to_date('2009-01-01','yyyy-mm-dd') SQL> / COUNT(*) ---------- 1584298 4037710从结果来看,的确存在很多历史数据,我们将 2009 年之前的数据迁移到别的表中。
SQL> create table tab_appexchange_log_20090101 2 as 3 select * from tab_appexchange_log 4 where receive_time < to_date('2009-01-01','yyyy-mm-dd') SQL> / Table created. SQL> select count(*) from tab_appexchange_log_20090101 2 / COUNT(*) ---------- 4037710数据已经迁移一份到新的表里面了,可以删除原表中的旧数据了。
SQL> delete tab_appexchange_log where receive_time < to_date('2009-01-01','yyyy-mm-dd') 2 / delete tab_appexchange_log where receive_time < to_date('2009-01-01','yyyy-mm-dd') * ERROR at line 1: ORA-30036: unable to extend segment by 8 in undo tablespace 'UNDOTBS2'这里出现了一个 ORA-30036的错误,大概意思是undo表空间不足,看来是要删除的数据太多了,于是考虑化整为零,删一点,commit一下。
SQL> delete tab_appexchange_log where receive_time < to_date('2007-01-01','yyyy-mm-dd') 2 / 0 rows deleted. SQL> delete tab_appexchange_log where receive_time < to_date('2008-01-01','yyyy-mm-dd') 2 / 1586227 rows deleted. SQL> commit; Commit complete. SQL> delete tab_appexchange_log where receive_time < to_date('2008-06-01','yyyy-mm-dd') 2 / 813019 rows deleted. SQL> commit; Commit complete. SQL> delete tab_appexchange_log where receive_time < to_date('2009-01-01','yyyy-mm-dd') 2 / 1638464 rows deleted. SQL> commit; Commit complete.可以预知,现在表里面的数据应该只有 15***** 条,以下证实了这个想法。
SQL> select count(*) from tab_appexchange_log 2 / COUNT(*) ---------- 1584522我们再看看表占用空间的情况:
SQL> select segment_name , bytes/1048576 MB 2 from user_segments 3 order by bytes asc; SEGMENT_NAME MB -------------------------------------------------- ---------- TAB_NODE_PARAM .375 SYS_LOB0000053047C00004$$ .4375 PK_TAB_FLOW_HIS 1 PK_TAB_NODE_HIS 2 TAB_TASK_HIS 2 TAB_FLOW_HIS 4 TAB_NODE_HIS 17 PK_TAB_APPEXCHANGE_LOG 152 TAB_APPEXCHANGE_LOG_20090101 552 TAB_APPEXCHANGE_LOG 768看来原表所占的空间还没有释放,这个涉及一个HWM的说法,还有可以观察到在TAB_APPEXCHANGE_LOG上面的一个索引 PK_TAB_APPEXCHANGE_LOG 空间没有回收。
我们回过头来看看刚才的操作对undo表空间产生的压力:
TO_CHAR(BEGIN_TIME, TO_CHAR(END_TIME,'Y UNDOBLKS ------------------- ------------------- ---------- 2009-06-30 13:39:32 2009-06-30 13:46:35 96164 2009-06-30 13:29:32 2009-06-30 13:39:32 89316 2009-06-30 13:19:32 2009-06-30 13:29:32 209885 2009-06-30 13:09:32 2009-06-30 13:19:32 105 2009-06-30 12:59:32 2009-06-30 13:09:32 125 2009-06-30 12:49:32 2009-06-30 12:59:32 74 2009-06-30 12:39:32 2009-06-30 12:49:32 80 2009-06-30 12:29:32 2009-06-30 12:39:32 72 2009-06-30 12:19:32 2009-06-30 12:29:32 76 2009-06-30 12:09:32 2009-06-30 12:19:32 89 2009-06-30 11:59:32 2009-06-30 12:09:32 126呵呵,刚才DML的操作对undo表空间产生的压力还是非常可观的。
我们开始回收表所占的空间:
SQL> alter table TAB_APPEXCHANGE_LOG enable row movement; Table altered. SQL> alter table TAB_APPEXCHANGE_LOG shrink space; Table altered.
由于这么大规模的删除,会导致很多索引中存在很多标记为删除的节点,占地方还不能发挥作用,因此重建索引将这些无用节点清理出去才是上策。
SQL> analyze index PK_TAB_APPEXCHANGE_LOG validate structure; Index analyzed. SQL> select height, name , LF_ROWS_LEN,LF_ROWS,DEL_LF_ROWS,DEL_LF_ROWS_LEN from index_stats; HEIGHT NAME LF_ROWS_LEN LF_ROWS DEL_LF_ROWS DEL_LF_ROWS_LEN ---------- ------------------------------ ----------- ---------- ----------- --------------- 3 PK_TAB_APPEXCHANGE_LOG 88905533 5621608 4036429 63558680 SQL> alter index PK_TAB_APPEXCHANGE_LOG rebuild online; Index altered. SQL> analyze index PK_TAB_APPEXCHANGE_LOG validate structure; Index analyzed. SQL> select height, name , LF_ROWS_LEN,LF_ROWS,DEL_LF_ROWS,DEL_LF_ROWS_LEN from index_stats; HEIGHT NAME LF_ROWS_LEN LF_ROWS DEL_LF_ROWS DEL_LF_ROWS_LEN ---------- ------------------------------ ----------- ---------- ----------- --------------- 3 PK_TAB_APPEXCHANGE_LOG 25347109 1585195 0 0如果现在我们再查一下索引所占的空间的话,会发现所占空间是没有减少的,和表一样,同样也是需要收缩一下空间。
SQL> alter index PK_TAB_APPEXCHANGE_LOG shrink space; Index altered. SQL> select segment_name , bytes/1048576 MB 2 from user_segments 3 order by bytes 4 / SEGMENT_NAME MB -------------------------------------------------- ---------- TAB_NODE_PARAM .375 SYS_LOB0000053047C00004$$ .4375 PK_TAB_FLOW_HIS 1 TAB_TASK_HIS 2 PK_TAB_NODE_HIS 2 TAB_FLOW_HIS 4 TAB_NODE_HIS 17 PK_TAB_APPEXCHANGE_LOG 28.1875 TAB_APPEXCHANGE_LOG 220.8125 TAB_APPEXCHANGE_LOG_20090101 552
自此一次删除大表的操作完成,回顾一下,主要有以下几点:
1。分批删除数据,按批次提交。
2。回收表所占的空间。
3。处理由于表收缩所导致的索引失效问题。
今天在公司中碰到访问表数据(test 表)速度非常慢,简单的一个select 语句花了10多分钟, 后来查询一下表的数据量,一共有278万多条数据,而且这个数据表的数据大都过期了,对于现在的业务没什么用。可悲的是这个表竟然也没做分区,也许是前人设计时欠缺了考虑。因为这个表的访问非常频繁,适成的整个系统卡住的现象,必须马上解决。当然解决的办法就是把没用的过期的数据删除掉,但是表的数据量这么大,直接delete掉过期的数据行肯定要花很多时间,而且也会产生很多不必要的undo,redo信息。
所以我采用 create table test_temp as select * from test t where t.add_data >= '20100817000000', 这样重新建立一个表test_temp只保存最近一天的数据也就几十条而已,然后重命名原来test表为testold表,表test_temp为test表,,再把相关的存储过程,存储函数重新编译一下,并重新建立索引,做表分析,删除原来testold表。 哇,大功告成,访问速度快了。不过,后来发现这个做法产生了两个问题:
1. test_temp表直接建立在默认的system表空间,这是非常不好的,所以采用这种方法记得指定表空间,语法如下: create table T tablespace ts as ........(ts为指定的表空间)
2.原来test表里面的一个约束可能不会移动test_temp表上,比如字段默认值,这个就可能对现在系统DML操作造成影响,所以一定要记住检查一下各个约束,对加上约束。笔者就是出现这个情况,导致部份DML失败。
Oracle删除大表并回收空间的过程
近日在查询某项日志的时候,发现查询非常缓慢,根据以往的经验这是由于某个日志表过大引起的,为了加快查询,决定将大部分的历史数据迁移到另外一个表中,本文主要记录删除这个大表的过程,就解决问题而言还有很多方法,但是本文侧重点在于如何处理大数据量删除的操作,及其善后工作。
首先看看我们要做数据迁移的表所占的空间:
SQL> select segment_name , bytes/1048576 MB 2 from user_segments 3 order by bytes 4 / SEGMENT_NAME MB -------------------------------------------------- ---------- TAB_NODE_PARAM .375 SYS_LOB0000053047C00004$$ .4375 PK_TAB_FLOW_HIS 1 PK_TAB_NODE_HIS 2 TAB_TASK_HIS 2 TAB_FLOW_HIS 4 TAB_NODE_HIS 17 PK_TAB_APPEXCHANGE_LOG 152 TAB_APPEXCHANGE_LOG 768看看历史数据大概所占的空间比例:
SQL> select count(*) from tab_appexchange_log 2 where receive_time >= to_date('2009-01-01','yyyy-mm-dd') 3 union all 4 select count(*) from tab_appexchange_log 5 where receive_time < to_date('2009-01-01','yyyy-mm-dd') SQL> / COUNT(*) ---------- 1584298 4037710从结果来看,的确存在很多历史数据,我们将 2009 年之前的数据迁移到别的表中。
SQL> create table tab_appexchange_log_20090101 2 as 3 select * from tab_appexchange_log 4 where receive_time < to_date('2009-01-01','yyyy-mm-dd') SQL> / Table created. SQL> select count(*) from tab_appexchange_log_20090101 2 / COUNT(*) ---------- 4037710数据已经迁移一份到新的表里面了,可以删除原表中的旧数据了。
SQL> delete tab_appexchange_log where receive_time < to_date('2009-01-01','yyyy-mm-dd') 2 / delete tab_appexchange_log where receive_time < to_date('2009-01-01','yyyy-mm-dd') * ERROR at line 1: ORA-30036: unable to extend segment by 8 in undo tablespace 'UNDOTBS2'这里出现了一个 ORA-30036的错误,大概意思是undo表空间不足,看来是要删除的数据太多了,于是考虑化整为零,删一点,commit一下。
SQL> delete tab_appexchange_log where receive_time < to_date('2007-01-01','yyyy-mm-dd') 2 / 0 rows deleted. SQL> delete tab_appexchange_log where receive_time < to_date('2008-01-01','yyyy-mm-dd') 2 / 1586227 rows deleted. SQL> commit; Commit complete. SQL> delete tab_appexchange_log where receive_time < to_date('2008-06-01','yyyy-mm-dd') 2 / 813019 rows deleted. SQL> commit; Commit complete. SQL> delete tab_appexchange_log where receive_time < to_date('2009-01-01','yyyy-mm-dd') 2 / 1638464 rows deleted. SQL> commit; Commit complete.可以预知,现在表里面的数据应该只有 15***** 条,以下证实了这个想法。
SQL> select count(*) from tab_appexchange_log 2 / COUNT(*) ---------- 1584522我们再看看表占用空间的情况:
SQL> select segment_name , bytes/1048576 MB 2 from user_segments 3 order by bytes asc; SEGMENT_NAME MB -------------------------------------------------- ---------- TAB_NODE_PARAM .375 SYS_LOB0000053047C00004$$ .4375 PK_TAB_FLOW_HIS 1 PK_TAB_NODE_HIS 2 TAB_TASK_HIS 2 TAB_FLOW_HIS 4 TAB_NODE_HIS 17 PK_TAB_APPEXCHANGE_LOG 152 TAB_APPEXCHANGE_LOG_20090101 552 TAB_APPEXCHANGE_LOG 768看来原表所占的空间还没有释放,这个涉及一个HWM的说法,还有可以观察到在TAB_APPEXCHANGE_LOG上面的一个索引 PK_TAB_APPEXCHANGE_LOG 空间没有回收。
我们回过头来看看刚才的操作对undo表空间产生的压力:
TO_CHAR(BEGIN_TIME, TO_CHAR(END_TIME,'Y UNDOBLKS ------------------- ------------------- ---------- 2009-06-30 13:39:32 2009-06-30 13:46:35 96164 2009-06-30 13:29:32 2009-06-30 13:39:32 89316 2009-06-30 13:19:32 2009-06-30 13:29:32 209885 2009-06-30 13:09:32 2009-06-30 13:19:32 105 2009-06-30 12:59:32 2009-06-30 13:09:32 125 2009-06-30 12:49:32 2009-06-30 12:59:32 74 2009-06-30 12:39:32 2009-06-30 12:49:32 80 2009-06-30 12:29:32 2009-06-30 12:39:32 72 2009-06-30 12:19:32 2009-06-30 12:29:32 76 2009-06-30 12:09:32 2009-06-30 12:19:32 89 2009-06-30 11:59:32 2009-06-30 12:09:32 126呵呵,刚才DML的操作对undo表空间产生的压力还是非常可观的。
我们开始回收表所占的空间:
SQL> alter table TAB_APPEXCHANGE_LOG enable row movement; Table altered. SQL> alter table TAB_APPEXCHANGE_LOG shrink space; Table altered.
由于这么大规模的删除,会导致很多索引中存在很多标记为删除的节点,占地方还不能发挥作用,因此重建索引将这些无用节点清理出去才是上策。
SQL> analyze index PK_TAB_APPEXCHANGE_LOG validate structure; Index analyzed. SQL> select height, name , LF_ROWS_LEN,LF_ROWS,DEL_LF_ROWS,DEL_LF_ROWS_LEN from index_stats; HEIGHT NAME LF_ROWS_LEN LF_ROWS DEL_LF_ROWS DEL_LF_ROWS_LEN ---------- ------------------------------ ----------- ---------- ----------- --------------- 3 PK_TAB_APPEXCHANGE_LOG 88905533 5621608 4036429 63558680 SQL> alter index PK_TAB_APPEXCHANGE_LOG rebuild online; Index altered. SQL> analyze index PK_TAB_APPEXCHANGE_LOG validate structure; Index analyzed. SQL> select height, name , LF_ROWS_LEN,LF_ROWS,DEL_LF_ROWS,DEL_LF_ROWS_LEN from index_stats; HEIGHT NAME LF_ROWS_LEN LF_ROWS DEL_LF_ROWS DEL_LF_ROWS_LEN ---------- ------------------------------ ----------- ---------- ----------- --------------- 3 PK_TAB_APPEXCHANGE_LOG 25347109 1585195 0 0如果现在我们再查一下索引所占的空间的话,会发现所占空间是没有减少的,和表一样,同样也是需要收缩一下空间。
SQL> alter index PK_TAB_APPEXCHANGE_LOG shrink space; Index altered. SQL> select segment_name , bytes/1048576 MB 2 from user_segments 3 order by bytes 4 / SEGMENT_NAME MB -------------------------------------------------- ---------- TAB_NODE_PARAM .375 SYS_LOB0000053047C00004$$ .4375 PK_TAB_FLOW_HIS 1 TAB_TASK_HIS 2 PK_TAB_NODE_HIS 2 TAB_FLOW_HIS 4 TAB_NODE_HIS 17 PK_TAB_APPEXCHANGE_LOG 28.1875 TAB_APPEXCHANGE_LOG 220.8125 TAB_APPEXCHANGE_LOG_20090101 552
自此一次删除大表的操作完成,回顾一下,主要有以下几点:
1。分批删除数据,按批次提交。
2。回收表所占的空间。
3。处理由于表收缩所导致的索引失效问题。