명령어/DB

[PostgreSQL] REFRESH MATERIALIZED VIEW CONCURRENTLY 갱신 중에도 조회를 막지 않는다

jykim23 2026. 7. 23. 21:57
반응형

설치·접속: PostgreSQL 설치와 접속

부제: 대시보드가 계속 읽는 집계 뷰를 새로 굽고 싶은데, 일반 REFRESH는 갱신 내내 조회를 통째로 막아 버려서 서비스 중단 없이 최신화하고 싶을 때

-- 전제조건: 행을 유일하게 식별하는 UNIQUE 인덱스가 반드시 있어야 한다
CREATE UNIQUE INDEX m10_mv_pk ON m10_mv(id);

-- [세션 A] 대용량 뷰를 CONCURRENTLY로 갱신 (약 1.9초 소요)
REFRESH MATERIALIZED VIEW CONCURRENTLY m10_big;   -- Time: 1889.277 ms
-- [세션 B] 세션 A가 한창 갱신하는 중에 조회 → 차단되지 않고 즉답
SELECT grp, c FROM m10_big ORDER BY grp LIMIT 3;   -- Time: 5.795 ms
 grp |  c   
-----+------
   0 | 3000        <- 갱신 진행 중엔 아직 '예전' 결과가 보인다
   1 | 3000
   2 | 3000
(3 rows)   -- 갱신 완료 후 다시 조회하면 c = 30000

일반 REFRESH MATERIALIZED VIEW는 뷰에 AccessExclusiveLock을 걸어, 다시 굽는 동안 모든 SELECT를 대기시킨다. 뷰가 크면 그 시간 내내 대시보드가 멈춘다. CONCURRENTLY를 붙이면 갱신은 새 데이터를 임시로 만든 뒤 변경분만 반영하는 방식이라, 조회를 막는 강한 잠금을 잡지 않는다. 위에서 세션 A가 1.9초간 갱신하는 동안 세션 B의 조회는 5.8ms 만에 예전 스냅샷을 반환했고, 갱신이 끝난 뒤에야 새 값(30000)이 보였다. 읽는 쪽은 한순간도 끊기지 않는다. 대신 각 행을 유일하게 식별하는 UNIQUE 인덱스가 없으면 이 방식을 쓸 수 없다.

이렇게도 쓴다

UNIQUE 인덱스가 없으면 CONCURRENTLY는 거부된다(반드시 먼저 만든다).

REFRESH MATERIALIZED VIEW CONCURRENTLY m10_mv;
-- ERROR: cannot refresh materialized view "public.m10_mv" concurrently
-- HINT:  Create a unique index with no WHERE clause ...

 

읽기가 잠깐 멈춰도 되는 소규모 뷰는 그냥 일반 갱신이 더 빠르다.

REFRESH MATERIALIZED VIEW m10_mv;   -- 짧게 잠그고 통째로 다시 굽기

 

크론으로 주기 갱신을 자동화한다. (조합: 셸 + psql)

# 매시 정각, 조회 안 끊고 새로고침
0 * * * * psql -d shop -c "REFRESH MATERIALIZED VIEW CONCURRENTLY m10_big"

 

정의만 먼저 만들고 데이터는 첫 REFRESH 때 채운다. (조합: WITH NO DATA)

CREATE MATERIALIZED VIEW m10_mv2 AS SELECT ... WITH NO DATA;
REFRESH MATERIALIZED VIEW m10_mv2;   -- 이때 처음 채워짐

 

채워졌는지 시스템 카탈로그로 확인한다. (조합: pg_matviews)

SELECT matviewname, ispopulated FROM pg_matviews WHERE matviewname = 'm10_big';

 

조회 성능을 위해 뷰 위에 추가 인덱스를 건다.

CREATE INDEX ON m10_big(c DESC);   -- CONCURRENTLY 갱신은 인덱스를 자동 반영
반응형