最近客户反馈他们delete了包含LOB列的表,通过shrink space 无法降低LOB列的高水位,无法回收空间,进行了测试。
环境说明:
1、数据库版本11.2.0.4.0
2、DB_SECUREFILE=PERMITTED(允许创建SecureFiles(默认值))
测试目的:
通过对表进行delete操作后,想通过move和shrink降低表和LOB列的高水位。
结论:
1、当LOB类型为SecureFile时,通过对表进行shrink 操作,表高水位可以降低,但是LOB列高水位无法降低。
2、当LOB类型为BasicFiles时,通过对表进行shrink 操作,表高水位可以降低,LOB列高水位也可以降低。
3、当LOB类型为SecureFile时,通过对表进行move 操作,表高水位可以降低,但是LOB列高水位无法降低,需要通过MOVE LOB列进行操作降低。
4、当LOB类型为BasicFiles时,通过对表进行move 操作,表高水位可以降低,但是LOB列高水位无法降低,需要通过MOVE LOB列进行操作降低。
5、不管怎么样,LOB列的高水位无法降低,可以通过单独通过move lob列来进行降低高水位。
6、在对LOB列单独MOVE的时候,LOB列的索引不会失效,但是非LOB列的索引会失效!生产上一定要慎重,同时如果做的话,别忘记观察索引状态,及时rebuild索引。
以下是详细的测试过程:
===================== 先进行shrink操作测试 ================
1、创建SECUREFILES 和 BASICFILES类型LOB表
--创建SECUREFILE类型LOB表
CREATE TABLE SECUREFILES_TAB (
id NUMBER,
clob_data CLOB
)
LOB(clob_data) STORE AS SECUREFILE;
--创建BASICFILE类型LOB表(不加LOB(clob_data) STORE AS BASICFILE 这段默认就是BASICFILE类型)
CREATE TABLE BASICFILES_TAB(
id NUMBER,
clob_data CLOB
);
2、创建索引(观察索引失效情况),生产上,可以先考虑插入数据,再创建索引,提升插入效率
create index ind_SECUREFILES_TAB_id on SECUREFILES_TAB(id);
create index ind_BASICFILES_TAB_id on BASICFILES_TAB(id);
SQL> create index ind_SECUREFILES_TAB_id on SECUREFILES_TAB(id);
create index ind_BASICFILES_TAB_id on BASICFILES_TAB(id);
Index created.
SQL>
Index created.
SQL>
3、插入数据
SQL> DECLARE
2 l_clob CLOB := RPAD('X',10000, 'X');
3 BEGIN
4 for i IN 1..1000 LOOP
5 INSERT INTO SECUREFILES_TAB VALUES (i, l_clob);
6 END LOOP;
7 COMMIT;
8 for i IN 1..1000 LOOP
9 INSERT INTO BASICFILES_TAB VALUES (i, l_clob);
10 END LOOP;
11 COMMIT;
12 END;
13 /
PL/SQL procedure successfully completed.
4、检查数据
SQL> SQL>
SQL> select count(*) from SECUREFILES_TAB;
COUNT(*)
----------
1000
SQL> select count(*) from BASICFILES_TAB;
COUNT(*)
----------
1000
5、检查表对应的LOB列
SQL> set linesize 300
col COLUMN_NAME for a20
6、检查索引状态,都是有效的
select owner,index_name,table_name,status from dba_indexes where table_name in ('SECUREFILES_TAB','BASICFILES_TAB');
7、查看表和LOB列大小
SQL> SELECT segment_name, bytes
2 FROM dba_segments
3 WHERE segment_name IN ('SECUREFILES_TAB','BASICFILES_TAB','SYS_LOB0000028976C00002$$','SYS_LOB0000028973C00002$$');
8、对表进行delete 操作
SQL>
delete from SECUREFILES_TAB where id>10;
delete from BASICFILES_TAB where id>10;
990 rows deleted.
SQL>
990 rows deleted.
9、delete 后再次查看表和LOB列大小,没有变化
SQL> SELECT segment_name, bytes
2 FROM user_segments
3 WHERE segment_name IN ('SECUREFILES_TAB','BASICFILES_TAB','SYS_LOB0000028976C00002$$','SYS_LOB0000028973C00002$$');
10、对表进行shrink操作
SQL>
ALTER TABLE YJB.SECUREFILES_TAB ENABLE ROW MOVEMENT;
ALTER TABLE YJB.SECUREFILES_TAB SHRINK SPACE CASCADE;
ALTER TABLE YJB.BASICFILES_TAB ENABLE ROW MOVEMENT;
ALTER TABLE YJB.BASICFILES_TAB SHRINK SPACE CASCADE;
Table altered.
SQL>
Table altered.
SQL> SQL>
Table altered.
SQL>
Table altered.
11、查看对表做shrink后,表和LOB列大小
发现
1、表的大小都变小了,即高水位降低了
2、BASICFILE的LOB列的大小也变小了,但是LOB列类型为SECUREFILE的LOB,大小保持不变。
SQL> SELECT segment_name, bytes
2 FROM dba_segments
3 WHERE segment_name IN ('SECUREFILES_TAB','BASICFILES_TAB','SYS_LOB0000028976C00002$$','SYS_LOB0000028973C00002$$');
12、查看索引状态,通过shrink操作后,都是有效的,主要是由于shrink会自动维护索引。
SQL> select owner,index_name,table_name,status from dba_indexes where table_name in ('SECUREFILES_TAB','BASICFILES_TAB');
13、对无法降低高水位的LOB列进行单独MOVE操作
SQL> alter table SECUREFILES_TAB move lob(clob_data) store as (tablespace users);
Table altered.
=======
14、再次查看LOB列的高水位情况,发现已经下降,同时回收了空间
SQL> SELECT segment_name, bytes
2 FROM user_segments
3 WHERE segment_name IN ('SECUREFILES_TAB','BASICFILES_TAB','SYS_LOB0000028890C00002$$','SYS_LOB0000028893C00002$$');
15、再次检查索引,LOB列的索引状态正常,但是发现非LOB列的索引反而变成:失效状态,这个坑,大家生产上,还是要特别注意。
16、通过rebuild 进行重建后,id列索引恢复正常。
SQL> alter index IND_SECUREFILES_TAB_ID rebuild;
===================== 然后进行MOVE操作测试 ================
1、创建SECUREFILES 和 BASICFILES类型LOB表
创建SECUREFILE类型LOB表
SQL> CREATE TABLE SECUREFILES_TAB (
2 id NUMBER,
3 clob_data CLOB
4 )
5 LOB(clob_data) STORE AS SECUREFILE;
--创建BASICFILE类型LOB表
CREATE TABLE BASICFILES_TAB(
id NUMBER,
clob_data CLOB
)
LOB(clob_data) STORE AS BASICFILE;
Table created.
SQL> SQL> SQL> 2 3 4 5
Table created.
2、创建索引
create index ind_SECUREFILES_TAB_id on SECUREFILES_TAB(id);
create index ind_BASICFILES_TAB_id on BASICFILES_TAB(id);
3、插入数据。
SQL> SQL> SQL> SQL> DECLARE
2 l_clob CLOB := RPAD('X',10000, 'X');
3 BEGIN
4 for i IN 1..1000 LOOP
5 INSERT INTO SECUREFILES_TAB VALUES (i, l_clob);
6 END LOOP;
7 COMMIT;
8 for i IN 1..1000 LOOP
9 INSERT INTO BASICFILES_TAB VALUES (i, l_clob);
10 END LOOP;
11 COMMIT;
12 END;
13 /
PL/SQL procedure successfully completed.
4、查看数据量。
SQL> select count(*) from SECUREFILES_TAB;
select count(*) from BASICFILES_TAB;
COUNT(*)
----------
1000
SQL>
COUNT(*)
----------
1000
SQL>
5、查看表的LOB列
SQL> set linesize 300
col COLUMN_NAME for a20
select owner,table_name,column_name,segment_name,INDEX_NAME,tablespace_name,COMPRESSION,DEDUPLICATION,SECUREFILE from dba_lobs where table_name in ('SECUREFILES_TAB','BASICFILES_TAB');
6、检查索引状态,都是有效的
SQL> SQL> select owner,index_name,table_name,status from dba_indexes where table_name in ('SECUREFILES_TAB','BASICFILES_TAB');
7、检查表、索引大小
SELECT segment_name, bytes
FROM dba_segments
WHERE segment_name IN ('SECUREFILES_TAB','BASICFILES_TAB','SYS_LOB0000029137C00002$$','SYS_LOB0000029134C00002$$');
8、对表进行delete 操作,表和LOB高水位都没有下降。
delete from SECUREFILES_TAB where id>10;
delete from BASICFILES_TAB where id>10;
SELECT segment_name, bytes
FROM dba_segments
WHERE segment_name IN ('SECUREFILES_TAB','BASICFILES_TAB','SYS_LOB0000029137C00002$$','SYS_LOB0000029134C00002$$');
9、MOVE表后,表和索引高水位下降,但是两种类型的LOB对象高水位没有降低
SQL> alter table SECUREFILES_TAB move;
alter table BASICFILES_TAB move;
SQL> SELECT segment_name, bytes
FROM dba_segments
WHERE segment_name IN ('SECUREFILES_TAB','BASICFILES_TAB','SYS_LOB0000029137C00002$$','SYS_LOB0000029134C00002$$');
10、LOB列索引未失效,但是非LOB列的索引失效了(shrink这点就比move要强)。
SQL> select owner,index_name,table_name,status from dba_indexes where table_name in ('SECUREFILES_TAB','BASICFILES_TAB');
11、单独对LOB列进行move操作,可以看到LOB列的高水位已经降低
SQL>
SQL> alter table SECUREFILES_TAB move lob(clob_data) store as (tablespace users);
alter table BASICFILES_TAB move lob(clob_data) store as (tablespace users);
SQL> SELECT segment_name, bytes
2 FROM dba_segments
3 WHERE segment_name IN ('SECUREFILES_TAB','BASICFILES_TAB','SYS_LOB0000028908C00002$$','SYS_LOB0000028911C00002$$','IND_BASICFILES_TAB_ID','IND_SECUREFILES_TAB_ID');
12、最后别忘记把失效的索引rebuild.