The UNDO tablespace is one of the workhorses of an Oracle database (TEMP being the other) yet, unlike TEMP, UNDO requires a special tablespace configuration so that the UNDO mechanism operates properly. Conventional wisdom on configuring tablespaces in general has little or no place when it comes to the UNDO tablespace and following such guidelines with the UNDO tablespace can wreak havoc on storage. Let’s look into that a bit more.
UNDO is a special mechanism that provides a way for Oracle to do several things:
* Provide information to rollback an uncommitted transaction * Provide a read-consistent view of data at the time a query starts * Allow recovery to undo uncommitted changes to the database * Provide FLASHBACK QUERY functionality * Allow recovery from logical corruption using FLASHBACK features
It’s also configured to reuse segment space as it becomes available. This is where conventional tablespace wisdom can bite you if that conventional tablespace/datafile wisdom is to set autoextend on for at least one file in the tablespace. Doing so for other tablespaces can be a good idea. [Monitoring TEMP can help catch runaway statements that consume vast amounts of temporary space and cause the TEMP tablespace to expand to the limits of the available storage; such statements are usually few and far between.] Setting autoextend for any file in the UNDO tablespace is a mistake and possibly a disaster waiting to happen.
Let’s look at what is supposed to happen with the UNDO tablespace:
* A transaction begins, updating a large number of rows in a table. * Oracle tracks the changes via the REDO and UNDO mechanisms, writing UNDO data to the UNDO tablespace. * As UNDO records age out the space is reused by existing transactions
That, of course, is the same mechanism that can throw the dastardly ORA-01555 (snapshot too old) error. With automatic UNDO management this usually isn’t an issue but there are some who think that setting autoextend on for the UNDO datafiles will fix that problem. Unfortuately it won’t as autoextend doesn’t increase the size of the undo segments, just the UNDO tablespace, and if autoextend is on this defeats the mechanism of reusing UNDO space from aged-out UNDO segments. As a result the UNDO tablespace can grow seemingly without bound since Oracle will simply allocated additional file space rather than resuse the expired segments.
Oracle does not recommend the use of autoextend on any of the datafiles associated with the UNDO tablespace, which doesn’t mean Oracle doesn’t allow such a configuration. Examples in the documentation clearly set up autoextend on an UNDO tablespace. According to the documentation: “Oracle Database supports automatic extension of the undo tablespace to facilitate capacity planning of the undo tablespace in the production environment.” To me production is not the place to be unsure of the UNDO configuration; that’s what the testing database environment is for. Running UNDO with autoextend on, in a load-testing scenario might be a good idea as it could provide very useful information for the setup of a fixed-size UNDO tablespace in the production environment. Letting UNDO grow essentially unchecked is not wise; let the system generate errors for space and address them as the need arises. It’s very likely that these errors will be few and require far smaller adjustments than autoextend would generate.
UNDO needs to be checked just like other tablespaces and two views are key to this task: V$UNDOSTAT and V$ROLLSTAT. The following queries report on the status of used UNDO segments, who is using them, and their respective sizes:
set linesize 80 set verify off echo off pause off timing off time off set feedback off column o format a8 heading 'O/S|User' column u format a20 heading 'Oracle|Userid' column s format a42 heading 'R-S|Name' column txt format a45 heading 'Current Statement' word select osuser o, username u, segment_name s, sa.sql_text txt from v$session s, v$transaction t, dba_rollback_segs r, v$sqlarea sa where s.taddr = t.addr and t.xidusn = r.segment_id(+) and s.sql_address = sa.address(+) / set verify on column o clear column u clear column txt clear REM Displays who is using what rollback segment REM select vs.username, vt.used_ublk from v$session vs, v$transaction vt where vt.addr = vs.taddr /
This script, or something similar, can help keep track of who is using UNDO segments and how large those segments are. It can also help if it’s run during long transactions as any UNDO size errors will report the segment at fault and that can be mapped back to the user, and transaction, causing the problem.
If even more UNDO information is desired the following script will report undo segment size, resizes, extensions, active transactions and a few other bits of information:
break on today column today noprint new_value xdate select substr(to_char(sysdate,'fmMonth DD, YYYY HH:MI:SS P.M.'),1,35) today from dual; column name noprint new_value dbname column owner format a35 column object format a35 column object_name format a35 column "ID#" format a12 column "ID" format a12 column "File Name" format a60 column "Phy Reads" format a10 column "Phy Writes" format a10 column "Blk Reads" format a10 column "Blk Writes" format a10 column "Read Time" format a10 column "Write Time" format a10 column "File Total" format a10 column "Table Name (Segment)" format a35 column "DataFile Name" format a60 column "Tablespace Name" format a35 column "TableSpace Name" format a35 column "Rollback Name" format a35 column "Rollback_Name" format a35 column "Rollback_name" format a35 column "INI_extent" format a10 column "Next Exts" format a10 column "MinEx" format a10 column "MaxEx" format a10 column "Size (Bytes)" format a10 column "Extent#" format a10 column "Status" format a10 column "%Incr" format a10 column machine format a25 column terminal format a25 column extent format a12 column extend format a12 column waits format a12 column xacts format a12 column wraps format a12 column "Seg Type" format a35 column "DB Username" format a35 select name from v$database; set heading on set feedback off set linesize 350 trimspool on spool undo_info.lst prompt ********************************************************** prompt ***** Database Information ***** prompt ********************************************************** ttitle left "DATABASE: "dbname" (AS OF: "xdate")" select name, created, log_mode from v$database; prompt prompt ********************************************************** ttitle off rem ------------------------------------------------------------- rem Rollback Information rem ------------------------------------------------------------- set pagesize 66 set line 350 trimspool on TTitle left "*** Database: "dbname", Rollback Information ( As of: " xdate " ) ***" skip 2 select substr(sys.dba_rollback_segs.SEGMENT_ID,1,5) "ID#", substr(sys.dba_segments.OWNER,1,8) "Owner", substr(sys.dba_segments.TABLESPACE_NAME,1,17) "Tablespace Name", substr(sys.dba_segments.SEGMENT_NAME,1,17) "Rollback Name", substr(sys.dba_rollback_segs.INITIAL_EXTENT,1,10) "INI_Extent", substr(sys.dba_rollback_segs.NEXT_EXTENT,1,10) "Next Exts", substr(sys.dba_segments.MIN_EXTENTS,1,5) "MinEx", substr(sys.dba_segments.MAX_EXTENTS,1,5) "MaxEx", substr(sys.dba_segments.PCT_INCREASE,1,5) "%Incr", substr(sys.dba_segments.BYTES,1,15) "Size (Bytes)", substr(sys.dba_segments.EXTENTS,1,6) "Extent#", substr(sys.dba_rollback_segs.STATUS,1,10) "Status" from sys.dba_segments, sys.dba_rollback_segs where sys.dba_segments.segment_name = sys.dba_rollback_segs.segment_name and sys.dba_segments.segment_type = 'ROLLBACK' order by sys.dba_rollback_segs.segment_id; ttitle off TTitle left " " skip 2 - left "*** Database: "dbname", Rollback Status ( As of: " xdate " ) ***" skip 2 select substr(V$rollname.NAME,1,20) "Rollback_Name", substr(V$rollstat.EXTENTS,1,6) "EXTENT", v$rollstat.RSSIZE, v$rollstat.WRITES, substr(v$rollstat.XACTS,1,6) "XACTS", v$rollstat.GETS, substr(v$rollstat.WAITS,1,6) "WAITS", v$rollstat.HWMSIZE, v$rollstat.SHRINKS, substr(v$rollstat.WRAPS,1,6) "WRAPS", substr(v$rollstat.EXTENDS,1,6) "EXTEND", v$rollstat.AVESHRINK, v$rollstat.AVEACTIVE from v$rollname, v$rollstat where v$rollname.USN = v$rollstat.USN order by v$rollname.USN; ttitle off TTitle left " " skip 2 - left "*** Database: "dbname", Rollback Segment Mapping ( As of: " xdate " ) ***" skip 2 select r.name Rollback_Name, p.pid Oracle_PID, p.spid OS_PID, nvl(p.username,'NO TRANSACTION') TXN_OWNER, p.terminal Terminal from v$lock l, v$process p, v$rollname r where l.addr = p.addr(+) and trunc(l.id1(+)/65536)=r.usn and l.type(+) = 'TX' and l.lmode(+) = 6 order by r.name; ttitle off rem ------------------------------------------------------------- rem ------------------------------------------------------------- spool off set feedback on
Such a report may be very useful during problem investigations.
The UNDO tablespace should not be configured using autoextend as it intereferes with the normal operation of the UNDO mechanism. If there are regularly occurring UNDO errors then it’s very likely the UNDO tablespace is undersized and it should be expanded manually, in known increments, until the errors go away. Of course there will likely be that one transaction that does throw an error on occasion but those should be dealt with on a case-by-case basis; in the case of ORA-01555 errors it may not be the UNDO tablespace at fault so query tuning might be the correct direction to take. Each generated error needs to be evaluated on its own and addressed accordingly; not every error benefits from a generalized ‘solution’, and it’s better to let the space-related errors be thrown so an actual root cause can be determined than to let Oracle ‘run wild’ with space allocations by setting autoextend on.
Monitoring and managing UNDO space is a bit different than for any other tablespace, and once that is known the task becomes a bit easier. And shortcuts, like using autoextend, can create more problems than they were intended to fix.