Per capire chi è SMON e cosa fa, diamo uno sguardo direttamente alla documentazione di Oracle (In appendice, ho inserito
altre definizioni di SMON prese dalla documentazione ufficiale):
Dal manuale
Concept:
The system monitor process (SMON) performs recovery, if necessary, at instance startup. SMON is
also responsible for cleaning up
temporary segments that are no longer in use and for coalescing contiguous free extents within dictionary managed tablespaces. If any
terminated transactions were skipped during instance recovery because of file-read or offline errors, SMON recovers them when the
tablespace or file is brought back online. SMON checks regularly to see whether it is needed. Other processes can call SMON if they
detect a need for it.
With Real Application Clusters, the SMON process of one instance can perform instance recovery for a failed CPU or instance.
Di seguito commenterò quanto sopra detto, ed in particolare:
- la "pulizia dei segmenti temporanei"
- la "fusione di segmenti contigui"
1. Pulizia dei segmenti temporanei
====================
Come detto in [1], uno dei compiti base del
System Monitor, è quello di ciclare durante la vita dell'istanza: ogni 25 cicli (cioè ogni 2 ore e 5 minuti), SMON interroga la
tabella dei segmenti (sys.seg$) per vedere se ci sono segmenti temporanei che devono essere eliminti.
Lo statement che SMON esegue sul dizionario dati, in questo caso è:
select file#, block#
from seg$
where type = 3
Come verifica, ho messo sotto trace SMON, sul mio db 9R2P6, ed ecco cosa ho trovato:
PARSING IN CURSOR #1 len=51 dep=1 uid=0 oct=3 lid=0 tim=38631880512065 hv=3587907089 ad='8efaa9e8'
select file#, block#, ts# from seg$ where type# = 3
END OF STMT
PARSE #1:c=0,e=2433,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=4,tim=38631880512045
EXEC #1:c=0,e=121,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=38631880512470
WAIT #1: nam='db file sequential read' ela= 9601 p1=1 p2=50 p3=1
WAIT #1: nam='db file sequential read' ela= 7383 p1=1 p2=233 p3=1
WAIT #1: nam='db file sequential read' ela= 7063 p1=1 p2=406 p3=1
WAIT #1: nam='db file scattered read' ela= 5373 p1=1 p2=683 p3=2
WAIT #1: nam='db file sequential read' ela= 64 p1=1 p2=1074 p3=1
WAIT #1: nam='db file sequential read' ela= 68 p1=1 p2=1082 p3=1
WAIT #1: nam='db file sequential read' ela= 63 p1=1 p2=7668 p3=1
WAIT #1: nam='db file scattered read' ela= 8759 p1=1 p2=7671 p3=2
WAIT #1: nam='db file sequential read' ela= 77 p1=1 p2=7674 p3=1
WAIT #1: nam='db file sequential read' ela= 55 p1=1 p2=7676 p3=1
WAIT #1: nam='db file scattered read' ela= 12652 p1=1 p2=32394 p3=6
WAIT #1: nam='db file scattered read' ela= 5918 p1=1 p2=32401 p3=9
WAIT #1: nam='db file scattered read' ela= 1521 p1=1 p2=32412 p3=2
WAIT #1: nam='db file scattered read' ela= 14441 p1=1 p2=35429 p3=16
WAIT #1: nam='db file scattered read' ela= 1522 p1=1 p2=35445 p3=14
WAIT #1: nam='db file sequential read' ela= 67 p1=1 p2=36722 p3=1
WAIT #1: nam='db file scattered read' ela= 12576 p1=1 p2=36725 p3=16
WAIT #1: nam='db file scattered read' ela= 147 p1=1 p2=36742 p3=2
WAIT #1: nam='db file scattered read' ela= 1818 p1=1 p2=36745 p3=4
WAIT #1: nam='db file sequential read' ela= 73 p1=1 p2=36750 p3=1
WAIT #1: nam='db file scattered read' ela= 780 p1=1 p2=36752 p3=5
WAIT #1: nam='db file scattered read' ela= 192 p1=1 p2=36759 p3=2
FETCH #1:c=30000,e=107837,p=90,cr=130,cu=0,mis=0,r=1,dep=1,og=4,tim=38631880620440
Se Oracle scopre che un nuovo extent libero finisce lì dove ne inizia uno libero già esistente, allora cancella
la riga esistente e la sostituisce con una che descrive la nuova posizione e la lunghezza totale della combinazione.
Questo è noto come "forward coalescing" ed è utilizzato per mantenere il numero di extents in un tablespace, il più basso
possibile.
Quello che mi sarei aspettato a questo punto allora era un nuovo statemet di SELECT, per determiare cosa cancellare e poi
uno statement di DELETE ed uno di UPDATE. Nelle prove che ho fatto tuttavia, l'UPDATE non è comparso. Probabilmente
ciò è dovuto al fatto che come db di sviluppo, c'è poca attività:
PARSING IN CURSOR #2 len=190 dep=1 uid=0 oct=3 lid=0 tim=38631880624167 hv=1006414593 ad='90fbb760'
select type#, blocks, extents, minexts, maxexts, extsize, extpct,
user#, iniexts,NVL(lists,65535),NVL(groups,65535), cachehint, hwmincr,NVL(spare1,0)
from seg$ where ts#=:1 and file#=:2 and block#=:3
END OF STMT
PARSE #2:c=0,e=3326,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=0,tim=38631880624151
EXEC #2:c=10000,e=20954,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=38631880645727
FETCH #2:c=0,e=162,p=0,cr=3,cu=0,mis=0,r=1,dep=1,og=4,tim=38631880646023
STAT #2 id=1 cnt=1 pid=0 pos=1 obj=14 op='TABLE ACCESS CLUSTER SEG$ '
STAT #2 id=2 cnt=1 pid=1 pos=1 obj=9 op='INDEX UNIQUE SCAN I_FILE#_BLOCK# '
=====================
PARSING IN CURSOR #2 len=56 dep=1 uid=0 oct=7 lid=0 tim=38631880813111 hv=3395058227 ad='90f99c80'
delete from seg$ where ts#=:1 and file#=:2 and block#=:3
END OF STMT
PARSE #2:c=0,e=1689,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=0,tim=38631880813095
EXEC #2:c=0,e=1826,p=0,cr=5,cu=2,mis=0,r=1,dep=1,og=4,tim=38631880815298
STAT #2 id=1 cnt=0 pid=0 pos=1 obj=0 op='DELETE '
STAT #2 id=2 cnt=1 pid=1 pos=1 obj=14 op='TABLE ACCESS CLUSTER SEG$ '
STAT #2 id=3 cnt=1 pid=2 pos=1 obj=9 op='INDEX UNIQUE SCAN I_FILE#_BLOCK# '
Una descrizione sui segmenti temporanei la possiamo ritrovare in [3]. In tale documento oltre ad una
spiegazione sommaria di cosa siano e come vengono utilizzati, troviamo riferimenti ad altri documenti
che spiegano in dettaglio i vari argomenti trattani.
2. Fusione dei segmenti contigui
===================
Sempre in [1], leggiamo che il secondo, per unire lo spazio libero, seleziona semplicemente tutte le righe dalla sys.fet$,
per vedere se
alcuni di essi costituiscono pezzi adiacenti si spazio libero; in questo caso, SMON può sostiruire due o più righe nella
fet$ con una singola riga (questo è fatto da un singolo UPDATE e molti DELETEs).
ogni 5 minuti interroga la tabella degli extent liberi (sys.fet$) per vedere se ci sono estensioni libere adiacenti
che possono essere fuse insieme in un singolo extent. In [1], leggiamo che lo statement in questo caso è:
select f.file#, f.block#, f.ts#, f.length
from fet$ f, ts$ t
where t.ts#=f.ts#
and t.dflextpct!=0
Mettendo sotto trace SMON, ho scoperto che:
PARSING IN CURSOR #1 len=115 dep=1 uid=0 oct=3 lid=0 tim=38641606564180 hv=2095543314 ad='9051deb0'
select f.file#, f.block#, f.ts#, f.length from fet$ f, ts$
t where t.ts#=f.ts# and t.dflextpct!=0 and t.bitmapped=0
END OF STMT
PARSE #1:c=0,e=291,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=38641606564159
BINDS #1:
EXEC #1:c=0,e=175,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=38641606564706
FETCH #1:c=0,e=293,p=0,cr=6,cu=0,mis=0,r=1,dep=1,og=4,tim=38641606565103
FETCH #1:c=0,e=48,p=0,cr=1,cu=0,mis=0,r=1,dep=1,og=4,tim=38641606565317
FETCH #1:c=0,e=47,p=0,cr=1,cu=0,mis=0,r=1,dep=1,og=4,tim=38641606565490
FETCH #1:c=0,e=48,p=0,cr=1,cu=0,mis=0,r=1,dep=1,og=4,tim=38641606565663
FETCH #1:c=0,e=48,p=0,cr=1,cu=0,mis=0,r=1,dep=1,og=4,tim=38641606565837
WAIT #1: nam='db file sequential read' ela= 108 p1=1 p2=1071 p3=1
WAIT #1: nam='db file sequential read' ela= 77 p1=1 p2=1390 p3=1
WAIT #1: nam='db file sequential read' ela= 75 p1=1 p2=35344 p3=1
WAIT #1: nam='db file scattered read' ela= 461 p1=1 p2=35346 p3=8
WAIT #1: nam='db file scattered read' ela= 12694 p1=1 p2=35361 p3=15
WAIT #1: nam='db file scattered read' ela= 806 p1=1 p2=35376 p3=9
WAIT #1: nam='db file scattered read' ela= 516 p1=1 p2=35386 p3=10
WAIT #1: nam='db file scattered read' ela= 373 p1=1 p2=35399 p3=7
WAIT #1: nam='db file scattered read' ela= 180 p1=1 p2=35407 p3=3
WAIT #1: nam='db file scattered read' ela= 8799 p1=1 p2=35412 p3=16
WAIT #1: nam='db file sequential read' ela= 121 p1=1 p2=35428 p3=1
WAIT #1: nam='db file scattered read' ela= 182 p1=1 p2=35672 p3=3
WAIT #1: nam='db file scattered read' ela= 428 p1=1 p2=35676 p3=8
WAIT #1: nam='db file sequential read' ela= 72 p1=1 p2=35685 p3=1
WAIT #1: nam='db file scattered read' ela= 228 p1=1 p2=35688 p3=4
FETCH #1:c=10000,e=31120,p=88,cr=106,cu=0,mis=0,r=0,dep=1,og=4,tim=38641606597082
STAT #1 id=1 cnt=5 pid=0 pos=1 obj=0 op='NESTED LOOPS '
STAT #1 id=2 cnt=1 pid=1 pos=1 obj=16 op='TABLE ACCESS FULL TS$ '
STAT #1 id=3 cnt=5 pid=1 pos=2 obj=12 op='TABLE ACCESS CLUSTER FET$ '
Come si nota, rispetto a quanto descritto in [1], compare una nuova condizione di AND:
AND BITMAPPED=0
Questo è perchè con l'introduzione dei Locally Managed Tablespaces, le cose sono cambiate. In un LMT [2]
Oracle non deve cercare in una tabella per trovare una riga che descrive quale pezzo sia disponibile; piuttosto guarda la mappa
di bit presente nei primi 64KB del datafile (ogni bit rappresenta un pezzo del file) per determinare se esiste un extent libero.
I vantaggi in questo caso sono tanti, a partire dal fatto che invece di utilizzare un enqueue di "single space transaction (ST)"
sull'intero database, Oracle ne utilizza uno nuovo (TT) permettendo un "TT enqueue" per tablespace in modo da ridurre i problemi di
contenziono dovuti a transazioni che simultaneamente modificano lo spazio.
[1]
SMON, Free Space and TEMP
(
Jonathan Lewis)
[2]
Locally Managed Tablespaces
(
Jonathan Lewis)
[3]
177334.1 - Overview of Temporary Segments
(
metalink)
Alcuni documenti molto interessanti su cosa fa SMON si trovano su
metalink. In particolare:
Note:
21169.1 - EVENT: 10061 "disable SMON from cleaning temp segment"
Note:
35513.1 - Removing `stray` TEMPORARY Segments
Note:
47400.1 - EVENT: DROP_SEGMENTS - Forcing cleanup of TEMPORARY segments
Note:
68836.1 - How To Efficiently Drop A Table With Many Extents
Di seguito altre definizioni che ho trovato su SMON, prese direttamente dalla documentazione ufficiale.
Oracle 10gR2 Administrator Guide:
The system monitor performs recovery when a failed instance starts up again. In a Real Application Clusters database, the SMON process
of one instance can perform instance recovery for other instances that have failed. SMON also cleans up temporary segments that are no
longer in use and recovers dead transactions skipped during system failure and instance recovery because of file-read or offline errors.
These transactions are eventually recovered by SMON when the tablespace or file is brought back online.
Oracle 10gR2 Administrator Guide:
The PL/SQL package DBMS_STATS lets you generate and manage statistics for cost-based optimization. You can use this package to gather,
modify, view, export, import, and delete statistics. You can also use this package to identify or name statistics that have been
gathered.
Formerly, you enabled DBMS_STATS to automatically gather statistics for a table by specifying the MONITORING keyword in the CREATE (or
ALTER) TABLE statement. Starting with Oracle Database 10g, the MONITORING and NOMONITORING keywords have been deprecated and statistics
are collected automatically. If you do specify these keywords, they are ignored.
Monitoring tracks the approximate number of INSERT, UPDATE, and DELETE operations for the table since the last time statistics were
gathered. Information about how many rows are affected is maintained in the SGA, until periodically (about every three hours) SMON
incorporates the data into the data dictionary. This data dictionary information is made visible through the DBA_TAB_MODIFICATIONS,
ALL_TAB_MODIFICATIONS, or USER_TAB_MODIFICATIONS views. The database uses these views to identify tables with
stale statistics.
To disable monitoring of a table, set the STATISTICS_LEVEL initialization parameter to BASIC. Its default is TYPICAL, which enables
automatic statistics collection. Automatic statistics collection and the DBMS_STATS package enable the optimizer to generate accurate
execution plans.