11 버전에서 일반 테이블을 파티션 테이블로 바꾸기
11 버전에서 일반 테이블을 파티션 테이블로 바꾸기
운영 환경에서 기존 테이블을 파티션 테이블로 바꾸기는 작업은 생각보다 까다롭습니다.
이 글은 지난 PGConf APAC 2019 에서 대형 테이블 다루기 발표자료에 대한 글쓴이 나름대로의 해석입니다.
시나리오는 다음과 같습니다.
- 준비 (단일 트랜잭션)
- 새 파티션 테이블을 만든다
- 기존 테이블을 다른 이름으로 바꾼다
- 기존 테이블과 이름이 같은 뷰를 만드는데, 그 내용은 새 파티션 테이블과 이름이 바뀐 옛 일반 테이블을 모두 함께 조회하는 것이다
- 새로 만든 뷰에 insert, delete rule을 추가하고
- update 에서 사용할 트리거 함수를 만들고, 새로 만든 뷰에 트리거를 지정한다.
- 자료 이동 (단일 트랜잭션)
- 기본키 단위로 하나씩 모든 자료에 대해서 update 구문을 실행한다
- 뒷 정리 (단일 트랜잭션)
- 뷰를 지우고,
- 파티션 테이블을 뷰 이름으로 바꾼다
다음은 뷰에 insert, delete 룰과 update 트리거를 지정하는 작업을 pgbench 테이블을 대상으로 한 것입니다.
BEGIN;
-- 새 해쉬 파티션 상위 테이블을 만들고
CREATE TABLE new_pgbench_accounts (
aid INT NOT NULL PRIMARY KEY,
bid INT,
abalance INT,
filler CHAR(84)
) PARTITION BY hash (aid);
-- 다섯개의 하위 테이블을 만들고
CREATE TABLE pgbench_accounts_0 PARTITION OF new_pgbench_accounts FOR
VALUES
WITH (modulus 5, remainder 0);
CREATE TABLE pgbench_accounts_1 PARTITION OF new_pgbench_accounts FOR
VALUES
WITH (modulus 5, remainder 1);
CREATE TABLE pgbench_accounts_2 PARTITION OF new_pgbench_accounts FOR
VALUES
WITH (modulus 5, remainder 2);
CREATE TABLE pgbench_accounts_3 PARTITION OF new_pgbench_accounts FOR
VALUES
WITH (modulus 5, remainder 3);
CREATE TABLE pgbench_accounts_4 PARTITION OF new_pgbench_accounts FOR
VALUES
WITH (modulus 5, remainder 4);
-- 기존 테이블의 이름을 바꾸고
ALTER TABLE
pgbench_accounts RENAME TO old_pgbench_accounts;
-- 기존 테이블 이름으로 뷰를 만들고
CREATE VIEW pgbench_accounts AS
SELECT
*
FROM
old_pgbench_accounts
UNION ALL
SELECT
*
FROM
new_pgbench_accounts;
-- 그 뷰에 insert 룰 추가
CREATE RULE pgbench_accounts_insert AS
ON INSERT TO pgbench_accounts
DO INSTEAD
INSERT INTO new_pgbench_accounts (aid, bid, abalance, filler)
VALUES
(new.*);
-- 그 뷰에 delete 룰 추가
CREATE RULE pgbench_accounts_delete AS ON DELETE TO pgbench_accounts
DO INSTEAD (
DELETE FROM
new_pgbench_accounts
WHERE
(
new_pgbench_accounts.aid = old.aid
);
DELETE FROM
old_pgbench_accounts
WHERE
(
old_pgbench_accounts.aid = old.aid
);
);
-- update 트리거추가
-- (with delete returing insert on conflict 룰을 지원하지 않아 트리거로 만들었다)
CREATE FUNCTION tr_pgbench_accounts_update() RETURNS TRIGGER
LANGUAGE plpgsql AS $$
begin
delete from
old_pgbench_accounts
where
aid = old.aid;
insert into new_pgbench_accounts
values
(new.*) ON CONFLICT (aid) do
update
set
(aid, bid, abalance, filler) = row(EXCLUDED.*);
return new;
end;
$$;
CREATE TRIGGER tr_pgbench_accounts_update INSTEAD OF
UPDATE
ON pgbench_accounts FOR EACH ROW
EXECUTE PROCEDURE tr_pgbench_accounts_update();
END;