Slow TRUNCATE TABLE due to operations on MON_MODS$


27.01.2015
by Kamil Stawiarski

One of my customers had a problem with a very slow TRUNCATE TABLE operation on a partitioned table having 22 000 partitions. The problem has raised after a test migration to database 12c. I’ve prepared a little demo to address this issue and show the walkaround for it.

Oracle Database 12c (12.1.0.2):

1[oracle@skiper ~]$ sqlplus hr/hr
2 
3SQL*Plus: Release 12.1.0.2.0 Production on Tue Jan 27 10:11:23 2015
4 
5Copyright (c) 1982, 2014, Oracle.  All rights reserved.
6 
7Last Successful login time: Mon Jan 26 2015 17:56:53 +01:00
8 
9Connected to:
10Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
11With the Partitioning, Automatic Storage Management, OLAP, Advanced Analytics
12and Real Application Testing options
13 
14SQL> select count(1)
15  2  from user_tab_partitions
16  3  where table_name='TRUNCATE_TEST';
17 
18  COUNT(1)
19----------
20     22000
21 
22SQL> set timing on
23SQL> truncate table TRUNCATE_TEST;
24 
25Table truncated.
26 
27Elapsed: 00:05:33.71

Oracle Database 11g (11.2.0.4)

1[oracle@skiper ~]$ sqlplus hr/hr
2 
3SQL*Plus: Release 11.2.0.4.0 Production on Tue Jan 27 10:15:05 2015
4 
5Copyright (c) 1982, 2013, Oracle.  All rights reserved.
6 
7 
8Connected to:
9Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
10With the Partitioning, Automatic Storage Management, OLAP, Data Mining
11and Real Application Testing options
12 
13SQL> select count(1)
14  2  from user_tab_partitions
15  3  where table_name='TRUNCATE_TEST';
16 
17  COUNT(1)
18----------
19     22000
20 
21SQL> set timing on
22SQL> truncate table TRUNCATE_TEST;
23 
24Table truncated.
25 
26Elapsed: 00:00:06.16

What the hell? On 12c the truncate took 00:05:33.71 and on 11g it was only 00:00:06.16
To understand the problem I’ve used the 10046 event on level 12 to generate trace with bind variables and wait events:

1SQL> get spid
2  1  select spid
3  2  from v$process p, v$session s
4  3  where p.addr=s.paddr
5  4* and s.username='HR'
6SQL> /
7 
8SPID
9------------------------
104797
11 
12SQL> oradebug setospid 4797
13Oracle pid: 28, Unix process pid: 4797, image: oracle@skiper (TNS V1-V3)
14SQL> oradebug event 10046 trace name context forever, level 12
15Statement processed.
16SQL> oradebug tracefile_name
17/u01/app/oracle/diag/rdbms/rico/rico/trace/rico_ora_4797.trc

A quick look on the size of the trace files assures me that in 12c there is a lot more operations during the "TRUNCATE TABLE".

1[oracle@skiper truncate_test]$ wc -l raw_truncate11g.trc
21113 raw_truncate11g.trc
3[oracle@skiper truncate_test]$ wc -l raw_truncate12c.trc
44313645 raw_truncate12c.trc

Further investigations shows the SQLs which are the reason of a slow TRUNCATE execution in the 12c database.

1select inserts, deletes
2from
3   sys.mon_mods$  where  obj# = :objn
4 
5 
6call     count       cpu    elapsed       disk      query    current        rows
7------- ------  -------- ---------- ---------- ---------- ----------  ----------
8Parse    44000      2.18       2.70          0          0          0           0
9Execute  44000      8.56       9.89          0          0          0           0
10Fetch    44000      3.14       4.03          0     110000          0       22000
11------- ------  -------- ---------- ---------- ---------- ----------  ----------
12total   132000     13.88      16.62          0     110000          0       22000
1SQL ID: bb926a5dcb8kr Plan Hash: 3530933756
2 
3merge into sys.mon_mods$ m
4  using dual
5  on (m.obj# = :objn and
6     (:pobjn = 0 or
7     m.obj# = :pobjn or
8    m.obj# in (select obj#
9              from tabpart$
10             where bo# = :pobjn and obj# = :objn
11            union all
12           select obj#
13          from tabcompart$
14         where bo# = :pobjn and obj# = :objn
15        union all
16       select tsp.obj#
17      from tabsubpart$ tsp, tabcompart$ tcp
18     where tsp.pobj# = tcp.obj#
19      and (tcp.bo# = :pobjn or tcp.obj# = :pobjn)
20     and tsp.obj# = :objn)))                                  when matched
21  then                                                            update
22                                                                 set inserts =
23   inserts + :ins, updates = updates + :upd,                        deletes =
24  deletes + :del,                                                  flags =
25  (decode(bitand(flags, :flag), :flag, flags, flags + :flag)),
26  drop_segments = drop_segments + :dropseg,
27  timestamp = :time                                                    when
28  NOT matched then
29  insert (obj#, inserts, updates, deletes,
30         timestamp, flags, drop_segments)
31  values (:objn, :ins, :upd, :del, :time, :flag, :dropseg)
32 
33 
34call     count       cpu    elapsed       disk      query    current        rows
35------- ------  -------- ---------- ---------- ---------- ----------  ----------
36Parse    44000      2.01       2.28          0          0          0           0
37Execute  44000     57.61      65.34          0     264100     133393       44000
38Fetch        0      0.00       0.00          0          0          0           0
39------- ------  -------- ---------- ---------- ---------- ----------  ----------
40total    88000     59.62      67.63          0     264100     133393       44000

The MON_MODS$ table is related to the following views:

  • USER_TAB_MODIFICATIONS
  • ALL_TAB_MODIFICATIONS
  • DBA_TAB_MODIFICATIONS
  • ALL_TAB_STATISTICS
  • DBA_TAB_STATISTICS
  • USER_TAB_STATISTICS
  • ALL_IND_STATISTICS
  • DBA_IND_STATISTICS
  • USER_IND_STATISTICS
  • _user_stat

So it looks like that this behavior is related to the DML monitoring feature. Is it possible? The "TRUNCATE TABLE" is a DDL, not a DML operation!

Let’s check it by turning off the DML monitoring feature:

1[oracle@skiper ~]$ sqlplus / as sysdba
2 
3SQL*Plus: Release 12.1.0.2.0 Production on Tue Jan 27 11:08:03 2015
4 
5Copyright (c) 1982, 2014, Oracle.  All rights reserved.
6 
7 
8Connected to:
9Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
10With the Partitioning, Automatic Storage Management, OLAP, Advanced Analytics
11and Real Application Testing options
12 
13SQL> alter system set "_dml_monitoring_enabled"=FALSE scope=memory;
14 
15System altered.
16 
17SQL> conn hr/hr
18Connected.
19SQL> set timing on
20SQL> truncate table TRUNCATE_TEST;
21 
22Table truncated.
23 
24Elapsed: 00:00:00.18

WOW! So the conclusion is: TRUNCATE TABLE in database 12c is using the DML monitoring feature for some reason! Why? I don’t know it yet but I have a hypothesis that this is somehow related to the new features in the database 12c, regarding online operations on partitioned tables:

http://docs.oracle.com/database/121/VLDBG/to_vldbg14107_d290.htm#VLDBG14107

The problem is: my test table has no indexes and no data. It’s just an empty table with 22000 partitions. And database knows it, because I’ve gathered statistics:

1SQL> select last_analyzed, num_rows
2  2  from user_tables
3  3  where table_name='TRUNCATE_TEST';
4 
5LAST_ANAL   NUM_ROWS
6--------- ----------
726-JAN-15      0
8 
9SQL> select sum(num_rows), sum(blocks)
10  2  from user_tab_partitions
11  3  where table_name='TRUNCATE_TEST';
12 
13SUM(NUM_ROWS) SUM(BLOCKS)
14------------- -----------
15        0       0

So what to do? As always: keep calm and use hidden parameters 🙂


Contact us

Database Whisperers sp. z o. o. sp. k.
al. Jerozolimskie 200, 3rd floor, room 342
02-486 Warszawa
NIP: 5272744987
REGON:362524978
+48 508 943 051
+48 661 966 009
info@ora-600.pl

Newsletter Sign up to be updated