Oracle 分区表降低高水位(HWM)笔记

一、高水位线(HWM)基础

1. 定义

Oracle 中每个段(segment)都有容纳数据的上限即高水位线(HWM),HWM 表示段中未被使用的数据块数量,仅上涨不下跌(truncate 操作除外),即使删除表中全部数据,HWM 仍保持原值。

2. 影响

  • 全表扫描会读取 HWM 以下所有数据块,即使无数据,导致性能低下;
  • 插入数据使用 append 关键字时,会使用 HWM 以上数据块并自动抬高 HWM。

二、通用降低高水位线的方法

方法 语法 注意事项
表移动 alter table table_name move [tablespace tablespace_name]; 批量生成脚本: `select 'alter index
表收缩 alter table table_name enable row movement; alter table table_name shrink space; 需先开启行移动 对分区表可按分区收缩
表重建 create table t1 as select * from t; drop table t; alter table t1 rename to t; 需重新创建索引、约束等
emp/imp - 逻辑导出导入,重建表结构
释放未使用空间 alter table table_name deallocate unused; 仅释放 HWM 以上空间,无法释放以下空闲空间
truncate truncate table_name; 会清空表数据,重置 HWM,但数据不可恢复

三、分区表降低高水位线实操

场景1:使用 shrink space 收缩

1. 环境模拟
sql 复制代码
-- 创建分区表
create table scott.t_par
(id number,
inc_datetime varchar2(19),
random_id number,
random_string varchar2(60)
) 
partition by range (id) 
(
partition p_01 values less than(200000),
partition p_02 values less than(400000),
partition p_03 values less than(600000),
partition p_04 values less than(800000),
partition p_05 values less than(1000000),
partition p_max values less than(maxvalue)
);

-- 插入测试数据
insert into t_par
select rownum as id,
to_char(sysdate + rownum/24/3600, 'yyyy-mm-dd hh24:mi:ss') as inc_datetime,
trunc(dbms_random.value(0, 100)) as random_id,
dbms_random.string('x', 20) random_string
from dual
connect by level <= 1500000;
commit;

-- 查询数据分布
select count(*) from t_par partition(p_01);
select count(*) from t_par partition(p_02);
select count(*) from t_par partition(p_03);
select count(*) from t_par partition(p_04);
select count(*) from t_par partition(p_05);
select count(*) from t_par partition(p_max);

-- 收集统计信息
exec DBMS_STATS.GATHER_TABLE_STATS(ownname=>'SCOTT',tabname=>'T_PAR',ESTIMATE_PERCENT=>dbms_stats.auto_sample_size,method_opt=>'for all indexed columns',cascade=>true,force=>true,degree=>10);

-- 查看高水位
select table_name,partition_name,
ROUND ( (blocks * 8), 2) "高水位空间 k",
ROUND ( (num_rows * avg_row_len / 1024), 2) "真实使用空间 k",
ROUND ( (blocks * 10 / 100) * 8, 2) "预留空间(pctfree) k",
ROUND ( (blocks * 8 - (num_rows * avg_row_len / 1024) - blocks * 8 * 10 / 100),2) "浪费空间 k"
from dba_tab_partitions where lower(table_name)='t_par'
ORDER BY 5 DESC;
2. 模拟高水位(删除偶数ID数据)
sql 复制代码
delete from t_par where mod(id,2) = 0;
commit;

-- 再次收集统计信息并查看高水位
exec DBMS_STATS.GATHER_TABLE_STATS(ownname=>'SCOTT',tabname=>'T_PAR',ESTIMATE_PERCENT=>dbms_stats.auto_sample_size,method_opt=>'for all indexed columns',cascade=>true,force=>true,degree=>10);

select table_name,partition_name,
ROUND ( (blocks * 8), 2) "高水位空间 k",
ROUND ( (num_rows * avg_row_len / 1024), 2) "真实使用空间 k",
ROUND ( (blocks * 10 / 100) * 8, 2) "预留空间(pctfree) k",
ROUND ( (blocks * 8 - (num_rows * avg_row_len / 1024) - blocks * 8 * 10 / 100),2) "浪费空间 k"
from dba_tab_partitions where lower(table_name)='t_par'
ORDER BY 5 DESC;
3. 降低高水位
sql 复制代码
-- 开启行移动
alter table t_par enable row movement;

-- 按分区收缩
alter table t_par modify partition p_01 shrink space cascade; 
alter table t_par modify partition p_02 shrink space cascade;
alter table t_par modify partition p_03 shrink space cascade;
alter table t_par modify partition p_04 shrink space cascade;
alter table t_par modify partition p_05 shrink space cascade;
alter table t_par modify partition p_max shrink space cascade;

-- 收集统计信息后查看高水位
exec DBMS_STATS.GATHER_TABLE_STATS(ownname=>'SCOTT',tabname=>'T_PAR',ESTIMATE_PERCENT=>dbms_stats.auto_sample_size,method_opt=>'for all indexed columns',cascade=>true,force=>true,degree=>10);

select table_name,partition_name,
ROUND ( (blocks * 8), 2) "高水位空间 k",
ROUND ( (num_rows * avg_row_len / 1024), 2) "真实使用空间 k",
ROUND ( (blocks * 10 / 100) * 8, 2) "预留空间(pctfree) k",
ROUND ( (blocks * 8 - (num_rows * avg_row_len / 1024) - blocks * 8 * 10 / 100),2) "浪费空间 k"
from dba_tab_partitions where lower(table_name)='t_par'
ORDER BY 5 DESC;

场景2:使用 move tablespace 移动

1. 环境模拟(含索引创建)
sql 复制代码
-- 创建分区表(同场景1)
create table scott.t_par
(id number,
inc_datetime varchar2(19),
random_id number,
random_string varchar2(60)
) 
partition by range (id) 
(
partition p_01 values less than(200000),
partition p_02 values less than(400000),
partition p_03 values less than(600000),
partition p_04 values less than(800000),
partition p_05 values less than(1000000),
partition p_max values less than(maxvalue)
);

-- 插入测试数据(同场景1)
insert into t_par
select rownum as id,
to_char(sysdate + rownum/24/3600, 'yyyy-mm-dd hh24:mi:ss') as inc_datetime,
trunc(dbms_random.value(0, 100)) as random_id,
dbms_random.string('x', 20) random_string
from dual
connect by level <= 1500000;
commit;

-- 创建全局索引和本地索引
-- 全局索引
create index idx_t_par on t_par(random_id) global 
partition by range (random_id) 
(
partition p_01 values less than(200000),
partition p_02 values less than(400000),
partition p_03 values less than(600000),
partition p_04 values less than(800000),
partition p_05 values less than(1000000),
partition p_max values less than(maxvalue)
);

-- 本地索引
create index idx_t_par_2 on t_par(random_string) local (partition p01,partition p02,partition p03,partition p04,partition p05,partition p_max);

-- 查询数据分布、收集统计信息、查看索引/高水位(同场景1)
select count(*) from t_par partition(p_01);
select count(*) from t_par partition(p_02);
select count(*) from t_par partition(p_03);
select count(*) from t_par partition(p_04);
select count(*) from t_par partition(p_05);
select count(*) from t_par partition(p_max);

exec DBMS_STATS.GATHER_TABLE_STATS(ownname=>'SCOTT',tabname=>'T_PAR',ESTIMATE_PERCENT=>dbms_stats.auto_sample_size,method_opt=>'for all indexed columns',cascade=>true,force=>true,degree=>10);

-- 查看索引统计信息
select t2.table_name,
       t1.index_name,
       t1.partition_name,
       t1.last_analyzed,
       t1.blevel,
       t1.num_rows,
       t1.leaf_blocks,
       t1.status
  from dba_ind_partitions t1, user_indexes t2
 where t1.index_name = t2.index_name
   and t2.table_name = 'T_PAR';

-- 查看高水位
select table_name,partition_name,
ROUND ( (blocks * 8), 2) "高水位空间 k",
ROUND ( (num_rows * avg_row_len / 1024), 2) "真实使用空间 k",
ROUND ( (blocks * 10 / 100) * 8, 2) "预留空间(pctfree) k",
ROUND ( (blocks * 8 - (num_rows * avg_row_len / 1024) - blocks * 8 * 10 / 100),2) "浪费空间 k"
from dba_tab_partitions where lower(table_name)='t_par'
ORDER BY 5 DESC;
2. 模拟高水位(删除偶数ID数据)
sql 复制代码
delete from t_par where mod(id,2) = 0;
commit;

-- 收集统计信息并查看高水位
exec DBMS_STATS.GATHER_TABLE_STATS(ownname=>'SCOTT',tabname=>'T_PAR',ESTIMATE_PERCENT=>dbms_stats.auto_sample_size,method_opt=>'for all indexed columns',cascade=>true,force=>true,degree=>10);

select table_name,partition_name,
ROUND ( (blocks * 8), 2) "高水位空间 k",
ROUND ( (num_rows * avg_row_len / 1024), 2) "真实使用空间 k",
ROUND ( (blocks * 10 / 100) * 8, 2) "预留空间(pctfree) k",
ROUND ( (blocks * 8 - (num_rows * avg_row_len / 1024) - blocks * 8 * 10 / 100),2) "浪费空间 k"
from dba_tab_partitions where lower(table_name)='t_par'
ORDER BY 5 DESC;
3. 降低高水位(move + 重建索引)
sql 复制代码
-- 生成并执行分区move脚本
select 'alter table '||TABLE_OWNER||'.'||TABLE_NAME||' move partition '||PARTITION_NAME|| ' tablespace SCOTT; ' from DBA_tab_partitions WHERE TABLE_OWNER='SCOTT' and TABLE_NAME='T_PAR';

alter table SCOTT.T_PAR move partition P_MAX tablespace SCOTT; 
alter table SCOTT.T_PAR move partition P_05 tablespace SCOTT; 
alter table SCOTT.T_PAR move partition P_04 tablespace SCOTT; 
alter table SCOTT.T_PAR move partition P_03 tablespace SCOTT; 
alter table SCOTT.T_PAR move partition P_02 tablespace SCOTT; 
alter table SCOTT.T_PAR move partition P_01 tablespace SCOTT; 

-- 查看索引状态(已失效)
select t2.table_name,
       t1.index_name,
       t1.partition_name,
       t1.last_analyzed,
       t1.blevel,
       t1.num_rows,
       t1.leaf_blocks,
       t1.status
  from dba_ind_partitions t1, user_indexes t2
 where t1.index_name = t2.index_name
   and t2.table_name = 'T_PAR';

-- 重建索引(区分分区/非分区索引)
begin
  for c1 in (select t.index_name, t.partitioned from user_indexes t where table_name = 'T_PAR')
  loop
    if c1.partitioned='NO' then
      -- 重建非分区全局索引
      execute immediate 'alter index ' || c1.index_name || ' rebuild';
    else
      -- 重建分区索引的失效分区
      for c2 in (select partition_name from user_ind_partitions where index_name=c1.index_name and status = 'UNUSABLE')
      loop
        execute immediate 'alter index ' || c1.index_name || ' rebuild partition ' || c2.partition_name;
      end loop;
    end if;
  end loop;
end;
/

-- 如有LOB字段,单独move
-- alter table BDHI.TEST move lob(A) store as (tablespace IDWD_TBS002);

-- 收集统计信息
exec DBMS_STATS.GATHER_TABLE_STATS(ownname=>'SCOTT',tabname=>'T_PAR',ESTIMATE_PERCENT=>dbms_stats.auto_sample_size,method_opt=>'for all indexed columns',cascade=>true,force=>true,degree=>10);

-- 查看索引状态和高水位
select t2.table_name,
       t1.index_name,
       t1.partition_name,
       t1.last_analyzed,
       t1.blevel,
       t1.num_rows,
       t1.leaf_blocks,
       t1.status
  from dba_ind_partitions t1, user_indexes t2
 where t1.index_name = t2.index_name
   and t2.table_name = 'T_PAR';

select table_name,partition_name,
ROUND ( (blocks * 8), 2) "高水位空间 k",
ROUND ( (num_rows * avg_row_len / 1024), 2) "真实使用空间 k",
ROUND ( (blocks * 10 / 100) * 8, 2) "预留空间(pctfree) k",
ROUND ( (blocks * 8 - (num_rows * avg_row_len / 1024) - blocks * 8 * 10 / 100),2) "浪费空间 k"
from dba_tab_partitions where lower(table_name)='t_par'
ORDER BY 5 DESC;

四、关键注意事项

  1. shrink space :需开启行移动,对分区表可按分区操作,不影响索引(cascade 会同步收缩索引);
  2. move tablespace:移动后索引失效,需重建(分区索引需按分区重建);有 LOB 字段需单独移动;
  3. 统计信息:操作后需收集统计信息,保证执行计划准确性;
  4. truncate:重置 HWM 但清空数据,生产环境需谨慎使用;
  5. deallocate unused:仅释放 HWM 以上空间,无法解决 HWM 以下空闲问题。
相关推荐
oi..9 小时前
python Get/Post请求练习
开发语言·经验分享·笔记·python·程序人生·安全·网络安全
努力学习的小廉9 小时前
redis学习笔记(九)—— Redis 持久化
redis·笔记·学习
-Da-10 小时前
【操作系统学习日记】并发编程中的竞态条件与同步机制:互斥锁与信号量
java·服务器·javascript·数据库·系统架构
Predestination王瀞潞10 小时前
Base Tools-Associate-Fifth:re库详解
数据库·mysql
Ricky_Theseus10 小时前
SQL Server2008 select语句基本语法
数据库·sql
网络工程小王10 小时前
【Python数据分析基础】
大数据·数据库·人工智能·学习
busideyang11 小时前
函数指针类型定义笔记
c语言·笔记·stm32·单片机·算法·嵌入式
Fortune7911 小时前
用Pandas处理时间序列数据(Time Series)
jvm·数据库·python
2401_8785302111 小时前
高级爬虫技巧:处理JavaScript渲染(Selenium)
jvm·数据库·python
2401_8735449211 小时前
使用Black自动格式化你的Python代码
jvm·数据库·python