zoukankan      html  css  js  c++  java
  • 转  表空间占用

    http://blog.sina.com.cn/s/blog_6ff05a2c0100mjrw.html

     

    ORACLE]关于delete释放表存储空间的中度研究

    (2010-11-17 16:47:10)

    http://simg.sinajs.cn/blog7style/images/common/sg_trans.gif转载

    标签:

    oracle

    delete

    存储空间

    重用

    分类: Oracle

    在给QA部门做Oracle基础原理分享时,有位同学提问,他说他在测试的时候造了一批数据,例如有1000万,然后按照一定的条件删掉了100万,接着又新插入了100万,结果却报表空间不足的错误。他的疑问是新插入的100万难道不能重用之前删除的100万的存储空间吗?
    我这里由浅入深地按照问答的形式来进行研究解答。
    1.insert
    操作能否重用delete操作释放的表存储空间?
    答案是肯定的,如果Oracle连这一点都做不到那现在肯定早已关门大吉了。简单实验如下:
    ETL@RACTEST> create table ttt1 (a char(2000));

    Table created.

    ETL@RACTEST> Select owner,segment_name,Sum(bytes)/1024/1024||'MB' as sizes
       From dba_extents
       WHERE
       segment_name='TTT1'
       Group By owner,segment_name
       ORDER BY 3 DESC;

    OWNER    SEGME    SIZES
    -----         -----      ------------
    ETL           TTT1  .0625MB
    我创建了一个空表,这个空表初始占了0 .0625MB
    ETL@RACTEST> insert into ttt1 select 'a' from dual connect by level<=100;

    100 rows created.

    Elapsed: 00:00:00.00
    ETL@RACTEST> commit;

    Commit complete.

    Elapsed: 00:00:00.00
    ETL@RACTEST> Select owner,segment_name,Sum(bytes)/1024/1024||'MB' as sizes
       From dba_extents
       WHERE
       segment_name='TTT1'
       Group By owner,segment_name
       ORDER BY 3 DESC;

    OWNER SEGME SIZES
    ----- ----- ------------------------------------------
    ETL   TTT1  .3125MB
    然后插入100行数据,插入以后表的存储大小是0.3125MB
    ETL@RACTEST> delete from ttt1;

    100 rows deleted.

    Elapsed: 00:00:00.01
    ETL@RACTEST> commit;

    Commit complete.

    Elapsed: 00:00:00.00
    ETL@RACTEST> Select owner,segment_name,Sum(bytes)/1024/1024||'MB' as sizes
       From dba_extents
       WHERE
       segment_name='TTT1'
       Group By owner,segment_name
       ORDER BY 3 DESC;

    OWNER SEGME SIZES
    ----- ----- ------------------------------------------
    ETL   TTT1  .3125MB
    我将表里的数据全部清空,用的是delete,这时查表还是占用了0.3125MB,因为delete不会改变表的高水标记,因此虽然表里面没有数据,但是还占用着数据块。这里稍微说明一下,高水标记除了truncate操作以外,只会增大,不会减小。虽然高水标记里的块可能有空块,这个块也要被放到freelist中,但是这个块只能被重用,也就是说它只能被这个表使用,而不能彻底释放给别的数据对象使用,这点要清楚。
    ETL@RACTEST> insert into ttt1 select 'a' from dual connect by level<=100;

    100 rows created.

    Elapsed: 00:00:00.01
    ETL@RACTEST> commit;

    Commit complete.

    Elapsed: 00:00:00.00
    ETL@RACTEST> Select owner,segment_name,Sum(bytes)/1024/1024||'MB' as sizes
       From dba_extents
       WHERE
       segment_name='TTT1'
       Group By owner,segment_name
       ORDER BY 3 DESC;

    OWNER SEGME SIZES
    ----- ----- ------------------------------------------
    ETL   TTT1  .3125MB
    我重新插入100条数据,可以看到表占用的空间还是0.3125MB,说明delete释放掉的表存储空间是可以被重用的。根据我上面的说明,高水标记只能增大不能减小,可以判定新插入的100条数据用的是之前的块,而没有申请新块,因为如果申请了新块,高水标记会增长,那么表占用的存储空间也会增长。
    2.Jack
    提的问题,为什么删除100万,再插入100万会报表空间不足。
    有几个原因,我这里先讲两个简单的,下面再讲主要原因。
    第一个原因:退一万步讲,新插入的100万数据通常和之前删除的100万数据不完全一样,有可能新插的100万行数据比之前删除的100万行数据占用的存储空间多,这是正常的。例如一个字段定义为varchar2(4000),那么存'a'和存'abcdefg'所占用的存储空间就不同。
    第二个原因:表的一个数据块有可利用的存储空间后会被放入freelist链表中,通常会被放到链尾。而申请新块时可能从freelist的链头申请,因此可能不用之前释放的块而申请新块,因为高水标记只能增长不能减小,因此表的存储占用也只会增长不会减小,这样报表空间不足也是可能的。
    3.
    是不是delete释放的存储一定会被重用?
    答案是否定的,先举个例子,后面再说明原因。
    ETL@RACTEST> create table ttt5(a char(800),b CHAR(2));

    Table created.
    ETL@RACTEST> insert into ttt5 select 'a',level from dual connect by level<=20;

    20 rows created.

    Elapsed: 00:00:00.00
    ETL@RACTEST> commit;

    Commit complete.
    ETL@RACTEST> select b,dbms_rowid.rowid_block_number(rowid) rn from ttt5 order by to_number(b);


            RN
    -- ----------
         47016
         47016
         47016
         47016
         47016
         47016
         47016
         47016
         47012
    10      47012
    11      47012
    12      47012
    13      47012
    14      47012
    15      47012
    16      47012
    17      47013
    18      47013
    19      47013
    20      47013

    20 rows selected.
    我创建了一个表,插入20条数据,RN是块的序号,可以看到一个块可以存储8条数据,47013块没有被占满。
    ETL@RACTEST> ETL@RACTEST> delete from ttt5 where b<=2;

    2 rows deleted.

    Elapsed: 00:00:00.00
    ETL@RACTEST> commit;

    Commit complete.
    之后我删除掉2条数据。
    ETL@RACTEST> select b,dbms_rowid.rowid_block_number(rowid) rn from ttt5 order by to_number(b);

            RN
    -- ----------
         47016
         47016
         47016
         47016
         47016
         47016
         47012
    10      47012
    11      47012
    12      47012
    13      47012
    14      47012
    15      47012
    16      47012
    17      47013
    18      47013
    19      47013
    20      47013

    18 rows selected.
    再查可以看到47016块的两条数据已经被释放掉了。
    ETL@RACTEST> insert into ttt5 select 'a','b' from dual connect by level<=10000;

    10000 rows created.

    Elapsed: 00:00:00.08
    ETL@RACTEST> commit;

    Commit complete.
    然后我向表插入大量数据。
    ETL@RACTEST> select b,dbms_rowid.rowid_block_number(rowid) rn from ttt5 where dbms_rowid.rowid_block_number(rowid)=47016;

            RN
    -- ----------
         47016
         47016
         47016
         47016
         47016
         47016

    6 rows selected.
    可以看到47016块并没有被重用。
    4.
    delete释放的存储空间什么时候会被重用?
    再举个例子,建表操作都一样:
    ETL@RACTEST> create table ttt4(a char(800),b CHAR(2));


    Table created.

    Elapsed: 00:00:00.00
    ETL@RACTEST> ETL@RACTEST>  insert into ttt4 select 'a',level from dual connect by level<=20;

    20 rows created.

    Elapsed: 00:00:00.00
    ETL@RACTEST>
    ETL@RACTEST> commit;

    Commit complete.

    Elapsed: 00:00:00.00
    ETL@RACTEST> select b,dbms_rowid.rowid_block_number(rowid) rn from ttt4 order by to_number(b);

            RN
    -- ----------
         47000
         47000
         47000
         47000
         47000
         47000
         47000
         47000
         46996
    10      46996
    11      46996
    12      46996
    13      46996
    14      46996
    15      46996
    16      46996
    17      46997
    18      46997
    19      46997
    20      46997

    20 rows selected.
    这时候我删除3行而不是2
    ETL@RACTEST> delete from ttt4 where b<=3;

    3 rows deleted.

    Elapsed: 00:00:00.00
    ETL@RACTEST> commit;

    Commit complete.
    然后还是插入大量数据
    ETL@RACTEST> insert into ttt4 select 'a','b' from dual connect by level<=10000;

    10000 rows created.

    Elapsed: 00:00:00.49
    ETL@RACTEST> commit;

    Commit complete.
    再查一下:
    ETL@RACTEST> select b,dbms_rowid.rowid_block_number(rowid) rn from ttt4 where dbms_rowid.rowid_block_number(rowid)=47000;

            RN
    -- ----------
         47000
         47000
         47000
         47000
         47000
         47000
         47000
         47000
    可以看到这时47000块被重用了!
    好,我解释一下原因。原来一个表有两个参数:PCT_FREEPCT_USEDPCT_FREE大致为一个块剩余空间占总块的比重,PCT_USED大致为一个块使用的空间占总块的比重。我们来看一下:
    ETL@RACTEST> select dbms_metadata.get_ddl('TABLE','TTT4','ETL') from dual;

    DBMS_METADATA.GET_DDL('TABLE','TTT4','ETL')
    --------------------------------------------------------------------------------

      CREATE TABLE "ETL"."TTT4"
          "A" CHAR(800),
            "B" CHAR(2)
       ) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING
      STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
      PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
      TABLESPACE "TBS_DW_ADMIN_00"
    可以看到TTT4表在建表时没有指定PCTFREE PCTUSED ,默认的值为PCTFREE = 10 PCTUSED = 40
    这两个参数有什么用呢?ITPUB上有人这样解释:
    INSERT
    UPDATE的时候如果BLOCK剩余空间小于PCT_FREE相对应的大小,就从FREELIST中去掉,如果DELETE的时候小于PCT_USED相对应的大小,就加入FREELIST中。
    这样就应该很明确了吧,上面的实验删除TTT52行时,块使用的空间占总空间的比重仍然大于40%,所以块没有被放到freelist中,而TTT4的表删了3行,块使用的空间占总空间的比重小于40%了,所以被放到freelist中,就可以被重用了。好了,真相大白了!
    最后给个建议,Oracle的这两个默认参数并不一定在任何情况下都是最理想的。例如如果我们的表从来不做update操作,那么在建表时PCT_FREE可以设置为0,如果我们想充分利用delete释放的空间,那么PCTUSED可以设置得高一点,70,80,甚至90都可以!

     

  • 相关阅读:
    PHP+JQUEY+AJAX实现分页
    Flume知识扩展
    Flume高级之自定义MySQLSource
    Flume监控之Ganglia
    Flume 概述/企业案例
    Yarn (转自之乎者也)
    MapReduce如何解决数据倾斜?
    JVM调优
    Hive性能优化
    HBase的二级索引
  • 原文地址:https://www.cnblogs.com/wangduqiang/p/4180956.html
Copyright © 2011-2022 走看看