PostgreSQL + Redis Cache Architecture for Next.js: Production Guide

Web Görsel
Why Three Layers?
- Edge cache: Closest to user; TTFB drops to 20-50 ms
- Redis: Shared in-memory cache; ms-level queries
- PostgreSQL: Source of truth, transactional integrity
Layer 1: Edge Cache (Cloudflare / Vercel)
// app/blog/[slug]/page.tsx
export const revalidate = 300; // 5 min ISR
export default async function Page({ params }) {
const post = await getPost(params.slug);
return <Article data={post} />;
}
Layer 2: Redis (Application Cache)
- Frequently computed aggregations (homepage popular products)
- User sessions
- Rate-limit counters
- Third-party API responses (exchange rates, weather)
- Job queues (Bull/BullMQ)
import { createClient } from ''redis'';
const redis = createClient({ url: process.env.REDIS_URL });
await redis.connect();
export async function cache<T>(key: string, fn: () => Promise<T>, ttl = 60) {
const cached = await redis.get(key);
if (cached) return JSON.parse(cached) as T;
const data = await fn();
await redis.setEx(key, ttl, JSON.stringify(data));
return data;
}
Layer 3: PostgreSQL
Performance Tuning
shared_buffers= 25% of RAMeffective_cache_size= 75% of RAMwork_mem= 32 MB × connections (rough)- Composite indexes on hot queries
- Slow query log enabled, reviewed weekly
PgBouncer (Mandatory)
In serverless/Vercel-style environments each function opens a new Postgres connection — pool depletes quickly. PgBouncer transaction pooling serves thousands of app connections through 20 real Postgres connections.
Invalidation Strategies
Time-based (TTL)
Simplest. Expires after 5 minutes. Good when minor staleness is tolerable.
Tag-based (Next.js 14+)
import { unstable_cache, revalidateTag } from ''next/cache'';
const getProduct = unstable_cache(
async (id) => db.products.findUnique({ where: { id } }),
[''product''],
{ tags: [''product''] }
);
revalidateTag(''product'');
Event-driven
Postgres LISTEN/NOTIFY signals Redis on DB changes; relevant key is invalidated.
Cache Warming
- Cron every hour fetches homepage + popular categories
- Post-build fetches first 100 sitemap URLs
- New product creation triggers related category warm
Real Production Architecture
User
|
v
Cloudflare (edge cache, DDoS, WAF)
| (miss)
v
Vercel / Node.js Next.js
|
+-- Redis (sessions, rate limit, aggregates)
|
+-- PgBouncer
|
v
PostgreSQL (read replica + primary)
Monitoring
- Redis cache hit ratio: > 80% healthy, > 95% excellent
- Postgres slow queries (> 200 ms): weekly report
- Cloudflare cache status: X-Cache-Status header
- p95, p99 response times (averages mislead)
Security
- Redis AUTH and ACL (Redis 6+)
- PostgreSQL only from private network
- Connection strings in secrets manager
- Per-user cache keys when caching user-scoped data
Common Mistakes
- Treating cache as a fix for slow queries
- Setting infinite TTL and serving stale data
- Caching user-specific data under shared keys (leak risk)
- Connecting to Postgres from serverless without PgBouncer
- No versioning in cache keys (post-deploy stale data)
- Storing very large JSON in Redis (1 MB+ discouraged)
FAQs
Redis vs memcached?
Redis wins on pub/sub, persistence, data structures. Modern default.
Can Postgres be both cache and source?
Yes — materialized views + pg_cron. For small projects Redis-less is fine. High traffic needs Redis.
Is ISR enough without Redis?
ISR caches HTML; Redis caches data. Different problems.
How to raise cache hit ratio?
Longer TTLs + tag-based invalidation + better key design + pre-warming hot data.
Next Step
Set up Next.js + Postgres + Redis cache architecture — technical call.
Yorumlar (0)
Bu konuda yardima mi ihtiyaciniz var?
Ekibimiz, projenize en uygun cozumleri sunmak icin hazir.
Iletisime Gecin