KingbaseES 普通表線上改為分割槽表案例
阿新 • • 發佈:2022-05-19
對大表進行分割槽,但避免長時間鎖表
假設您有一個應用程式,該應用程式具有一個巨大的表,並且需要始終可用。它變得如此之大,以至於在不對其進行分割槽的情況下對其進行管理變得越來越困難。但是,您又不能使表離線以修改為分割槽表。
這是處理問題的祕訣。它不一定適用於所有情況,特別是具有非常重的寫入負載的表,但它可能適用於許多情況。
首先,讓我們設定我們的示例表,並用一些資料填充它,在本例中為 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;
我們的應用程式應該保持完整的功能,並且沒有意識到我們在進行更改。