496 lines
10 KiB
Markdown
496 lines
10 KiB
Markdown
---
|
|
id: db-postgres-extensions
|
|
title: Postgres Extensions — pgvector / TimescaleDB / Citus
|
|
category: Coding
|
|
status: draft
|
|
source_trust_level: B
|
|
verification_status: conceptual
|
|
created_at: 2026-05-09
|
|
updated_at: 2026-05-09
|
|
tags: [database, postgres, extensions, vibe-coding]
|
|
tech_stack: { language: "PostgreSQL", applicable_to: ["Backend"] }
|
|
applied_in: []
|
|
aliases: [Postgres extensions, pgvector, TimescaleDB, Citus, PostGIS, pg_cron, pg_partman]
|
|
---
|
|
|
|
# Postgres Extensions
|
|
|
|
> Postgres = "swiss army knife". **Extension 가 거의 모든 use case**. pgvector / TimescaleDB / Citus / PostGIS / pg_cron 등.
|
|
|
|
## 📖 핵심 개념
|
|
- Extension: SQL + C code module.
|
|
- `CREATE EXTENSION`: 활성.
|
|
- Cloud RDS / Aurora / Neon = 대부분 지원.
|
|
- Self-host = 직접 install.
|
|
|
|
## 💻 코드 패턴
|
|
|
|
### 자주 쓰는 extension
|
|
```sql
|
|
-- Vector
|
|
CREATE EXTENSION vector; -- pgvector
|
|
|
|
-- Time-series
|
|
CREATE EXTENSION timescaledb; -- TimescaleDB
|
|
|
|
-- Distributed
|
|
CREATE EXTENSION citus; -- Citus
|
|
|
|
-- Geo
|
|
CREATE EXTENSION postgis; -- PostGIS
|
|
|
|
-- Crypto
|
|
CREATE EXTENSION pgcrypto; -- 해시, 암호
|
|
|
|
-- UUID
|
|
CREATE EXTENSION "uuid-ossp"; -- UUID 생성
|
|
|
|
-- Cron
|
|
CREATE EXTENSION pg_cron; -- DB 안 cron
|
|
|
|
-- Stats
|
|
CREATE EXTENSION pg_stat_statements; -- query 분석
|
|
CREATE EXTENSION auto_explain; -- slow query log
|
|
|
|
-- HLL (probabilistic)
|
|
CREATE EXTENSION hll; -- HyperLogLog
|
|
|
|
-- Trigram (fuzzy search)
|
|
CREATE EXTENSION pg_trgm;
|
|
|
|
-- JSON 강력
|
|
CREATE EXTENSION pg_jsonschema;
|
|
|
|
-- Async / queue
|
|
CREATE EXTENSION pgmq; -- message queue
|
|
|
|
-- Compression
|
|
CREATE EXTENSION pg_lz;
|
|
```
|
|
|
|
### pgvector (vector search)
|
|
```sql
|
|
CREATE EXTENSION vector;
|
|
|
|
CREATE TABLE docs (
|
|
id BIGSERIAL,
|
|
content TEXT,
|
|
embedding VECTOR(1536)
|
|
);
|
|
|
|
CREATE INDEX ON docs USING hnsw (embedding vector_cosine_ops);
|
|
|
|
-- Search
|
|
SELECT * FROM docs ORDER BY embedding <=> $1::vector LIMIT 5;
|
|
```
|
|
|
|
→ [[DB_pgvector_Production]].
|
|
|
|
### TimescaleDB (time-series)
|
|
```sql
|
|
CREATE EXTENSION timescaledb;
|
|
|
|
CREATE TABLE metrics (
|
|
ts TIMESTAMPTZ NOT NULL,
|
|
device_id TEXT,
|
|
cpu DOUBLE PRECISION
|
|
);
|
|
|
|
SELECT create_hypertable('metrics', 'ts', chunk_time_interval => INTERVAL '1 day');
|
|
|
|
-- 자동 partition + 압축 + retention
|
|
```
|
|
|
|
→ [[DB_Time_Series_Patterns]].
|
|
|
|
### Citus (sharding)
|
|
```sql
|
|
CREATE EXTENSION citus;
|
|
|
|
SELECT create_distributed_table('orders', 'tenant_id');
|
|
-- 자동 sharding by tenant_id
|
|
```
|
|
|
|
→ [[DB_Sharding_Strategies]].
|
|
|
|
### PostGIS (geo)
|
|
```sql
|
|
CREATE EXTENSION postgis;
|
|
|
|
CREATE TABLE places (
|
|
id SERIAL PRIMARY KEY,
|
|
name TEXT,
|
|
location GEOGRAPHY(POINT, 4326)
|
|
);
|
|
|
|
INSERT INTO places (name, location) VALUES (
|
|
'Tower',
|
|
ST_GeographyFromText('POINT(127.0 37.5)')
|
|
);
|
|
|
|
-- 1km 안 가까운
|
|
SELECT * FROM places
|
|
WHERE ST_DWithin(location, ST_GeographyFromText('POINT(127.0 37.5)'), 1000);
|
|
|
|
-- 거리
|
|
SELECT name, ST_Distance(location, ST_GeographyFromText('POINT(127.0 37.5)')) AS dist
|
|
FROM places ORDER BY dist LIMIT 10;
|
|
```
|
|
|
|
### pg_trgm (fuzzy search)
|
|
```sql
|
|
CREATE EXTENSION pg_trgm;
|
|
|
|
CREATE INDEX users_name_trgm ON users USING GIN (name gin_trgm_ops);
|
|
|
|
-- Similar names
|
|
SELECT name, similarity(name, 'alice') AS sim
|
|
FROM users
|
|
WHERE name % 'alice' -- pg_trgm operator
|
|
ORDER BY sim DESC LIMIT 10;
|
|
|
|
-- Partial match
|
|
SELECT * FROM users WHERE name ILIKE '%al%'; -- 빠름 (with trgm index)
|
|
```
|
|
|
|
### pg_cron (scheduled jobs)
|
|
```sql
|
|
CREATE EXTENSION pg_cron;
|
|
|
|
-- 매일 오전 9시 cleanup
|
|
SELECT cron.schedule('cleanup-old-data', '0 9 * * *', $$
|
|
DELETE FROM events WHERE created_at < NOW() - INTERVAL '90 days'
|
|
$$);
|
|
|
|
-- 매 5분
|
|
SELECT cron.schedule('sync-cache', '*/5 * * * *', 'CALL refresh_cache()');
|
|
|
|
-- List
|
|
SELECT * FROM cron.job;
|
|
|
|
-- Unschedule
|
|
SELECT cron.unschedule('cleanup-old-data');
|
|
```
|
|
|
|
→ Application-level cron 대안.
|
|
|
|
### pgcrypto (encryption)
|
|
```sql
|
|
CREATE EXTENSION pgcrypto;
|
|
|
|
-- Hash
|
|
SELECT crypt('password', gen_salt('bf'));
|
|
|
|
-- Verify
|
|
SELECT crypt('password', stored_hash) = stored_hash;
|
|
|
|
-- Random
|
|
SELECT gen_random_uuid();
|
|
SELECT gen_random_bytes(16);
|
|
|
|
-- Encrypt
|
|
SELECT pgp_sym_encrypt('secret', 'password');
|
|
SELECT pgp_sym_decrypt(encrypted, 'password');
|
|
```
|
|
|
|
### pgmq (message queue in PG)
|
|
```sql
|
|
CREATE EXTENSION pgmq;
|
|
|
|
SELECT pgmq.create('my_queue');
|
|
|
|
-- Send
|
|
SELECT pgmq.send('my_queue', '{"order_id": 42}');
|
|
|
|
-- Read (with VT — 30s lock)
|
|
SELECT * FROM pgmq.read('my_queue', 30, 1);
|
|
-- {msg_id, message, ...}
|
|
|
|
-- Delete (ack)
|
|
SELECT pgmq.delete('my_queue', 1);
|
|
|
|
-- Archive
|
|
SELECT pgmq.archive('my_queue', 1);
|
|
```
|
|
|
|
→ Postgres = light queue. SQS / RabbitMQ alternative.
|
|
|
|
### pg_stat_statements (query 분석)
|
|
```sql
|
|
CREATE EXTENSION pg_stat_statements;
|
|
|
|
-- Top slow queries
|
|
SELECT
|
|
query,
|
|
calls,
|
|
total_exec_time / 1000 AS total_seconds,
|
|
mean_exec_time AS avg_ms,
|
|
rows
|
|
FROM pg_stat_statements
|
|
ORDER BY total_exec_time DESC LIMIT 20;
|
|
```
|
|
|
|
→ [[DB_Postgres_EXPLAIN]].
|
|
|
|
### auto_explain (slow query log)
|
|
```sql
|
|
-- postgresql.conf
|
|
shared_preload_libraries = 'auto_explain'
|
|
|
|
ALTER SYSTEM SET auto_explain.log_min_duration = '500ms';
|
|
ALTER SYSTEM SET auto_explain.log_analyze = on;
|
|
ALTER SYSTEM SET auto_explain.log_buffers = on;
|
|
SELECT pg_reload_conf();
|
|
```
|
|
|
|
→ Slow query 자동 EXPLAIN log.
|
|
|
|
### pg_partman (자동 partition)
|
|
```sql
|
|
CREATE EXTENSION pg_partman;
|
|
|
|
SELECT partman.create_parent(
|
|
p_parent_table => 'public.events',
|
|
p_control => 'created_at',
|
|
p_type => 'native',
|
|
p_interval => 'monthly',
|
|
p_premake => 3
|
|
);
|
|
|
|
-- Maintenance (매 시간)
|
|
SELECT partman.run_maintenance_proc();
|
|
```
|
|
|
|
→ Automatic partition creation + drop.
|
|
|
|
### plv8 (JS in DB)
|
|
```sql
|
|
CREATE EXTENSION plv8;
|
|
|
|
CREATE FUNCTION my_function(input TEXT)
|
|
RETURNS TEXT AS $$
|
|
return input.toUpperCase();
|
|
$$ LANGUAGE plv8;
|
|
|
|
SELECT my_function('hello'); -- 'HELLO'
|
|
```
|
|
|
|
→ JavaScript stored procedure.
|
|
|
|
### Foreign Data Wrapper (FDW)
|
|
```sql
|
|
-- Postgres → Postgres
|
|
CREATE EXTENSION postgres_fdw;
|
|
|
|
CREATE SERVER remote_pg
|
|
FOREIGN DATA WRAPPER postgres_fdw
|
|
OPTIONS (host 'remote.example.com', dbname 'app');
|
|
|
|
CREATE FOREIGN TABLE remote_users
|
|
(id UUID, email TEXT)
|
|
SERVER remote_pg
|
|
OPTIONS (schema_name 'public', table_name 'users');
|
|
|
|
SELECT * FROM remote_users;
|
|
```
|
|
|
|
→ Postgres → MySQL / S3 / file 도 가능.
|
|
|
|
### pg_jsonschema (JSON validation)
|
|
```sql
|
|
CREATE EXTENSION pg_jsonschema;
|
|
|
|
CREATE TABLE events (
|
|
data JSONB CHECK (jsonschema_is_valid('
|
|
{"type":"object","required":["type"],"properties":{"type":{"type":"string"}}}
|
|
', data))
|
|
);
|
|
```
|
|
|
|
### pgaudit (compliance)
|
|
```sql
|
|
CREATE EXTENSION pgaudit;
|
|
|
|
ALTER SYSTEM SET pgaudit.log = 'write,ddl';
|
|
SELECT pg_reload_conf();
|
|
```
|
|
|
|
→ Detailed audit log.
|
|
|
|
### pg_hint_plan (force plan)
|
|
```sql
|
|
CREATE EXTENSION pg_hint_plan;
|
|
|
|
/*+ IndexScan(orders orders_user_idx) */
|
|
SELECT * FROM orders WHERE user_id = $1;
|
|
```
|
|
|
|
→ Planner hint. Last resort.
|
|
|
|
### Cloud 의 extension 지원
|
|
```
|
|
RDS Postgres: 100+ extension.
|
|
Aurora: 비슷.
|
|
Supabase: pgvector, pg_cron, etc 강.
|
|
Neon: pgvector, postgis.
|
|
Cloud SQL: 표준 set.
|
|
|
|
→ Provider docs 검사.
|
|
```
|
|
|
|
### Self-host
|
|
```bash
|
|
# Docker
|
|
docker run -d \
|
|
-e POSTGRES_PASSWORD=secret \
|
|
-p 5432:5432 \
|
|
pgvector/pgvector:pg16
|
|
|
|
# Or 직접 install
|
|
apt install postgresql-16-pgvector
|
|
```
|
|
|
|
### Extension version 관리
|
|
```sql
|
|
-- 현재 version
|
|
SELECT * FROM pg_extension WHERE extname = 'vector';
|
|
|
|
-- Update
|
|
ALTER EXTENSION vector UPDATE TO '0.7.0';
|
|
|
|
-- Available versions
|
|
SELECT * FROM pg_available_extension_versions WHERE name = 'vector';
|
|
```
|
|
|
|
### pg_repack (online table rewrite)
|
|
```bash
|
|
pg_repack -d mydb -t orders
|
|
```
|
|
|
|
→ VACUUM FULL 의 zero-downtime alternative.
|
|
|
|
→ [[DB_Vacuum_Autovacuum]].
|
|
|
|
### Useful 시작 set
|
|
```sql
|
|
-- 시작 시 활성
|
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
|
|
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
|
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
|
```
|
|
|
|
### Combo (modern app)
|
|
```
|
|
- pgvector (RAG)
|
|
- pg_cron (scheduled tasks)
|
|
- pgmq (light queue)
|
|
- pg_trgm (search)
|
|
- pg_stat_statements (monitoring)
|
|
- pgaudit (compliance)
|
|
```
|
|
|
|
→ Postgres 만으로 큰 stack 가능.
|
|
|
|
### Multi-extension query
|
|
```sql
|
|
-- Vector + cron + JSON
|
|
SELECT cron.schedule('embed-new-docs', '*/10 * * * *', $$
|
|
UPDATE docs SET embedding = embed(content)
|
|
WHERE embedding IS NULL
|
|
$$);
|
|
```
|
|
|
|
### Custom extension (자체 build)
|
|
```c
|
|
// my_extension.c
|
|
#include "postgres.h"
|
|
#include "fmgr.h"
|
|
|
|
PG_MODULE_MAGIC;
|
|
|
|
PG_FUNCTION_INFO_V1(my_function);
|
|
Datum my_function(PG_FUNCTION_ARGS) {
|
|
int32 arg = PG_GETARG_INT32(0);
|
|
PG_RETURN_INT32(arg * 2);
|
|
}
|
|
```
|
|
|
|
→ C 작성 → DB 안 native function.
|
|
|
|
### Extension as source of truth
|
|
```
|
|
Cloud-native:
|
|
- pgvector + RAG
|
|
- pg_cron + jobs
|
|
- pgmq + queue
|
|
- pg_partman + time-series
|
|
|
|
→ Postgres = monolith DB. 작은 팀 = 강력.
|
|
```
|
|
|
|
### When NOT to use
|
|
```
|
|
- 큰 throughput / 분산 — Citus / Yugabyte
|
|
- Real-time analytics (PB) — ClickHouse / Druid
|
|
- 강력 search — Elasticsearch
|
|
- Real-time messaging — Kafka
|
|
- 큰 vector (1B+) — Vespa / Milvus
|
|
```
|
|
|
|
### Migration path
|
|
```
|
|
Start: Postgres + extensions (작은 stack).
|
|
Grow: 일부 = 별 system (Kafka, ClickHouse).
|
|
End: Specialized stack.
|
|
|
|
→ Premature specialization X.
|
|
PG 가 90% case 충분.
|
|
```
|
|
|
|
### Backup with extensions
|
|
```bash
|
|
pg_dump --extensions=all -d mydb > backup.sql
|
|
|
|
# Or specific
|
|
pg_dump --extension=pg_cron --extension=vector -d mydb
|
|
```
|
|
|
|
### Test
|
|
```ts
|
|
// Test 가 같은 extension 가짐
|
|
beforeAll(async () => {
|
|
await db.execute(`CREATE EXTENSION IF NOT EXISTS pg_trgm`);
|
|
});
|
|
```
|
|
|
|
## 🤔 의사결정 기준
|
|
| 사용 | 추천 extension |
|
|
|---|---|
|
|
| Vector search | pgvector |
|
|
| Time-series | TimescaleDB |
|
|
| Sharding | Citus |
|
|
| Geo | PostGIS |
|
|
| Search | pg_trgm + tsvector |
|
|
| Cron | pg_cron |
|
|
| Queue | pgmq |
|
|
| Crypto | pgcrypto |
|
|
|
|
## ❌ 안티패턴
|
|
- **Cloud 가 안 지원 — extension 가정**: 검사.
|
|
- **Major upgrade 시 extension 호환 X**: 검증.
|
|
- **Extension 너무 많이**: 의존 복잡.
|
|
- **자체 patch — upstream 무시**: 유지 어려움.
|
|
- **Production 가 latest minor**: 검증.
|
|
|
|
## 🤖 LLM 활용 힌트
|
|
- Postgres + 5 ~ 10 extension = 큰 stack.
|
|
- pgvector + pg_cron + pgmq = mini SaaS.
|
|
- Cloud 의 supported list 확인.
|
|
- 점진 도입.
|
|
|
|
## 🔗 관련 문서
|
|
- [[DB_pgvector_Production]]
|
|
- [[DB_Time_Series_Patterns]]
|
|
- [[DB_Sharding_Strategies]]
|