beta DB / Redis 최적 정리계획

PostgreSQL 18.3 + Redis 8.6.1 · 실측 기반 분석 · 2026-06-05
~9 GB
회수 가능 (PG 29G→~20G)
87%
PG 컨테이너 메모리 (6.96/8G)
7.9 GB
job_logs (7일치 폭증)
5.06 GB
Redis peak (현재 980M)

요약 TL;DR

최근 커밋은 beta에 악영향이 없으며 오히려 메모리를 안정화시켰다. 진짜 부채는 코드가 아니라 데이터 누적job_logs 파티셔닝 부재Redis 캐시·큐 인스턴스 혼재 2가지다.

영역현황판정
마이그레이션 0440~0448컨테이너 로그 migrations applied successfully (attempt 1/5)정상 적용
Redis OOM 대응 (#8175/#8164/#8157)maxmemory 8G, evicted 0, 현재 980M해소됨
캐시 히트율 / dead tuple98.31% / <5%건강
job_logs7일치인데 7.8M행 / 7.9G, 일 100만 폭증파티셔닝 필요
Redis db0큐+캐시+throttle 1.25M키 혼재, noeviction인스턴스 분리 필요

본문검색 인덱스 — 오판 정정 기록 중요

분석 중 idx_emails_body_text_trgm(1.3G)에 대해 2차례 잘못된 결론을 냈고, git 추적으로 진실을 확정했다.

단계결론정확도
1차scan 21 → "미사용, 희주/PM 확인 후 DROP"오판 (희주는 디자인 SSOT 오너, 무관)
2차"오늘 본문검색 활성화됨 → 보호 대상"부정확 (활성화 커밋은 feature 브랜치만)
확정커밋 7886b3ef7feat/inbox-body-search-* 브랜치 전용, alpha/beta 미머지git merge-base 검증
현재 상태: 본문검색은 production(beta)에서 비활성. 이 인덱스는 현재 미사용이지만 머지 임박한 기능의 선행 인덱스다. 머지 시 81만 행 풀스캔(주석상 24초)을 막으므로 유지가 정답 — 머지 무산 시에만 DROP. trgm 설정도 이미 최적(fastupdate=on, pending=32MB).

1순위 · job_logs P0 · ~7G

7일치(05-29~06-05)인데 7.8M행/7.9G, 일 100만 폭증, completed 85%(6.6M), 단일컬럼 인덱스 9개 난립.

조치방법효과
파티셔닝PARTITION BY RANGE(created_at) + 오래된 파티션 DROP TABLEDELETE→DROP(0초, bloat·VACUUM 0). 7일 유지 시 7.9G→~1G
인덱스 통합단일컬럼 9개 → (queue_name, status, created_at DESC) 복합 (PG18 B-tree skip scan이 prefix 생략 쿼리 커버)인덱스 용량↓ + INSERT 쓰기증폭↓
completed 단기보관retention 1~2일 (성공 로그는 단명)row 85% 감소
파티셔닝은 신규 마이그레이션 필수 — bun db:generate로 생성, hand-roll 금지(CLAUDE.md 0392 인시던트).

2순위 · 즉시 안전 DROP 인덱스 P1 · ~440M · 저위험

scan≈0, unique/pk 아님 — 무손실 회수.

DROP INDEX CONCURRENTLY leads_description_trgm_idx;              -- 215M, scan 0
DROP INDEX CONCURRENTLY leads_sendable_idx;                     -- 60M,  scan 0
DROP INDEX CONCURRENTLY idx_thread_summary_ws_open_outbound;    -- 127M, scan 2
DROP INDEX CONCURRENTLY recipient_send_time_stats_ws_scope_idx; -- 38M,  scan 1
recipient_send_time_stats_bucket_unique(116M, scan 0)는 UNIQUE 제약이라 제외 — 무결성용.

대형 테이블 점유도 데이터

테이블크기분포비고
job_logs7.9GP0 파티셔닝
emails6.6G인덱스 5.8G, 대부분 hot — 유지
email_events3.0G과거(04월) 多 → 아카이빙
sequence_step_contents3.0G인덱스 2.4G
leads1.9Gdescription_trgm DROP 대상

3순위 · Redis 구조 개선 P1 · OOM 근본대응

db0에 큐+캐시+throttle+SSE 1.25M키 혼재, strings 109만개=316M(87%), delayed zset 57k, lists avg 14,940(LTRIM 없음), frag 1.26.

조치효과
캐시·큐 혼재 (peak 5G 원인)큐 전용(noeviction) + 캐시 전용(allkeys-lru) 인스턴스 분리캐시 누수→큐 OOM(발송중단) 인과 단절
throttle stale 키cleanupStaleThrottleSlots() cron 상시화수십만 키 회수
web-extraction listRPUSH 뒤 LTRIM 추가 (SSE event-store 패턴)배치당 수MB×7일 제거
streams 옵션 누락 큐defaultStreamsOptions 일괄 적용stream 무한증가 차단
delayed 57k 적체메모리 상향 아님 → worker concurrency/sender capacity 증설zset 상시 축소
maxmemory 2G→4G→8G 단계 상향은 OOM 지연일 뿐. noeviction은 BullMQ 정합성상 유지 필수 — 대신 캐시를 별도 인스턴스로 분리해야 OOM=발송중단 리스크가 사라진다.

4순위 · PG18 설정 튜닝 P1 · 신기능 미적용

beta postgres command에 PG18 knob이 전무. docker-compose에 추가:

-c io_method=worker -c io_workers=4          # Docker seccomp가 io_uring 차단 → worker 표준
-c effective_io_concurrency=200              # NVMe/gp3 가정 (기본 16)
-c max_parallel_maintenance_workers=2        # trgm parallel GIN build용
-c autovacuum_vacuum_insert_scale_factor=0.05 # 폭증 테이블 선제 vacuum
효과: seq scan·VACUUM·집계 처리량 20~40%(pganalyze). 메모리/bloat 자체는 건강하므로 진짜 갭은 storage·write 증폭 쪽이다.

실행 우선순위 (WSJF) 로드맵

#작업회수위험선행조건
1안전 인덱스 4개 DROP440M낮음없음 (CONCURRENTLY)
2Redis throttle GC + list LTRIM수백M낮음코드 PR
3job_logs 파티셔닝 + retention~7G마이그레이션
4Redis 캐시·큐 인스턴스 분리OOM 해소인프라
5PG18 설정 + email_events 아카이빙1.5G+속도낮음재기동
즉시 시작 가능: 1·2번(저위험). 구조 작업: 3·4번(배포 계획 필요).

커뮤니티 인사이트 2026 최적패턴

PostgreSQL 18

  • io_method=worker가 컨테이너 표준 — io_uring은 Docker seccomp가 차단
  • parallel GIN build로 trgm 재인덱싱 비용 大폭 감소
  • B-tree skip scan — 선행컬럼 카디널리티 낮을 때(status/queue) 단일컬럼 인덱스 통합
  • 메이저 업그레이드 시 trgm/FTS REINDEX 필수 (collation provider 변경)

Redis 8 / BullMQ

  • noeviction이 유일한 정답 — allkeys-lru는 큐 손상
  • 큐 메타·페이로드·캐시 인스턴스 분리가 2026 정설
  • delayed zset 누적 = "조용한 backlog" → capacity 증설이 정답
  • Redis 8.6 HSETEX/HEXPIRE/HGETDEL로 hash 필드 단위 TTL