日期:2014-05-16  浏览次数:20464 次

Oracle 高水位线收缩示例

       一. 背景介绍

      我们知道这样一种情况,在oracle中,假设A表原先有1000W行数据,后来删除掉了999W行,只剩下1W行数据的时候,全表扫描A表的时间没有什么变化(删除前后)。这就是oracle里面的高水位线引起的。通俗地讲,假设我们要装1000L水,需要1000个桶,后来我倒掉了999个桶里面的水,但是我没把桶回收。这时,我去找水的时候,仍然要一个一个桶地去寻找。下面这个例子就是为了描述这样一种现象,并介绍怎么解决(也就是把桶回收)


      二. 操作步骤

      1. 命令行以sys用户登录

        

        2. 创建测试表

--创建测试表T
DROP TABLE t;
CREATE TABLE t (
  id NUMBER, 
  n1 NUMBER, 
  n2 NUMBER, 
  pad VARCHAR2(4000)
) tablespace users ;

--插入数据10000行
INSERT INTO t
SELECT rownum AS id,
       1+mod(rownum,251) AS n1,
       1+mod(rownum,251) AS n2,
       dbms_random.string('p',255) AS pad
FROM dual
CONNECT BY level <= 10000
ORDER BY dbms_random.value;

        3. 收集统计信息

--收集表T对象统计信息
BEGIN
  dbms_stats.gather_table_stats(
    ownname          => user,
    tabname          => 'T',
    estimate_percent => 100,
    method_opt       => 'for all columns size skewonly',
    cascade          => TRUE
  );
END;
/

--收集 plan_executetion_statistics(执行计划 执行时候的信息)
ALTER SESSION SET statistics_level = all;

        4. 第一次全表扫描获取的数据行数与逻辑读数

SELECT /*+ full(t) */ * FROM t WHERE n2 = 19;

select *
  from v$sql sqls
 where sqls.SQL_TEXT like '%SELECT /*+ full(t) */ * FROM t WHERE n2 = 19%'
--参数为上一条sql语句查询出的sql_id 
select last_output_rows, last_cr_buffer_gets, last_cu_buffer_gets
  from v$sql_plan_statistics stat
 where stat.SQL_ID = 'chk7agdpy3uqh'
我电脑上显示的是last_output_rows: 40,last_cr_buffer_gets: 436,表示返回40行数据,产生了436个逻辑读(我们这里假设一个逻辑读差不多就是一个块,也就是prefetch参数设置得比较大,使得一个块一次逻辑读就读完了),那么也就是差不多读了436个块。


        5. 删除表中绝大部分数据(大约是9960行)重新查询行数和逻辑读数

 DELETE t WHERE n2 <> 19;
 SELECT /*+ full(t) */ * FROM t WHERE n2 = 19;
 --参数为sql_id
 select last_output_rows, last_cr_buffer_gets, last_cu_buffer_gets
  from v$sql_plan_statistics stat
 where stat.SQL_ID = 'chk7agdpy3uqh'
我电脑上显示的和上一次一样,说明仍然读了436个块。但是已经有绝大部分块没有数据了,完全没必要读取这些没数据的块。


        6. 收缩高水位线

ALTER TABLE t ENABLE ROW MOVEMENT;
ALTER TABLE t SHRINK SPACE;

        7. 第三次进行全表扫描,重新查询行数和逻辑读数
SELECT /*+ full(t) */ * FROM t WHERE n2 = 19;

 select last_output_rows, last_cr_buffer_gets, last_cu_buffer_gets
  from v$sql_plan_statistics stat
 where stat.SQL_ID = 'chk7agdpy3uqh'
我电脑显示的是 last_output_rows: 40,last_cr_buffer_gets: 4 说明这一次只进行了4次逻辑读,已经把那些删除数据的块全部释放了。

       8. drop测试表

DROP TABLE t;
PURGE TABLE t;