Oracle中的高水位线

高水位线(HWM)在Oracle数据库中标识段的状态,指示已分配但未使用的块位置。块可能处于未格式化、已分配未格式化、有数据或无数据(由delete操作造成)的状态。HWM通常只升不降,除非执行特定操作如truncate、move或shrink。全表扫描会检查Low HWM以下所有块,即使无数据,导致碎片影响性能。HWM存在的原因及为何全表扫描包含无数据块,目前缺乏明确解释。
摘要由CSDN通过智能技术生成

高水位线(High Water Mark, HWM)类似于一个指针,用来标识分配给段(segment)的块(block)状态。块是Oracle中数据分配和操作的最小单位,段是类似于表、索引这样的数据库实体。块有下面几种状态:

  • 在HWM之上,块是未格式化和未使用的(unformated and unused)
  • 在HWM之下,块又有下面几种状态:
    • 分配的(allocated),但是还未格式化
    • 格式化并且存有数据
    • 格式化但是没有数据,delete操作会造成这种状态

另有一个低水位线的概念(Low HWM),处于Low HWM之下的块都是格式化的,处于Low HWM和HWM之间有可能格式化也可能未格式化。
示意图如下,

-- =============================================================
-- <-formated-->Low HWM<----allocated----->hwm<-----empty---->total
-- ================^========================^===================^

HWM有如下特点:

  • 一般情况下只升不降,只有在truncate、move、shrink等操作才会降低,delete不会降低,delete后留下的空间,以后insert可以使用。
  • 执行全表扫描时,数据库扫描Low HWM以下所有的块,不管有没有数据,读取两个水位线之间的块时则要小心一点,因为这中间的块并不一定都是格式化的。如果频繁的删除插入操作,会在HWM下的块中留下大量碎片,影响性能。
  • 当insert和update的时候,数据库在Low HWM和HWM之间或者Low HWM之下的空余空间进行写入。

可是为什么要设置一个HWM呢?既然有了一个位图块记录块的状态,为什么要设置一个水位线,全表扫描时还要扫描下面的所有块,而不是那些有数据的?这不是出力不讨好吗,百度了半天,没找到满意的答案,基本上大家都是说“它是这样的”,而没有说“它为什么是这样的”,知道的告诉一声。

下面是检测表水位线的一个小程序:

PROCEDURE P_TABLE_HWM_ANALYSE(TABLE_NAME IN VARCHAR2) IS
    -- 表占用空间高水位检测
    LVC_TABLE_TMP        VARCHAR2(50);
    LVC_MB               NUMBER; -- 表的大小MB
    LVC_TOTAL            NUMBER; -- 总的block数
    LVC_BLOCKS           NUMBER; -- 水位线,即hwm
    LVC_EMPTY_BLOCKS     NUMBER; -- 空余block
    LVC_USED             NUMBER; -- 使用的block
    LVC_USED_PERCENT     NUMBER; -- 使用百分比,LVC_USED/LVC_TOTAL
    LVC_HWM_PERCENT      NUMBER; -- 水位线百分比,LVC_BLOCKS/LVC_TOTAL
    LVC_USED_PERCENT_POS NUMBER;
    LVC_HWM_PERCENT_POS  NUMBER;
  BEGIN
    LVC_TABLE_TMP := UPPER(TABLE_NAME);

    EXECUTE IMMEDIATE 'ANALYZE TABLE ' || LVC_TABLE_TMP ||
                      ' ESTIMATE STATISTICS';

    SELECT ROUND(SUM(DECODE(BYTES, NULL, 0, BYTES)) / 1024 / 1024, 1),
           SUM(DECODE(BLOCKS, NULL, 0, BLOCKS))
      INTO LVC_MB, LVC_TOTAL
      FROM USER_SEGMENTS
     WHERE SEGMENT_NAME = LVC_TABLE_TMP
     GROUP BY SEGMENT_NAME;

    SELECT DECODE(BLOCKS, NULL, 0, BLOCKS),
           DECODE(EMPTY_BLOCKS, NULL, 0, EMPTY_BLOCKS)
      INTO LVC_BLOCKS, LVC_EMPTY_BLOCKS
      FROM USER_TABLES
     WHERE TABLE_NAME = LVC_TABLE_TMP;

    EXECUTE IMMEDIATE 'SELECT COUNT(DISTINCT DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
                        || DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID)) FROM ' ||
                      LVC_TABLE_TMP
      INTO LVC_USED;

    IF LVC_TOTAL = 0 THEN
      LVC_USED_PERCENT := 0;
      LVC_HWM_PERCENT  := 0;
    ELSE
      LVC_USED_PERCENT := ROUND(LVC_USED / LVC_TOTAL * 100);
      LVC_HWM_PERCENT  := ROUND(LVC_BLOCKS / LVC_TOTAL * 100);
    END IF;

    IF LVC_USED_PERCENT < 6 THEN
      LVC_USED_PERCENT_POS := 3;
    ELSE
      LVC_USED_PERCENT_POS := ROUND(LVC_USED_PERCENT / 2);
    END IF;

    IF LVC_HWM_PERCENT < 6 THEN
      LVC_HWM_PERCENT_POS := 3;
    ELSE
      LVC_HWM_PERCENT_POS := ROUND(LVC_HWM_PERCENT / 2);
    END IF;
    DBMS_OUTPUT.PUT_LINE(RPAD(LVC_TABLE_TMP || ': ' || LVC_MB || 'MB', 50) ||
                         LPAD('U: Used, H: HWM, T: Total', 52));

        DBMS_OUTPUT.PUT_LINE(LPAD('=', 102, '='));
    DBMS_OUTPUT.PUT_LINE(LPAD(LVC_USED_PERCENT || '%',
                              LVC_USED_PERCENT_POS));
    DBMS_OUTPUT.PUT_LINE('|' || LPAD('|U', LVC_USED_PERCENT + 2, '-'));
    DBMS_OUTPUT.PUT_LINE(LPAD(LVC_HWM_PERCENT || '%', LVC_HWM_PERCENT_POS));
    DBMS_OUTPUT.PUT_LINE('|' || LPAD('|H', LVC_HWM_PERCENT + 2, '-'));
        DBMS_OUTPUT.PUT_LINE('');
    DBMS_OUTPUT.PUT_LINE(LPAD('T', 103, '='));

  EXCEPTION
    WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(LVC_TABLE_TMP || ', ' || SQLERRM);

  END P_TABLE_HWM_ANALYSE;

测试一下,

DM2_LDCX_SY: 10MB                                                            U: Used, H: HWM, T: Total
======================================================================================================
                                         88%
|----------------------------------------------------------------------------------------|U
                                              98%
|--------------------------------------------------------------------------------------------------|H

======================================================================================================T
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值