惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
Cisco Blogs
Cyberwarzone
Cyberwarzone
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
SecWiki News
SecWiki News
Martin Fowler
Martin Fowler
T
Tor Project blog
N
Netflix TechBlog - Medium
C
Cybersecurity and Infrastructure Security Agency CISA
V
Vulnerabilities – Threatpost
V
Visual Studio Blog
GbyAI
GbyAI
PCI Perspectives
PCI Perspectives
D
DataBreaches.Net
Jina AI
Jina AI
H
Heimdal Security Blog
云风的 BLOG
云风的 BLOG
P
Privacy International News Feed
A
About on SuperTechFans
J
Java Code Geeks
美团技术团队
H
Hackread – Cybersecurity News, Data Breaches, AI and More
N
News | PayPal Newsroom
有赞技术团队
有赞技术团队
MyScale Blog
MyScale Blog
博客园 - 司徒正美
C
Check Point Blog
T
Threat Research - Cisco Blogs
Attack and Defense Labs
Attack and Defense Labs
宝玉的分享
宝玉的分享
AI
AI
Simon Willison's Weblog
Simon Willison's Weblog
C
Cyber Attacks, Cyber Crime and Cyber Security
I
Intezer
P
Proofpoint News Feed
Blog — PlanetScale
Blog — PlanetScale
Apple Machine Learning Research
Apple Machine Learning Research
Hugging Face - Blog
Hugging Face - Blog
The Last Watchdog
The Last Watchdog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Vercel News
Vercel News
I
InfoQ
阮一峰的网络日志
阮一峰的网络日志
Cisco Talos Blog
Cisco Talos Blog
W
WeLiveSecurity
Hacker News: Ask HN
Hacker News: Ask HN
Recent Commits to openclaw:main
Recent Commits to openclaw:main
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
D
Docker
博客园 - Franky
Security Archives - TechRepublic
Security Archives - TechRepublic

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Great Stack to Doesn't Work #3 — Redis: "99% Cache Hit Ratio, System Down"
Mehmet TURAÇ · 2026-05-31 · via DEV Community

A survival guide for when everything goes wrong in production.


Your Redis dashboard looks perfect. Hit ratio: 99.2%. Latency: sub-millisecond. Memory usage: 60% of available. Every metric says healthy.

Then at 2:47 PM, your API starts returning 500s. Response times spike to 30 seconds. Users can't log in. The dashboard still shows 99% hit ratio because the cache is working — it's serving cached errors to everyone equally fast.

Redis is doing exactly what you told it to do. The problem is what you told it to do.


Why Single-Threaded Is Fast (Until It Isn't)

Redis processes commands on a single thread. No locks. No context switching. No synchronization overhead. One CPU core, fully utilized, can handle 100K+ operations per second because it never waits for another thread to release a lock.

The event loop model (similar to Node.js) multiplexes thousands of client connections on a single thread using non-blocking I/O. Read a request, process it, write the response, move to the next. When your commands are simple — GET, SET, INCR — each one takes microseconds.

The trap: slow commands block everything. KEYS * on a million-key database? That's a full keyspace scan on the main thread. While it runs, every other client waits. SORT on a large set? Same. LRANGE on a list with 10 million elements? Same.

Redis 6.0 introduced I/O threading (io-threads config) for reading and writing network data on multiple threads, but command execution is still single-threaded. Redis 7.0 improved this further, but the fundamental model hasn't changed. Long-running commands on the main thread stall everything.

Rules:

  • Never use KEYS in production. Use SCAN instead — it's cursor-based and returns results incrementally.
  • Watch out for O(N) commands on large data structures: LRANGE, SMEMBERS, HGETALL on million-element structures.
  • Use SLOWLOG to find commands that are blocking the event loop.

Pipelining: The Easiest 10x You'll Ever Get

Every Redis command involves a network round trip: send request, wait for response. If you're executing 100 commands sequentially, that's 100 round trips. At 0.5ms per round trip, you're waiting 50ms for what should take 1ms of actual processing.

Pipelining batches commands into a single network write and reads all responses at once.

pipe = redis.pipeline()
for user_id in user_ids:
    pipe.get(f"user:{user_id}:profile")
results = pipe.execute()

Instead of 100 round trips, you make 1. The server processes all commands in sequence (it's single-threaded, remember) and buffers the responses. Your client sends the batch, waits once, and gets everything back.

Pipelining doesn't reduce server-side processing time — each command still runs individually. It eliminates network latency, which is almost always the dominant cost for simple commands.

The catch: if one command in the pipeline fails, the others still execute. Pipelining is not transactional. If you need atomicity, use MULTI/EXEC or Lua scripts.


Lua Scripting: Atomic Operations Without the Complexity

Redis evaluates Lua scripts atomically. While a script runs, nothing else executes. This makes Lua scripts the right tool for read-modify-write operations that would otherwise need distributed locking.

Classic example — rate limiting:

-- KEYS[1] = rate limit key
-- ARGV[1] = max requests
-- ARGV[2] = window in seconds
local current = redis.call('INCR', KEYS[1])
if current == 1 then
    redis.call('EXPIRE', KEYS[1], ARGV[2])
end
if current > tonumber(ARGV[1]) then
    return 0  -- rate limited
end
return 1  -- allowed

This increments a counter and sets expiry atomically. No race condition between INCR and EXPIRE. No chance of two requests both reading "0" and both thinking they're first.

Use EVALSHA instead of EVAL in production. EVALSHA references the script by its SHA1 hash, avoiding sending the full script text with every call. Load the script once with SCRIPT LOAD, then call it by hash.

Caveat: Lua scripts block the main thread for their entire duration. Keep them short. A script that queries 10 keys is fine. A script that iterates over 100,000 keys is a production incident waiting to happen.


Pub/Sub vs Streams: Two Very Different Tools

Pub/Sub is fire-and-forget. Publisher sends a message, all connected subscribers receive it instantly. If a subscriber disconnects and reconnects, it misses everything published while it was gone. No message persistence. No consumer groups. No acknowledgment.

Use Pub/Sub for: real-time notifications where missing a message is acceptable. Chat typing indicators. Cache invalidation signals. Dashboard live updates.

Streams (introduced in Redis 5.0) are persistent, append-only logs with consumer groups. Think of them as "Kafka Lite inside Redis."

XADD orders * user_id 42 amount 99.99
XREADGROUP GROUP payment_processors consumer_1 COUNT 10 BLOCK 5000 STREAMS orders >
XACK orders payment_processors 1234567890-0

Streams persist messages. Consumer groups track which consumer has read what. Unacknowledged messages can be claimed by other consumers if one dies. You get at-least-once delivery semantics.

Use Streams for: job queues, event sourcing, lightweight message processing where you don't want to deploy Kafka but need more than Pub/Sub.

Don't use Streams to replace Kafka at scale. Redis Streams are bounded by single-node memory. Kafka is designed for multi-broker distributed throughput. Different tools, different scale.


Memory Eviction: The Policy That Saves or Kills You

When Redis hits maxmemory, it needs to decide what to delete. The eviction policy determines what goes.

noeviction: Redis returns errors for write commands. Reads still work. Use this when you absolutely cannot lose data and you'd rather fail loudly than silently corrupt your cache. Common for session stores.

allkeys-lru: Evicts the least recently used key across all keys. The safest general-purpose policy. If you're using Redis purely as a cache, this is your default.

volatile-lru: Only evicts keys with a TTL set. Keys without TTL are never evicted. Use this when you have a mix of permanent data (config, feature flags) and cache data (user sessions, query results). The permanent data stays; the cache data gets evicted under pressure.

allkeys-lfu (Least Frequently Used): Evicts keys accessed least often, regardless of recency. Better than LRU when you have a mix of frequently-accessed hot data and occasionally-accessed warm data. A key accessed 1,000 times yesterday but not today won't be evicted as quickly as with LRU.

The disaster scenario: noeviction on a cache. Redis fills up. Every write fails. Your application treats the write failure as a cache miss and hits the database directly. Now your database is handling the full load that Redis was supposed to absorb. The database slows down. API latency spikes. Cascading failure.

Monitor evicted_keys in Redis INFO stats. A sudden spike means you're running out of memory and eviction is kicking in aggressively. Either add memory or investigate why your keyspace is growing.


Persistence: RDB vs AOF vs "I Thought Redis Was Just a Cache"

Many teams deploy Redis without persistence, treating it as a pure cache. Then the server restarts and 6 hours of cached data vanishes. Cold cache stampede: every request hits the database simultaneously.

RDB (snapshotting): Redis forks the process and writes the entire dataset to disk at intervals. Fast restores. Compact files. But you can lose data between snapshots — if Redis saves every 5 minutes and crashes 4 minutes after the last save, those 4 minutes are gone.

AOF (Append Only File): Redis logs every write operation. Three sync modes: always (fsync every write — safe but slow), everysec (fsync every second — good balance), no (let the OS decide — fastest but risky). On restart, Redis replays the log to rebuild state.

RDB + AOF: Use both. RDB for fast restores and backups. AOF for durability. On restart, Redis prefers AOF because it's more complete.

The real question: what happens to your system when Redis restarts with an empty cache? If the answer is "everything melts," you need persistence. If the answer is "things are slow for a few minutes while the cache warms up," maybe you don't — but you should still have RDB snapshots for disaster recovery.


The Thundering Herd: Cache Invalidation's True Face

You cache a popular product page for 5 minutes. 10,000 users are viewing it. The TTL expires. All 10,000 requests simultaneously hit the database for the same data. The database buckles under the sudden spike.

This is the thundering herd problem, and it's not theoretical. Any high-traffic system with TTL-based caching will encounter it.

Solutions:

Staggered TTLs. Add random jitter to expiration times: TTL = base_ttl + random(0, 60). Keys expire at different times, spreading the database load.

Lock-based refresh. When a key expires, only one request acquires a lock and rebuilds the cache. All others wait or serve stale data. Implementation with Lua:

local value = redis.call('GET', KEYS[1])
if value then return value end
local lock = redis.call('SET', KEYS[1] .. ':lock', 'locked', 'NX', 'EX', 5)
if lock then
    return nil  -- caller rebuilds cache
else
    return redis.call('GET', KEYS[1])  -- wait for rebuild
end

Early refresh. Refresh the cache before it expires. If TTL is 5 minutes, start a background refresh at 4 minutes. The cache never actually expires under normal operation.


How We Crashed Production 3 Times

Crash #1: Hot key. A flash sale product page was cached under a single key. 500,000 requests per second hit that one key. Redis can handle the throughput, but the single-threaded nature means this one key's reads were queuing behind each other. Latency spiked to 50ms — fine for one request, fatal for the 499,999 behind it.

Fix: cache the hot key locally in-process with a short TTL (1-2 seconds). Application memory serves 99% of requests, Redis serves the refresh.

Crash #2: Serialization bomb. Someone cached a full user object including activity history — 50MB serialized. Every time the app read that key, Redis had to send 50MB over the network. The single thread was blocked for 200ms per read. At 100 concurrent reads, the event loop was saturated.

Fix: cache only what you need. User profile: 2KB. User activity: separate key, paginated, never cached as a monolith.

Crash #3: Cache invalidation race. Service A updates a user record in the database and deletes the cache key. Service B reads the cache, gets a miss, reads the stale data from a read replica (replication lag), and writes the stale data back to cache. Now the cache has stale data and it won't refresh until the TTL expires.

Fix: don't write to cache after a miss if the data might be stale. Use read-from-primary for cache rebuilds, or use a TTL short enough that stale data self-corrects quickly.


When Redis, When Memcached?

This is a shorter decision than people make it.

Redis when: you need data structures beyond key-value (lists, sets, sorted sets, hashes, streams), persistence, pub/sub, Lua scripting, cluster mode, or any feature beyond simple caching.

Memcached when: you need a simple, multi-threaded cache with predictable memory allocation and you're caching large blobs (images, rendered HTML). Memcached's multi-threaded architecture handles large-value workloads more efficiently than Redis's single-threaded model.

In practice: Redis, almost always. The feature set is so much broader that the rare cases where Memcached wins are outweighed by Redis's versatility. The exception is if you're caching very large objects at very high throughput and you're hitting Redis's single-threaded bottleneck. Then Memcached's multi-threaded reads genuinely help.


Key Takeaways

Redis is fast by default and slow by mistake. The mistakes are predictable: slow commands on the main thread, missing pipelining, wrong eviction policy, no persistence on a critical cache, and hot keys.

Monitor commandstats to see which commands are running. Monitor slowlog to find the ones that are too slow. Monitor evicted_keys to know when you're running out of memory.

The 99% hit ratio dashboard doesn't mean your cache is healthy. It means your cache is serving something fast. Whether that something is correct, fresh, and useful — that's a different question.



Over to You

What's your worst Redis incident? Hot key? Thundering herd? Wrong eviction policy? The cache invalidation race condition stories are always the best.


If you enjoyed this, I write about production engineering, AI systems, and the messy reality of building software at scale.

Follow me:

This is part of the **Great Stack to Doesn't Work* series — a survival guide for when everything goes wrong in production. Follow the series to catch every episode.*