1. 程式人生 > 其它 >KingbaseES 普通表線上改為分割槽表案例

KingbaseES 普通表線上改為分割槽表案例

對大表進行分割槽,但避免長時間鎖表

假設您有一個應用程式,該應用程式具有一個巨大的表,並且需要始終可用。它變得如此之大,以至於在不對其進行分割槽的情況下對其進行管理變得越來越困難。但是,您又不能使表離線以修改為分割槽表。

這是處理問題的祕訣。它不一定適用於所有情況,特別是具有非常重的寫入負載的表,但它可能適用於許多情況。

首先,讓我們設定我們的示例表,並用一些資料填充它,在本例中為 1000 萬行:

create table orig_table
( id serial not null,
  data float default random()
);

create index orig_data_index on orig_table(data);
create index orig_id_index on orig_table(id);

insert into orig_table (id)
select nextval('orig_table_id_seq')
from generate_series(1,100000);

現在,我們將設定分割槽結構。在本例中,我們將在資料欄位上使用四個範圍:

create table part_table
(like orig_table including defaults including indexes including constraints)
partition by range(data)
(
    partition  part_1 values LESS THAN (0.25),
    partition  part_2 values LESS THAN (0.5),
    partition  part_3 values LESS THAN (0.75),
    partition  part_table values LESS THAN (maxvalue)
);
 

我們將重新命名原始表,然後使用該名稱建立一個檢視,該名稱是新分割槽表和舊的非分割槽表中行的並集,需要一個觸發器來處理檢視的所有插入、更新和刪除操作。然後,我們可以在一個快速事務中轉到過渡設定。由於我們不會再向舊的非分割槽表新增新元組,因此我們禁用了它的AUTOVACUUM。


\set SQLTERM /

BEGIN;
/
ALTER TABLE orig_table
    RENAME TO old_orig_table;
/

ALTER TABLE old_orig_table
    SET (
        autovacuum_enabled = false, toast.autovacuum_enabled = false
        );
/
CREATE VIEW orig_table AS
SELECT id, data
FROM old_orig_table
UNION ALL
SELECT id, data
FROM part_table
;
/

CREATE or replace TRIGGER orig_table_part_trigger
    INSTEAD OF INSERT OR UPDATE OR DELETE
    on orig_table
    FOR EACH ROW
begin
    IF TG_OP = 'INSERT'
    THEN
        INSERT INTO part_table
        VALUES (NEW.id, NEW.data);
        RETURN NEW;
    ELSIF TG_OP = 'DELETE'
    THEN
        DELETE
        FROM part_table
        WHERE id = OLD.id;
        DELETE
        FROM old_orig_table
        WHERE id = OLD.id;
        RETURN OLD;
    ELSE -- UPDATE
        DELETE
        FROM old_orig_table
        WHERE id = OLD.id;
        IF FOUND
        THEN
            INSERT INTO part_table
            VALUES (NEW.id, NEW.data);
        ELSE
            UPDATE part_table
            SET id   = NEW.id,
                data = NEW.data
            WHERE id = OLD.id;
        END IF;
        RETURN NEW;
    END IF;
end;
/
COMMIT;
/

\set SQLTERM ;

請注意,即使正在更新的行來自舊錶,所有插入和更新都將定向到分割槽表。我們將利用這一事實批量移動所有舊行。我們需要的是一個迴圈程式,它選擇少量的舊錶行來移動並更新它們,以便移動它們。這是我使用的示例程式 - 它是用Perl編寫的,但對於大多數讀者來說應該很容易理解,即使不是Perl精通。

--多次執行遷移語句
WITH oldkeys AS
         (
             SELECT id
             FROM old_orig_table
             LIMIT 10000
         )
UPDATE orig_table
SET id = id
WHERE ID IN (SELECT id FROM oldkeys);


select 'orig_table' as tab, count(*) as cnt from orig_table
union all
SELECT 'old_orig_table' as tab, count(*) as cnt FROM old_orig_table
union all
SELECT  'part_table' as tab, count(*) as cnt FROM part_table;
    tab         |  cnt
----------------+--------
 orig_table     | 100000
 old_orig_table |  80000
 part_table     |  20000
(3 行記錄)

如有必要,可以安全地中斷此程式。還有其他書寫方法。

一旦原始表中不再有資料行,我們就可以用完全分割槽的表替換檢視。在獨立的事務中(因為它可能需要一些時間(鎖等待),並且並不重要),我們最終刪除了舊的非分割槽表。

BEGIN;
    DROP VIEW orig_table CASCADE; 
    ALTER SEQUENCE orig_table_id_seq OWNED BY part_table.id;
    ALTER TABLE part_table RENAME TO orig_table;
COMMIT;

BEGIN;
    DROP TABLE old_orig_table;
COMMIT;

我們的應用程式應該保持完整的功能,並且沒有意識到我們在進行更改。