【问题标题】:How to clean unused space after rows deletion on table with functional indexes?如何在具有功能索引的表上删除行后清理未使用的空间?
【发布时间】:2019-07-17 13:14:38
【问题描述】:

如何正确清理表空间?

例如,我们有一个包含数百万行和功能索引的表。我们要删除表格的大部分内容。

为此,我们称之为:从 some_table where ....中删除。

接下来的步骤是什么?

这个顺序正确吗? 1. 删除功能指标。 2. 改变 some_table 收缩空间。 3. 再次创建功能索引。

【问题讨论】:

  • 如果你要使用它,我认为不需要删除索引。使用以下命令重建索引 alter table mytable move;还要回收空间:alter table mytable enable row movement; alter table mytable 收缩空间;
  • @MansiRaval 我认为您可能错过了 OP 问题的重点。您不能在具有基于函数的索引的表上发出alter table...shrink space。不允许 (ORA-10631)。
  • 是的,我不能用函数索引收缩表

标签: sql database oracle tablespace


【解决方案1】:

您可能已经意识到(或者您不会特别询问基于函数的索引),您不能简单地:

alter table mytable enable row movement;
alter table mytable shrink space;
alter table mytable disable row movement;

尝试这样做会导致:

ORA-10631: 不应为此对象指定 SHRINK 子句

(注意:此限制也适用于位图连接索引。)

显然,你可以先放弃 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;

不过,这不是在线操作。您的应用程序将在短时间内受到缺少的基于函数的索引的影响。

如果您需要在线操作并且您使用的是 Oracle 12.2 或更高版本,您可以尝试以下操作:

alter table mytable move online;

(alter table...move (no "online") 在 12.2 之前可用,但它不是在线操作,它会删除您的索引段,使索引标记为“不可用”并要求您重建它们。所以,不是在 12.2 之前确实是一个不错的选择。)

【讨论】:

    【解决方案2】:

    最好的方法

    创建一个仅包含有效数据的新表,并在其中重新创建索引,然后删除旧表。

    • 使用过滤后的数据创建新表,其中 table_name 是表的名称,filter_text 是一个以“WHERE ...”开头的条件,partitionText 是一个分区子句,如果您有一个用于表的分区子句,例如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;
    
    • 向新表添加属性。

    例如,约束、索引...这些可以从内置表中收集,例如 all_constraintsall_indexes。属性的移动也可以自动化,只需要应用一些重命名技巧。

    • 换新旧表
    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;
    
    • 放下旧桌子

    execute immediate 'DROP TABLE '||v_table_name||'_OT';

    TL;关于收缩和重组表的 DR 信息

    当考虑在实时生产数据库上存档大量数据时,这里提供了一些关于我的调查的信息和有用的链接。

    自动收缩一些表并处理它们的错误

    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;
    

    有用的选择

    可收缩表

    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'
    

    使收缩命令无法执行的索引

    SELECT *
    FROM all_indexes 
    WHERE index_type LIKE 'FUN%'
    and owner='YOUR_SCHEMA_OWNER'
    

    毫不妥协的可收缩表

    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'
    

    收缩问题、表和/或分区的压缩

    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
    

    在收缩之前/之后检查这些,测试一下

    select segment_name,bytes/1024/1024 as mb,blocks from user_segments where segment_name='TABLE_NAME'
    

    在我的例子中,我创建了一个包含几百万行的表(未分区),然后删除其中的 1/3,结果如下:

                    ||     BYTES     ||  BLOCKS     ||
    Before deletion ||   105250816   ||  12848      ||
    After deletion  ||    78774272   ||   9616      ||
    

    调查和副作用

    可能的副作用 https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:9536157800346457013

    ...以及何时应该使用重组: http://www.dba-oracle.com/t_table_fragmentation.htm

    ...启用行移动,而收缩空间可以重新排列行(这意味着如果使用基于 ROWID 的作业或选择或类似的东西,那么可能会有一些惊喜)

    http://www.dba-oracle.com/t_enable_row_movement.htm

    【讨论】:

    • 您只提供了一堆 SELECT 语句,但没有提供实际收缩数据的语句。
    • 接下来的步骤是什么? - 他问,我试图帮助他理解他所面临的:(。还有一群人在我之前写了缩小的 DDL,我只是想在这里放一些其他有用的数据
    • 创建一个只包含有效数据的新表,并在那里重新创建索引,然后删除旧表。然后将新表重命名为旧表,我猜。这种方法会破坏许多应用程序,除非您还考虑了授权、VPD 策略、触发器等。如果您想采用这种方法,请考虑 DBMS_REDEFINITION,因为它会为您处理大部分。
    • @MatthewMcPeak 另一个答案也为这种情况写了一些步骤,但是是的,我应该举一个例子
    • @MatthewMcPeak 我编辑了我的评论,以展示两种方式
    【解决方案3】:
    1. 创建表newtable select * from oldtable where...
    2. 删除旧表
    3. 将新表重命名为旧表
    4. 重新创建索引。

    【讨论】:

      【解决方案4】:
      • 创建表的副本
      • 放下旧表
      • 重命名副本

      有一个缺点,在此操作期间(可能需要一些时间)您的应用程序不可用。

      所以,你的方法

      1. 删除功能索引。
      2. 更改 some_table 收缩空间。
      3. 再次创建功能索引。

      是正确的。

      如果你有一个分区表,请看这个:https://dba.stackexchange.com/questions/162415/how-to-shrink-space-on-table-with-a-function-based-index

      【讨论】:

        【解决方案5】:

        我还没有看到任何人提出另一种选择。发出 DELETE,然后什么也不做。为什么我们认为我们需要重建索引?为什么我们认为我们需要调整表格的大小?如果我们在 DELETE 之后什么都不做,则该表的所有范围仍然分配给该表,并将在以后的 INSERTS 中使用。如果您有一家零售店并进行清仓销售,导致大量货架空置,您是否会重建商店以消除“浪费”的空间?还是您只是在新库存进入时重新使用空货架?通过调整表格大小可以获得什么?充其量,它会将空间释放回 TS,而不是 OS 文件系统。虽然有些用例主张调整表的大小,但这并不是自动的,因为在大(甚至大量)DELETE 之后调整大小是必要的。

        同样,我的回答是在 DELETE 之后什么都不做(当然,做一个 COMMIT!)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-01-07
          • 2017-04-28
          • 2017-08-31
          • 1970-01-01
          • 2018-09-28
          相关资源
          最近更新 更多