refactor(topics): 멀티 에이전트용 지식 재편 — _Common(공통 기본기) + Domain_* 구조

에이전트 8종(대화형/프로그래머 C·S/디자이너/설계자/기획자/QA/PD/PM)에게
[공통 기본 능력 + 롤별 Specialty] 2층으로 지식을 주입하기 위한 재분류.
문서 내용·포맷은 무수정, 폴더 이동만 (6,372개 문서 수 보존 확인).

- Topic_Programming → Domain_Programming (내부 구조 보존)
- Topic_Graphic → Domain_Design
- Topic_Business → Domain_Product
- Topic_General → Domain_General
- _Common 신설: Math(구 Topic_Math_Specialty), Reasoning(구 General/From_Thinking & Reasoning),
  Reasoning_Creativity(구 General/From_창의성), Communication(Poetic_Blog_Writing + From_writing)
- 타 도메인의 From_* 폴더는 유지 (출처 표기일 뿐, 이미 도메인에 맞게 분류된 문서)
- 빈 폴더 정리 (memory/procedures)
- 에이전트→폴더 매핑은 workspace의 .astra/agent-knowledge-map.json (9개 에이전트)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Antigravity Agent
2026-07-11 11:05:56 +09:00
parent 6549ead309
commit c24165b8bc
6193 changed files with 1717 additions and 31 deletions
@@ -0,0 +1,234 @@
---
id: db-postgres-explain
title: Postgres EXPLAIN — Plan / Index / Cost 분석
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [database, postgres, explain, vibe-coding]
tech_stack: { language: "SQL / Postgres", applicable_to: ["Backend"] }
applied_in: []
aliases: [EXPLAIN, EXPLAIN ANALYZE, query plan, sequential scan, index scan, nested loop, hash join]
---
# Postgres EXPLAIN
> 느린 query = plan 부터. **`EXPLAIN ANALYZE BUFFERS`** 가 표준. Sequential scan / index scan / join type / actual time 분석. **explain.dalibo.com** 또는 pgMustard 가 시각화.
## 📖 핵심 개념
- EXPLAIN: plan 만 (실행 X).
- EXPLAIN ANALYZE: 실제 실행 + 시간.
- BUFFERS: 디스크 vs cache hit.
- VERBOSE: 상세 정보.
## 💻 코드 패턴
### 기본
```sql
EXPLAIN ANALYZE
SELECT * FROM orders WHERE user_id = 'u1' ORDER BY created_at DESC LIMIT 10;
```
```
Limit (cost=0.42..1.20 rows=10 width=128) (actual time=0.05..0.15 rows=10 loops=1)
-> Index Scan using orders_user_created on orders (cost=0.42..123.45 rows=1500 width=128) (actual time=0.04..0.14 rows=10 loops=1)
Index Cond: (user_id = 'u1')
Planning Time: 0.12 ms
Execution Time: 0.18 ms
```
→ Index Scan + Index Cond + actual rows 적음 = OK.
### Sequential scan = warning
```
Seq Scan on orders (cost=0.00..50000.00 rows=1000000 width=128)
Filter: (user_id = 'u1')
Rows Removed by Filter: 999999
```
→ 100만 row 모두 읽음. Index 필요.
### Buffers 확인
```sql
EXPLAIN (ANALYZE, BUFFERS)
SELECT ...;
# Output:
Buffers: shared hit=42 read=8
# hit = cache, read =
# read = I/O bottleneck
```
### Index 추가 + 재실행
```sql
CREATE INDEX orders_user_created ON orders (user_id, created_at DESC);
EXPLAIN ANALYZE SELECT ... -- 다시
-- Index Scan 으로 변환
```
### 자주 쓰는 plan node
```
Seq Scan: 풀 스캔 — 큰 테이블 = 느림
Index Scan: index 로 row lookup — 빠름
Index Only Scan: index 만 — heap 접근 X (covering index)
Bitmap Heap Scan: 여러 row, bitmap 으로 batch
Hash Join: 양쪽 hash 후 조인 — 큰 join 빠름
Nested Loop: 각 row 마다 다른 쪽 lookup — 작은 한쪽
Merge Join: 양쪽 sort 후 merge
Sort: sort 작업 — work_mem 초과 시 디스크
Aggregate: group by / sum
HashAggregate: hash 기반 aggregate
```
### Statistics — planner 가 잘못 추측
```
rows=1000 (실제 100만)
→ planner 가 잘못 — Seq Scan 선택할 수 있음
```
```sql
ANALYZE orders; -- statistics update
```
→ 자주 쓰는 큰 table = autovacuum 외 명시.
### Index 사용 안 하는 경우
```sql
-- ❌ 함수 / 변환
SELECT * FROM users WHERE LOWER(email) = 'a@b.com';
-- email 인덱스 못 씀
-- ✅ Functional index
CREATE INDEX users_email_lower ON users ((LOWER(email)));
-- ❌ LIKE leading %
WHERE email LIKE '%@gmail.com'
-- ❌ Type cast
WHERE id::text = '42'
-- ❌ OR with different columns
WHERE name = 'a' OR email = 'b'
-- → UNION 으로 분리
```
### Composite index 순서
```sql
-- (user_id, created_at) 인덱스
SELECT ... WHERE user_id = $1 -- ✅ 사용
SELECT ... WHERE user_id = $1 AND created_at > $2 -- ✅
SELECT ... WHERE created_at > $2 -- ❌ leading 안 맞음
```
### LIMIT + ORDER BY = matching index
```sql
SELECT * FROM events ORDER BY ts DESC LIMIT 10;
-- (ts DESC) index = O(log n)
-- 또는 (user_id, ts DESC)
WHERE user_id = $1 ORDER BY ts DESC LIMIT 10;
```
### EXPLAIN 시각화
```
explain.dalibo.com
explain.depesz.com
pgMustard (paid)
```
→ 큰 plan tree 가 직관적.
### auto_explain (slow query log)
```sql
-- postgresql.conf
shared_preload_libraries = 'auto_explain'
auto_explain.log_min_duration = '500ms'
auto_explain.log_analyze = on
auto_explain.log_buffers = on
```
→ 500ms+ query 자동 plan log.
### pg_stat_statements
```sql
CREATE EXTENSION pg_stat_statements;
SELECT query, calls, total_exec_time, mean_exec_time, rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC LIMIT 20;
```
→ "어떤 query 가 가장 비싸"
### Common 문제 + fix
```
1. Seq Scan + 큰 table → CREATE INDEX
2. Sort 가 외부 디스크 (Sort Method: external merge) → work_mem 증가
3. Nested Loop + 큰 양쪽 → ANALYZE 후 hash join 으로
4. 큰 IN list → 임시 테이블 또는 ANY array
5. Subquery 가 매 row 실행 → JOIN 또는 LATERAL
```
### work_mem
```sql
SET work_mem = '64MB'; -- 세션
-- 또는 query
SELECT /*+ work_mem='64MB' */ ...; -- pg_hint_plan
```
→ Sort / Hash 가 메모리 안 들어가면 외부 디스크.
### Index size 검사
```sql
SELECT
schemaname, tablename, indexname,
pg_size_pretty(pg_relation_size(indexrelid)) AS size
FROM pg_indexes
JOIN pg_class ON pg_class.relname = indexname
ORDER BY pg_relation_size(indexrelid) DESC LIMIT 10;
```
### Unused index
```sql
SELECT
schemaname, relname, indexrelname,
idx_scan, pg_size_pretty(pg_relation_size(indexrelid)) AS size
FROM pg_stat_user_indexes
WHERE idx_scan = 0
AND indexrelname NOT LIKE '%_pkey'
ORDER BY pg_relation_size(indexrelid) DESC;
```
→ 사용 안 되는 index = drop.
## 🤔 의사결정 기준
| 증상 | 액션 |
|---|---|
| 느린 query | EXPLAIN ANALYZE |
| Seq Scan 큰 table | Index 추가 |
| Sort 외부 디스크 | work_mem 또는 인덱스 |
| Wrong rows estimate | ANALYZE |
| 매번 느림 | pg_stat_statements |
| Unused index | DROP |
## ❌ 안티패턴
- **EXPLAIN 만 + ANALYZE 안 함**: 실제 시간 모름.
- **Plan 만 보고 OK 가정**: row estimate 가 wrong 일 수 있음.
- **SELECT ***: column 다 read.
- **함수 / cast index 컬럼**: index 못 씀.
- **Statistics 안 update**: planner 잘못.
- **Unused index 방치**: 디스크 + INSERT 비용.
- **Production 에 ANALYZE 큰 table**: lock. CONCURRENTLY.
## 🤖 LLM 활용 힌트
- `EXPLAIN (ANALYZE, BUFFERS)` 표준.
- explain.dalibo.com 시각화.
- pg_stat_statements + auto_explain prod.
## 🔗 관련 문서
- [[DB_Index_Strategy]]
- [[DB_Query_Optimization]]
- [[DB_Vacuum_Autovacuum]]