目录
一、环境信息
|------|------------------------------------------------------------------------------------------------------------------------------|
| 名称 | 值 |
| CPU | 12th Gen Intel(R) Core(TM) i7-12700H |
| 操作系统 | CentOS Linux release 7.9.2009 (Core) |
| 内存 | 4G |
| 逻辑核数 | 2 |
| DM版本 | 1 DM Database Server 64 V8 2 DB Version: 0x7000c 3 03134284194-20240703-234060-20108 4 Msg Version: 12 5 Gsu level(5) cnt: 0 |
二、介绍
最近在工作中出现分区表膨胀(空洞率高)的问题,一张分区表几个T的数据量,周末空窗期不一定能完成全表的空洞率清理,所以就想到了用交换分区的方式来做指定分区的空洞率清理,下面来分享一下步骤。
三、实验
生产中建议在空窗期内完成如下操作,操作期间建议原分区表没有增删改的操作。
1、测试数据生成
sql
DROP TABLE IF EXISTS P_RANGE_DATE_TAB;
DROP TABLE IF EXISTS P_RANGE_DATE_TAB_COPY;
CREATE TABLE P_RANGE_DATE_TAB(C1 TIMESTAMP, C2 INT)
PARTITION BY RANGE(C1)(
PARTITION P2024 VALUES LESS THAN ('2025-01-01'),
PARTITION P2025 VALUES LESS THAN ('2026-01-01'),
PARTITION P2026 VALUES LESS THAN ('2027-01-01'),
PARTITION PMax VALUES LESS THAN (MAXVALUE)
);
CREATE INDEX IDX_P_RANGE_DATE_TAB_SUN_1 ON P_RANGE_DATE_TAB(C1);
CREATE INDEX IDX_P_RANGE_DATE_TAB_SUN_2 ON P_RANGE_DATE_TAB(C2);
INSERT INTO P_RANGE_DATE_TAB VALUES ('2024-01-01',1);
INSERT INTO P_RANGE_DATE_TAB VALUES ('2025-01-01',5);
INSERT INTO P_RANGE_DATE_TAB VALUES ('2025-01-02',6);
INSERT INTO P_RANGE_DATE_TAB VALUES ('2026-01-01',10);
INSERT INTO P_RANGE_DATE_TAB VALUES ('2028-01-01',15);
INSERT INTO P_RANGE_DATE_TAB SELECT '2025-01-01',LEVEL FROM DUAL CONNECT BY LEVEL <= 10000;
INSERT INTO P_RANGE_DATE_TAB SELECT * FROM P_RANGE_DATE_TAB PARTITION(P2025);
INSERT INTO P_RANGE_DATE_TAB SELECT * FROM P_RANGE_DATE_TAB PARTITION(P2025);
COMMIT;
BEGIN
FOR I IN 1..18 LOOP
INSERT INTO P_RANGE_DATE_TAB SELECT * FROM P_RANGE_DATE_TAB;
UPDATE P_RANGE_DATE_TAB SET C1 = date'2025-01-01' + 1 WHERE C1 = date'2025-01-01';
DELETE FROM P_RANGE_DATE_TAB WHERE C1 = date'2025-01-01' + 1;
INSERT INTO P_RANGE_DATE_TAB SELECT date'2025-01-01',LEVEL FROM DUAL CONNECT BY LEVEL <= 1000;
COMMIT;
END LOOP;
END;
/
2、复制表创建
sql
CREATE TABLE P_RANGE_DATE_TAB_COPY(C1 TIMESTAMP, C2 INT);
CREATE INDEX IDX_P_RANGE_DATE_TAB_COPY_SUN_1 ON P_RANGE_DATE_TAB_COPY(C1);
CREATE INDEX IDX_P_RANGE_DATE_TAB_COPY_SUN_2 ON P_RANGE_DATE_TAB_COPY(C2);
实际生产中,需要把表结构、索引等对象(除了分区信息)都建立上,不然交换分区时,会提示报错:[-7000]:交换对象不匹配。
按照实际情况创建结构,直接管理工具中点击表->属性,就可以获得完整结构,去掉分区即可。
3、复制分区数据
sql
INSERT INTO P_RANGE_DATE_TAB_COPY SELECT * FROM P_RANGE_DATE_TAB PARTITION(P2025);
COMMIT;
SQL按照实际情况修改,表名和分区名。
4、数据量对比
sql
SQL> SELECT COUNT(*) FROM P_RANGE_DATE_TAB PARTITION(P2025);
行号 COUNT(*)
---------- --------------------
1 1000
已用时间: 2.768(毫秒). 执行号:1162.
SQL> SELECT COUNT(*) FROM P_RANGE_DATE_TAB_COPY;
行号 COUNT(*)
---------- --------------------
1 1000
已用时间: 9.616(毫秒). 执行号:1163.
对比一下数据量稳妥一些。
5、复制表统计信息收集
sql
STAT 100 ON P_RANGE_DATE_TAB_COPY(C1);
STAT 100 ON P_RANGE_DATE_TAB_COPY(C2);
后续交换分区之后,统计信息也会变换过去。
6、查看空间占用
sql
SELECT
OWNER,
SEGMENT_NAME,
PARTITION_NAME,
SEGMENT_TYPE,
ROUND(SUM(BYTES) / 1024, 2) AS TOTAL_SIZE
FROM DBA_SEGMENTS
WHERE
OWNER = 'SYSDBA'
AND
SEGMENT_NAME IN ('P_RANGE_DATE_TAB','P_RANGE_DATE_TAB_COPY')
GROUP BY OWNER, SEGMENT_NAME, PARTITION_NAME, SEGMENT_TYPE
ORDER BY OWNER, SEGMENT_NAME, PARTITION_NAME, SEGMENT_TYPE;
行号 OWNER SEGMENT_NAME PARTITION_NAME SEGMENT_TYPE TOTAL_SIZE
---------- ------ --------------------- -------------- --------------- ----------
1 SYSDBA P_RANGE_DATE_TAB P2024 TABLE PARTITION 31488
2 SYSDBA P_RANGE_DATE_TAB P2025 TABLE PARTITION 23680
3 SYSDBA P_RANGE_DATE_TAB P2026 TABLE PARTITION 31488
4 SYSDBA P_RANGE_DATE_TAB PMAX TABLE PARTITION 31488
5 SYSDBA P_RANGE_DATE_TAB_COPY NULL TABLE 256
已用时间: 138.117(毫秒). 执行号:1164.
P2025占用23680K
P_RANGE_DATE_TAB_COPY占用256K
7、查看索引情况
sql
SELECT TABLE_OWNER,TABLE_NAME,TABLE_TYPE,STATUS,OWNER,INDEX_NAME,INDEX_TYPE FROM DBA_INDEXES
WHERE
TABLE_OWNER = 'SYSDBA'
AND
TABLE_NAME IN ('P_RANGE_DATE_TAB','P_RANGE_DATE_TAB_COPY')
ORDER BY TABLE_OWNER,TABLE_NAME;
行号 TABLE_OWNER TABLE_NAME TABLE_TYPE STATUS OWNER INDEX_NAME INDEX_TYPE
---------- ----------- --------------------- ---------- ------ ------ ------------------------------- ----------
1 SYSDBA P_RANGE_DATE_TAB TABLE VALID SYSDBA IDX_P_RANGE_DATE_TAB_SUN_2 NORMAL
2 SYSDBA P_RANGE_DATE_TAB TABLE VALID SYSDBA IDX_P_RANGE_DATE_TAB_SUN_1 NORMAL
3 SYSDBA P_RANGE_DATE_TAB TABLE VALID SYSDBA INDEX33555868 CLUSTER
4 SYSDBA P_RANGE_DATE_TAB_COPY TABLE VALID SYSDBA IDX_P_RANGE_DATE_TAB_COPY_SUN_1 NORMAL
5 SYSDBA P_RANGE_DATE_TAB_COPY TABLE VALID SYSDBA INDEX33555883 CLUSTER
6 SYSDBA P_RANGE_DATE_TAB_COPY TABLE VALID SYSDBA IDX_P_RANGE_DATE_TAB_COPY_SUN_2 NORMAL
6 rows got
个数一半一半说明我们索引建全了,且都是有效的。
8、查看统计信息
sql
SELECT OWNER,OBJECT_NAME,ID,COLID,T_FLAG,T_TOTAL,N_SAMPLE FROM SYSSTATS A
INNER JOIN
( SELECT OBJECT_ID,OWNER,OBJECT_NAME FROM DBA_OBJECTS
WHERE
OWNER = 'SYSDBA'
AND
OBJECT_NAME IN ('P_RANGE_DATE_TAB','P_RANGE_DATE_TAB_COPY')) B
ON A.ID = B.OBJECT_ID
ORDER BY OWNER,OBJECT_NAME,ID,COLID;
行号 OWNER OBJECT_NAME ID COLID T_FLAG T_TOTAL N_SAMPLE
---------- ------ --------------------- ----------- ----------- ------ -------------------- --------------------
1 SYSDBA P_RANGE_DATE_TAB_COPY 1193 0 C 1000 1000
2 SYSDBA P_RANGE_DATE_TAB_COPY 1193 1 C 1000 1000
已用时间: 3.047(毫秒). 执行号:1170.
我们可以看到复制表的统计信息。
原表我们没有收集,所以没有显示,是正常情况。
9、交换分区
sql
ALTER TABLE P_RANGE_DATE_TAB EXCHANGE PARTITION P2025 WITH TABLE P_RANGE_DATE_TAB_COPY;
按照实际情况改表名和分区名,别选错分区,导致分区数据错乱。
10、数据对比
sql
SQL> SELECT COUNT(*) FROM P_RANGE_DATE_TAB PARTITION(P2025);
行号 COUNT(*)
---------- --------------------
1 1000
已用时间: 7.302(毫秒). 执行号:1172.
SQL> SELECT COUNT(*) FROM P_RANGE_DATE_TAB_COPY;
行号 COUNT(*)
---------- --------------------
1 1000
已用时间: 11.796(毫秒). 执行号:1173.
除了数据量的对比,数据内容也建议大致看一下,是否正确。
11、查看空间占用
sql
SELECT
OWNER,
SEGMENT_NAME,
PARTITION_NAME,
SEGMENT_TYPE,
ROUND(SUM(BYTES) / 1024, 2) AS TOTAL_SIZE
FROM DBA_SEGMENTS
WHERE
OWNER = 'SYSDBA'
AND
SEGMENT_NAME IN ('P_RANGE_DATE_TAB','P_RANGE_DATE_TAB_COPY')
GROUP BY OWNER, SEGMENT_NAME, PARTITION_NAME, SEGMENT_TYPE
ORDER BY OWNER, SEGMENT_NAME, PARTITION_NAME, SEGMENT_TYPE;
行号 OWNER SEGMENT_NAME PARTITION_NAME SEGMENT_TYPE TOTAL_SIZE
---------- ------ --------------------- -------------- --------------- ----------
1 SYSDBA P_RANGE_DATE_TAB P2024 TABLE PARTITION 31488
2 SYSDBA P_RANGE_DATE_TAB P2025 TABLE PARTITION 1024
3 SYSDBA P_RANGE_DATE_TAB P2026 TABLE PARTITION 31488
4 SYSDBA P_RANGE_DATE_TAB PMAX TABLE PARTITION 31488
5 SYSDBA P_RANGE_DATE_TAB_COPY NULL TABLE 4352
已用时间: 100.927(毫秒). 执行号:1174.
|-----------------------|--------|-------|
| 名称 | 之前 | 如今 |
| P2025 | 23680K | 1024K |
| P_RANGE_DATE_TAB_COPY | 256K | 4252K |
P2025空间缩小为原有的23分之1,空间得到了清理。
12、查看索引情况
sql
SELECT TABLE_OWNER,TABLE_NAME,TABLE_TYPE,STATUS,OWNER,INDEX_NAME,INDEX_TYPE FROM DBA_INDEXES
WHERE
TABLE_OWNER = 'SYSDBA'
AND
TABLE_NAME IN ('P_RANGE_DATE_TAB','P_RANGE_DATE_TAB_COPY')
ORDER BY TABLE_OWNER,TABLE_NAME;
行号 TABLE_OWNER TABLE_NAME TABLE_TYPE STATUS OWNER INDEX_NAME INDEX_TYPE
---------- ----------- --------------------- ---------- ------ ------ ------------------------------- ----------
1 SYSDBA P_RANGE_DATE_TAB TABLE VALID SYSDBA IDX_P_RANGE_DATE_TAB_SUN_2 NORMAL
2 SYSDBA P_RANGE_DATE_TAB TABLE VALID SYSDBA IDX_P_RANGE_DATE_TAB_SUN_1 NORMAL
3 SYSDBA P_RANGE_DATE_TAB TABLE VALID SYSDBA INDEX33555868 CLUSTER
4 SYSDBA P_RANGE_DATE_TAB_COPY TABLE VALID SYSDBA IDX_P_RANGE_DATE_TAB_COPY_SUN_1 NORMAL
5 SYSDBA P_RANGE_DATE_TAB_COPY TABLE VALID SYSDBA INDEX33555870 CLUSTER
6 SYSDBA P_RANGE_DATE_TAB_COPY TABLE VALID SYSDBA IDX_P_RANGE_DATE_TAB_COPY_SUN_2 NORMAL
6 rows got
个数和之前一样,且都是有效的。
13、查看统计信息
sql
SELECT OWNER,OBJECT_NAME,ID,COLID,T_FLAG,T_TOTAL,N_SAMPLE FROM SYSSTATS A
INNER JOIN
( SELECT OBJECT_ID,OWNER,OBJECT_NAME FROM DBA_OBJECTS
WHERE
OWNER = 'SYSDBA'
AND
OBJECT_NAME IN ('P_RANGE_DATE_TAB','P_RANGE_DATE_TAB_COPY')) B
ON A.ID = B.OBJECT_ID
ORDER BY OWNER,OBJECT_NAME,ID,COLID;
行号 OWNER OBJECT_NAME ID COLID T_FLAG T_TOTAL N_SAMPLE
---------- ------ ---------------- ----------- ----------- ------ -------------------- --------------------
1 SYSDBA P_RANGE_DATE_TAB 1190 0 C 1000 1000
2 SYSDBA P_RANGE_DATE_TAB 1190 1 C 1000 1000
我们可以看到原表的统计信息,说明统计信息也随着交换分区,转移过来了。
14、删除复制表
sql
DROP TABLE IF EXISTS P_RANGE_DATE_TAB_COPY;
这一步建议在业务老师测试验证之后,再做删除操作。