如何使用功能索引在 table 上删除行后清理未使用的 space?
How to clean unused space after rows deletion on table with functional indexes?
如何正确清洁tablespace?
例如,我们有一个 table 具有数百万行和功能索引。我们要删除 table.
的大部分
为此我们调用了:从 some_table 中删除....
下一步是什么?
这个顺序正确吗?
1. 删除功能索引。
2. 改变some_table 收缩space。
3. 重新创建函数索引
- 创建table新table
select * 从旧table 哪里...
- 降旧table
- 将新table重命名为旧table
- 重新创建索引。
正如您可能意识到的(或者您不会特别询问 function-based 索引),您不能简单地:
alter table mytable enable row movement;
alter table mytable shrink space;
alter table mytable disable row movement;
尝试这样做将导致:
ORA-10631: SHRINK clause should not be specified for this object
(注意:此限制也适用于位图连接索引。)
很明显,你可以先放弃 FBI...
drop index my_fbi_index;
alter table mytable enable row movement;
alter table mytable shrink space;
alter table mytable disable row movement;
create index my_fbi_index... online;
但这不是在线操作。您的应用程序将在短时间内受到缺少 function-based-index 的影响。
如果您需要在线操作,并且您使用的是 Oracle 12.2 或更高版本,您可以试试这个:
alter table mytable move online;
(alter table...move
(无 "online")在 12.2 之前可用,但它不是在线操作,它会删除您的索引段,留下标记为 "unusable" 的索引并需要你重建它们。所以,在 12.2 之前并不是一个好的选择。)
最佳方式
创建一个只包含有效数据的新table,然后重新创建索引,然后删除旧的table.
- 使用过滤后的数据创建新的 table,其中 table_name 是 table 的名称,filter_text 是以 'WHERE ...' 开头的条件,并且 partitionText 是一个分区子句,如果您有一个用于 table 的子句,例如
RANGE (ENDEDAT) INTERVAL ( NUMTODSINTERVAL(1,''day'') ) ( PARTITION p_first VALUES LESS THAN ( TO_DATE(''01-01-2010'',''dd-MM-yyyy'') ) ) ENABLE ROW MOVEMENT
sqlCommand := 'create table ' || table_name ||'_TMP
tablespace &TBS_NORMAL_TABLES initrans 32 ' || partitionText ||'
nologging
AS (SELECT * FROM '||table_name|| ' ' ||filter_text||')';
EXECUTE IMMEDIATE sqlCommand;
- 将属性添加到新 table。
例如,约束、索引...这些可以从 built-in table 中收集,例如 all_constraints、all_indexes。属性的移动也可以自动化,只需要应用一些重命名技巧。
- 换旧换新table
execute immediate 'ALTER TABLE &Schemaowner..'||v_table_name||' RENAME TO '||v_table_name||'_OT';
execute immediate 'ALTER TABLE &Schemaowner..'||v_table_name||'_TP'||' RENAME TO '||v_table_name;
- 放弃旧的table
execute immediate 'DROP TABLE '||v_table_name||'_OT';
TL;关于缩减和重组的 DR 信息 tables
当考虑在实时生产数据库上归档大量数据时,这里有一些关于我的调查的信息和有用的链接。
自动缩小一些 table 并处理错误的方法
for i in (SELECT obj.owner,obj.table_name,(CASE WHEN NVL(idx.cnt, 0) < 1 THEN 'Y' ELSE 'N' END) as shrinkable, row_movement
FROM all_tables obj,
(SELECT table_name, COUNT(rownum) cnt
FROM user_indexes
WHERE index_type LIKE 'FUN%'
GROUP BY table_name) idx
WHERE obj.table_name = idx.table_name(+)
AND obj.owner = &Schemaowner
and obj.table_name like 'T_%' and obj.table_name not like 'TMP_%'
and NVL(idx.cnt,0) < 1)
loop
BEGIN
if i.row_movement='ENABLED' then
execute immediate 'alter table '||i.table_name||' shrink space';
else
execute immediate 'alter table '||i.table_name||' enable row movement';
execute immediate 'alter table '||i.table_name||' shrink space';
execute immediate 'alter table '||i.table_name||' disable row movement';
end if;
DBMS_OUTPUT.PUT_LINE('shrinked table: ' || i.table_name);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('error while shrinking table: ' || i.table_name);
DBMS_OUTPUT.PUT_LINE (SQLERRM);
DBMS_OUTPUT.PUT_LINE (SQLCODE);
if SQLCODE=-10635 then
for p in (SELECT partition_name ,tablespace_name FROM user_tab_partitions WHERE table_name = 'SOME_PARTITIONED_TABLE')
loop
BEGIN
execute immediate 'alter table '||i.table_name||' MOVE PARTITION ' || p.partition_name || ' ONLINE TABLESPACE ' || p.tablespace_name || ' COMPRESS UPDATE INDEXES';
DBMS_OUTPUT.PUT_LINE('moved partition: ' || p.partition_name);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('error while moving partition: ' || p.partition_name);
DBMS_OUTPUT.PUT_LINE (SQLERRM);
DBMS_OUTPUT.PUT_LINE (SQLCODE);
CONTINUE;
END;
end loop;
end if;
CONTINUE;
END;
end loop;
有用的选择
可收缩tables
SELECT obj.owner
,obj.table_name
,(CASE WHEN NVL(idx.cnt, 0) < 1 THEN 'Y' ELSE 'N' END) as shrinkable
FROM all_tables obj,
(SELECT table_name, COUNT(rownum) cnt
FROM user_indexes
WHERE index_type LIKE 'FUN%'
GROUP BY table_name) idx
WHERE obj.table_name = idx.table_name(+)
AND NVL(idx.cnt,0) < 1
and obj.owner='YOUR_SCHEMA_OWNER'
使收缩命令无法执行的索引table
SELECT *
FROM all_indexes
WHERE index_type LIKE 'FUN%'
and owner='YOUR_SCHEMA_OWNER'
可缩table不妥协
SELECT obj.owner
,obj.table_name
,(CASE WHEN NVL(idx.cnt, 0) < 1 THEN 'Y' ELSE 'N' END) as shrinkable
FROM all_tables obj,
(SELECT table_name, COUNT(rownum) cnt
FROM user_indexes
WHERE index_type LIKE 'FUN%'
GROUP BY table_name) idx
WHERE obj.table_name = idx.table_name(+)
AND NVL(idx.cnt,0) < 1
--and obj.table_name like 'T_%' and obj.table_name not like 'TMP_%'
and obj.compression != 'ENABLED'
and obj.table_name not in (SELECT table_name FROM user_tab_partitions WHERE compression = 'ENABLED')
and obj.owner='YOUR_SCHEMA_OWNER'
压缩问题,压缩 tables and/or 个分区
SELECT table_name,compression, compress_for FROM user_tables WHERE compression = 'ENABLED'
SELECT table_name,partition_name, compression, compress_for FROM user_tab_partitions WHERE compression = 'ENABLED' ORDER BY 1
检查这些 before/after 收缩,测试它的地狱
select segment_name,bytes/1024/1024 as mb,blocks from user_segments where segment_name='TABLE_NAME'
在我的例子中,我创建了一个包含几百万行的 table(未分区),然后删除了其中的 1/3,结果如下:
|| BYTES || BLOCKS ||
Before deletion || 105250816 || 12848 ||
After deletion || 78774272 || 9616 ||
调查和side-effects
可能的副作用
https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:9536157800346457013
...什么时候应该使用重组:
http://www.dba-oracle.com/t_table_fragmentation.htm
...启用行移动而收缩space可以重新排序行(这意味着如果使用基于ROWID工作或选择或类似的东西,那么可能会有一些惊喜)
- 创建您的 table
的副本
- 降旧table
- 重命名副本
有一个缺点,在此操作期间(可能需要一些时间)您的应用程序不可用。
所以,你的方法
- 删除函数索引。
- 改变some_table收缩space.
- 再次创建函数索引。
是正确的。
如果你有一个分区 table,请看这个:https://dba.stackexchange.com/questions/162415/how-to-shrink-space-on-table-with-a-function-based-index
我还没有看到有人提出过另一种选择。发出 DELETE,然后什么都不做。为什么我们认为我们需要重建索引?为什么我们认为我们需要调整 table 的大小?如果我们在 DELETE 之后什么都不做,table 的所有范围仍然分配给 table 并将用于未来的插入。如果你有一家零售店并进行清仓销售,导致大量货架空置,你会重建商店以消除 'wasted' space 吗?或者您只是 re-use 新货进货时空空如也的货架?调整 table 的大小有什么好处?充其量,它会将 space 释放回 TS,而不是 OS 文件系统。虽然有一些用例主张调整 table 的大小,但考虑到在大(甚至是大量)DELETE 之后调整大小必然是合理的,这不是自动的。
所以,我的回答是在 DELETE 之后什么也不做(好吧,当然是做 COMMIT!)
如何正确清洁tablespace?
例如,我们有一个 table 具有数百万行和功能索引。我们要删除 table.
的大部分为此我们调用了:从 some_table 中删除....
下一步是什么?
这个顺序正确吗? 1. 删除功能索引。 2. 改变some_table 收缩space。 3. 重新创建函数索引
- 创建table新table select * 从旧table 哪里...
- 降旧table
- 将新table重命名为旧table
- 重新创建索引。
正如您可能意识到的(或者您不会特别询问 function-based 索引),您不能简单地:
alter table mytable enable row movement;
alter table mytable shrink space;
alter table mytable disable row movement;
尝试这样做将导致:
ORA-10631: SHRINK clause should not be specified for this object
(注意:此限制也适用于位图连接索引。)
很明显,你可以先放弃 FBI...
drop index my_fbi_index;
alter table mytable enable row movement;
alter table mytable shrink space;
alter table mytable disable row movement;
create index my_fbi_index... online;
但这不是在线操作。您的应用程序将在短时间内受到缺少 function-based-index 的影响。
如果您需要在线操作,并且您使用的是 Oracle 12.2 或更高版本,您可以试试这个:
alter table mytable move online;
(alter table...move
(无 "online")在 12.2 之前可用,但它不是在线操作,它会删除您的索引段,留下标记为 "unusable" 的索引并需要你重建它们。所以,在 12.2 之前并不是一个好的选择。)
最佳方式
创建一个只包含有效数据的新table,然后重新创建索引,然后删除旧的table.
- 使用过滤后的数据创建新的 table,其中 table_name 是 table 的名称,filter_text 是以 'WHERE ...' 开头的条件,并且 partitionText 是一个分区子句,如果您有一个用于 table 的子句,例如
RANGE (ENDEDAT) INTERVAL ( NUMTODSINTERVAL(1,''day'') ) ( PARTITION p_first VALUES LESS THAN ( TO_DATE(''01-01-2010'',''dd-MM-yyyy'') ) ) ENABLE ROW MOVEMENT
sqlCommand := 'create table ' || table_name ||'_TMP
tablespace &TBS_NORMAL_TABLES initrans 32 ' || partitionText ||'
nologging
AS (SELECT * FROM '||table_name|| ' ' ||filter_text||')';
EXECUTE IMMEDIATE sqlCommand;
- 将属性添加到新 table。
例如,约束、索引...这些可以从 built-in table 中收集,例如 all_constraints、all_indexes。属性的移动也可以自动化,只需要应用一些重命名技巧。
- 换旧换新table
execute immediate 'ALTER TABLE &Schemaowner..'||v_table_name||' RENAME TO '||v_table_name||'_OT';
execute immediate 'ALTER TABLE &Schemaowner..'||v_table_name||'_TP'||' RENAME TO '||v_table_name;
- 放弃旧的table
execute immediate 'DROP TABLE '||v_table_name||'_OT';
TL;关于缩减和重组的 DR 信息 tables
当考虑在实时生产数据库上归档大量数据时,这里有一些关于我的调查的信息和有用的链接。
自动缩小一些 table 并处理错误的方法
for i in (SELECT obj.owner,obj.table_name,(CASE WHEN NVL(idx.cnt, 0) < 1 THEN 'Y' ELSE 'N' END) as shrinkable, row_movement
FROM all_tables obj,
(SELECT table_name, COUNT(rownum) cnt
FROM user_indexes
WHERE index_type LIKE 'FUN%'
GROUP BY table_name) idx
WHERE obj.table_name = idx.table_name(+)
AND obj.owner = &Schemaowner
and obj.table_name like 'T_%' and obj.table_name not like 'TMP_%'
and NVL(idx.cnt,0) < 1)
loop
BEGIN
if i.row_movement='ENABLED' then
execute immediate 'alter table '||i.table_name||' shrink space';
else
execute immediate 'alter table '||i.table_name||' enable row movement';
execute immediate 'alter table '||i.table_name||' shrink space';
execute immediate 'alter table '||i.table_name||' disable row movement';
end if;
DBMS_OUTPUT.PUT_LINE('shrinked table: ' || i.table_name);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('error while shrinking table: ' || i.table_name);
DBMS_OUTPUT.PUT_LINE (SQLERRM);
DBMS_OUTPUT.PUT_LINE (SQLCODE);
if SQLCODE=-10635 then
for p in (SELECT partition_name ,tablespace_name FROM user_tab_partitions WHERE table_name = 'SOME_PARTITIONED_TABLE')
loop
BEGIN
execute immediate 'alter table '||i.table_name||' MOVE PARTITION ' || p.partition_name || ' ONLINE TABLESPACE ' || p.tablespace_name || ' COMPRESS UPDATE INDEXES';
DBMS_OUTPUT.PUT_LINE('moved partition: ' || p.partition_name);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('error while moving partition: ' || p.partition_name);
DBMS_OUTPUT.PUT_LINE (SQLERRM);
DBMS_OUTPUT.PUT_LINE (SQLCODE);
CONTINUE;
END;
end loop;
end if;
CONTINUE;
END;
end loop;
有用的选择
可收缩tables
SELECT obj.owner
,obj.table_name
,(CASE WHEN NVL(idx.cnt, 0) < 1 THEN 'Y' ELSE 'N' END) as shrinkable
FROM all_tables obj,
(SELECT table_name, COUNT(rownum) cnt
FROM user_indexes
WHERE index_type LIKE 'FUN%'
GROUP BY table_name) idx
WHERE obj.table_name = idx.table_name(+)
AND NVL(idx.cnt,0) < 1
and obj.owner='YOUR_SCHEMA_OWNER'
使收缩命令无法执行的索引table
SELECT *
FROM all_indexes
WHERE index_type LIKE 'FUN%'
and owner='YOUR_SCHEMA_OWNER'
可缩table不妥协
SELECT obj.owner
,obj.table_name
,(CASE WHEN NVL(idx.cnt, 0) < 1 THEN 'Y' ELSE 'N' END) as shrinkable
FROM all_tables obj,
(SELECT table_name, COUNT(rownum) cnt
FROM user_indexes
WHERE index_type LIKE 'FUN%'
GROUP BY table_name) idx
WHERE obj.table_name = idx.table_name(+)
AND NVL(idx.cnt,0) < 1
--and obj.table_name like 'T_%' and obj.table_name not like 'TMP_%'
and obj.compression != 'ENABLED'
and obj.table_name not in (SELECT table_name FROM user_tab_partitions WHERE compression = 'ENABLED')
and obj.owner='YOUR_SCHEMA_OWNER'
压缩问题,压缩 tables and/or 个分区
SELECT table_name,compression, compress_for FROM user_tables WHERE compression = 'ENABLED'
SELECT table_name,partition_name, compression, compress_for FROM user_tab_partitions WHERE compression = 'ENABLED' ORDER BY 1
检查这些 before/after 收缩,测试它的地狱
select segment_name,bytes/1024/1024 as mb,blocks from user_segments where segment_name='TABLE_NAME'
在我的例子中,我创建了一个包含几百万行的 table(未分区),然后删除了其中的 1/3,结果如下:
|| BYTES || BLOCKS ||
Before deletion || 105250816 || 12848 ||
After deletion || 78774272 || 9616 ||
调查和side-effects
可能的副作用 https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:9536157800346457013
...什么时候应该使用重组: http://www.dba-oracle.com/t_table_fragmentation.htm
...启用行移动而收缩space可以重新排序行(这意味着如果使用基于ROWID工作或选择或类似的东西,那么可能会有一些惊喜)
- 创建您的 table 的副本
- 降旧table
- 重命名副本
有一个缺点,在此操作期间(可能需要一些时间)您的应用程序不可用。
所以,你的方法
- 删除函数索引。
- 改变some_table收缩space.
- 再次创建函数索引。
是正确的。
如果你有一个分区 table,请看这个:https://dba.stackexchange.com/questions/162415/how-to-shrink-space-on-table-with-a-function-based-index
我还没有看到有人提出过另一种选择。发出 DELETE,然后什么都不做。为什么我们认为我们需要重建索引?为什么我们认为我们需要调整 table 的大小?如果我们在 DELETE 之后什么都不做,table 的所有范围仍然分配给 table 并将用于未来的插入。如果你有一家零售店并进行清仓销售,导致大量货架空置,你会重建商店以消除 'wasted' space 吗?或者您只是 re-use 新货进货时空空如也的货架?调整 table 的大小有什么好处?充其量,它会将 space 释放回 TS,而不是 OS 文件系统。虽然有一些用例主张调整 table 的大小,但考虑到在大(甚至是大量)DELETE 之后调整大小必然是合理的,这不是自动的。
所以,我的回答是在 DELETE 之后什么也不做(好吧,当然是做 COMMIT!)