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

推荐订阅源

SecWiki News
SecWiki News
H
Help Net Security
罗磊的独立博客
Stack Overflow Blog
Stack Overflow Blog
M
MIT News - Artificial intelligence
Jina AI
Jina AI
L
LangChain Blog
K
Kaspersky official blog
I
Intezer
Martin Fowler
Martin Fowler
爱范儿
爱范儿
AWS News Blog
AWS News Blog
The Hacker News
The Hacker News
Recorded Future
Recorded Future
人人都是产品经理
人人都是产品经理
H
Hackread – Cybersecurity News, Data Breaches, AI and More
C
CXSECURITY Database RSS Feed - CXSecurity.com
Spread Privacy
Spread Privacy
Simon Willison's Weblog
Simon Willison's Weblog
U
Unit 42
N
News and Events Feed by Topic
A
Arctic Wolf
G
GRAHAM CLULEY
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 聂微东
F
Fortinet All Blogs
C
Cisco Blogs
美团技术团队
Vercel News
Vercel News
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
H
Hacker News: Front Page
T
Tailwind CSS Blog
I
InfoQ
宝玉的分享
宝玉的分享
Google DeepMind News
Google DeepMind News
博客园 - 司徒正美
P
Palo Alto Networks Blog
A
About on SuperTechFans
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
云风的 BLOG
云风的 BLOG
TaoSecurity Blog
TaoSecurity Blog
Google Online Security Blog
Google Online Security Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Privacy & Cybersecurity Law Blog
H
Heimdal Security Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Hacker News: Ask HN
Hacker News: Ask HN
O
OpenAI News
博客园 - Franky
Scott Helme
Scott Helme

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
Redis além do tutorial. Com os problemas que ninguém te conta
Milton Camar · 2026-05-12 · via DEV Community

Seu banco de dados é rápido. As queries estão otimizadas. Os índices estão no lugar. E mesmo assim, sob carga real, o sistema continua lento.

O problema muitas vezes não é o banco em si. É que você está pedindo para ele responder as mesmas perguntas, repetidamente, milhares de vezes por segundo.

É aqui que o Redis começa a importar.

Mas ao contrário do que muitos tutoriais sugerem, o Redis não é mágica. É uma ferramenta com trade-offs muito específicos — e usá-la errado pode criar problemas piores do que os que você tentou resolver.


Como o Redis Funciona Por Dentro

Redis armazena tudo em RAM como pares chave–valor, mas os valores não se limitam a strings simples. Ele suporta um conjunto rico de estruturas de dados:

Estrutura Casos de uso típicos
Strings Contadores, flags, resultados em cache
Hashes Perfis de usuário, dados de sessão
Lists Filas, feeds de atividade recente
Sets Tags, relacionamentos únicos
Sorted Sets Leaderboards, ranking com score
Streams Log de eventos com consumer groups

Cada estrutura vem com comandos otimizados que operam diretamente nos dados. Isso é a chave: em vez de buscar tudo e processar na sua aplicação, você delega a operação ao Redis.

A diferença entre ZRANGE leaderboard 0 9 REV WITHSCORES e buscar 10.000 linhas e ordenar no código é drástica em escala.

Por que RAM Não é o Único Motivo

A maioria das pessoas assume que Redis é rápido "porque usa RAM". Isso é verdade, mas incompleto.

O ganho real está em evitar round-trips desnecessários ao banco. Mesmo em redes locais de baixa latência, cada chamada TCP ao banco custa entre 0.5ms e 5ms. Com 500 usuários simultâneos fazendo 10 requests por segundo cada, você está fazendo 5.000 chamadas/segundo ao banco, sendo que boa parte delas responde a mesma pergunta.

Redis elimina essa camada de comunicação para os dados mais acessados. O hit em cache retorna em microssegundos não só porque está em RAM, mas porque está na mesma máquina, sem handshake TCP, sem parser de query, sem plano de execução.

O Event Loop Single-Threaded

Redis usa um event loop de thread única, um comando executa de cada vez.

Isso parece limitante. Na prática, é uma escolha deliberada e que funciona bem para workloads típicos: sem locks, sem contenção, operações atomicamente previsíveis. Em benchmarks, o Redis processa centenas de milhares de operações por segundo em hardware comum porque cada operação é pequena, previsível e não bloqueia outras.

O modelo escala bem enquanto os comandos são simples e rápidos, que é o caso da esmagadora maioria dos usos reais de Redis.

Onde isso vira problema: A partir do Redis 6.0, o I/O de rede passou a ser multi-threaded, mas a execução dos comandos continua single-threaded. Comandos lentos como KEYS *, SORT sem LIMIT, ou LRANGE em listas muito grandes bloqueiam todo o servidor enquanto executam. Um único comando mal escrito pode degradar o sistema inteiro. É por isso que KEYS * em produção é considerado um erro grave.

Persistência: Você Escolhe o Risco

Redis é in-memory, mas pode persistir dados de duas formas:

  • RDB (Redis Database): Snapshots periódicos. Mais rápido, mas você pode perder as escritas desde o último snapshot.
  • AOF (Append-Only File): Registra cada operação de escrita. Mais durável, mas gera arquivos grandes e pode impactar latência dependendo do fsync configurado (always, everysec, no).

Você pode usar ambos simultaneamente. A configuração padrão (appendfsync everysec) aceita perder até 1 segundo de dados em caso de crash, aceitável para cache, problemático para dados financeiros.


Onde o Redis Realmente Brilha (Com Código)

1. Caching - O Padrão Cache-Aside

O caso de uso mais comum. A aplicação verifica o Redis primeiro; em um hit, os dados retornam em microssegundos; em um miss, a aplicação consulta o banco e popula o Redis.

Em .NET, a abstração mais portável é IDistributedCache:

public class ProductService
{
    private readonly IDistributedCache _cache;
    private readonly IProductRepository _repository;
    private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(10);

    public ProductService(IDistributedCache cache, IProductRepository repository)
    {
        _cache = cache;
        _repository = repository;
    }

    public async Task<Product?> GetByIdAsync(int id)
    {
        var cacheKey = $"product:{id}";

        var cached = await _cache.GetStringAsync(cacheKey);
        if (cached is not null)
            return JsonSerializer.Deserialize<Product>(cached);

        var product = await _repository.GetByIdAsync(id);
        if (product is null) return null;

        await _cache.SetStringAsync(
            cacheKey,
            JsonSerializer.Serialize(product),
            new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = CacheDuration
            });

        return product;
    }
}

Enter fullscreen mode Exit fullscreen mode

Configuração no Program.cs:

builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = builder.Configuration.GetConnectionString("Redis");
    options.InstanceName = "myapp:";
});

Enter fullscreen mode Exit fullscreen mode

Quando isso funciona bem: Para dados lidos muito mais frequentemente do que mudam. A chave é ter um TTL razoável e aceitar que usuários diferentes podem ver versões ligeiramente diferentes do dado durante a janela de cache.


2. Rate Limiting — Atomicidade é Tudo

Redis torna rate limiting simples através de operações atômicas. Com StackExchange.Redis:

public class RedisRateLimiter
{
    private readonly IDatabase _db;

    public RedisRateLimiter(IConnectionMultiplexer redis)
    {
        _db = redis.GetDatabase();
    }

    public async Task<bool> IsAllowedAsync(string userId, int maxRequests, TimeSpan window)
    {
        var key = $"rate_limit:{userId}:{DateTimeOffset.UtcNow.ToUnixTimeSeconds() / (long)window.TotalSeconds}";

        var current = await _db.StringIncrementAsync(key);

        if (current == 1)
            await _db.KeyExpireAsync(key, window);

        return current <= maxRequests;
    }
}

Enter fullscreen mode Exit fullscreen mode

Este padrão usa uma janela fixa (fixed window). É simples, mas tem um edge case: um usuário pode fazer o dobro de requisições na virada da janela. Para uso mais preciso, o padrão sliding window usa Sorted Sets, cada requisição é inserida com timestamp como score, e você remove as entradas fora da janela antes de contar.


3. Session Storage

Sessions precisam de leituras e escritas rápidas em cada requisição. Redis se encaixa porque:

  • Latência baixa sem delay por request
  • TTL nativo, sessões expiram automaticamente sem cleanup jobs
  • Alta throughput para bases de usuários grandes

No ASP.NET Core, a configuração é direta:

builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "localhost:6379";
});

builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(30);
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
});

Enter fullscreen mode Exit fullscreen mode


4. Pub/Sub vs. Streams — Saiba a Diferença

Redis tem Pub/Sub built-in:

// Publisher
var sub = multiplexer.GetSubscriber();
await sub.PublishAsync("order:created", JsonSerializer.Serialize(order));

// Subscriber
await sub.SubscribeAsync("order:created", (channel, message) =>
{
    var order = JsonSerializer.Deserialize<Order>(message!);
    // processar pedido
});

Enter fullscreen mode Exit fullscreen mode

Pub/Sub é simples e eficiente, mas tem uma limitação fundamental: mensagens são fire-and-forget. Se nenhum subscriber estiver ouvindo no momento do publish, a mensagem é perdida para sempre. Não há histórico, não há replay, não há garantia de entrega.

Para workloads onde isso importa, Redis Streams é a alternativa correta. A diferença conceitual é significativa:

Pub/Sub Streams
Persistência Nenhuma — mensagem some após entrega Persistida no log até você deletar
Consumer groups Não — todos recebem tudo Sim — múltiplos consumers dividem as mensagens
Replay Impossível Sim — leitura por offset ou timestamp
Pending entries Não existe Sim — mensagens entregues mas não confirmadas ficam no PEL
Caso de uso Notificações em tempo real, broadcast Pipelines de eventos, filas duráveis

Com Streams, um consumer group garante que cada mensagem seja processada por exatamente um consumer do grupo. Se o consumer falha antes de confirmar (XACK), a mensagem fica no Pending Entries List (PEL) e pode ser reclamada e reprocessada. Isso não existe no Pub/Sub.

Resumindo: use Pub/Sub para broadcast em tempo real onde perder mensagens é tolerável. Use Streams quando você precisa de garantias de entrega, múltiplos consumers independentes ou auditoria de eventos.


Benefícios vs. Trade-offs

Benefício Trade-off
Velocidade Microssegundos de latência, sem round-trips ao banco RAM é cara por GB; inviável para datasets grandes e frios
Simplicidade API rica e intuitiva Operações relacionais e agregações complexas não existem
Flexibilidade Múltiplos data structures otimizados Modelagem exige pensar diferente de um banco relacional
Durabilidade Configurável (RDB + AOF) Por padrão, risco de perda de dados em crash
Operação Cloud-managed disponível Cluster mode tem complexidade; resharding não é trivial

As Armadilhas Que Aparecem em Produção

Esta é a parte que a maioria dos artigos ignora. Redis em produção tem problemas específicos que se manifestam sob carga real.

Cache Stampede (Thundering Herd)

Imagine 10.000 usuários acessando simultaneamente um dado cujo cache acabou de expirar. Todos consultam o banco ao mesmo tempo. O banco sofre um spike de carga. Paradoxalmente, o cache que deveria proteger o banco o derruba no exato momento em que expira.

Como mitigar:

1. Probabilistic Early Expiration (PER): Antes do TTL expirar, alguns processos já começam a renovar o cache de forma probabilística:

public async Task<T?> GetWithEarlyRenewalAsync<T>(
    string key,
    Func<Task<T>> factory,
    TimeSpan ttl,
    double beta = 1.0)
{
    var db = _redis.GetDatabase();
    var raw = await db.StringGetWithExpiryAsync(key);

    if (raw.Value.HasValue)
    {
        var remaining = raw.Expiry ?? TimeSpan.Zero;
        var recomputeTime = EstimatedRecomputeTime; // medir em produção

        // Decide probabilisticamente se renova antes de expirar
        var shouldRenew = -recomputeTime.TotalSeconds * beta * Math.Log(Random.Shared.NextDouble()) >= remaining.TotalSeconds;

        if (!shouldRenew)
            return JsonSerializer.Deserialize<T>(raw.Value!);
    }

    var result = await factory();
    await db.StringSetAsync(key, JsonSerializer.Serialize(result), ttl);
    return result;
}

Enter fullscreen mode Exit fullscreen mode

2. Mutex/Lock no miss com ownership: Apenas um processo refaz a query; os outros aguardam ou retornam o dado stale temporariamente. Mas atenção ao bug clássico desta abordagem: se o processo que adquiriu o lock demorar mais do que o TTL do lock para terminar, o lock expira, outro processo o adquire, e o processo original — ao finalizar — deleta o lock do processo errado, criando uma race condition silenciosa.

A solução é usar um token de ownership: só quem criou o lock pode deletá-lo.

var lockKey = $"lock:{cacheKey}";
var lockToken = Guid.NewGuid().ToString(); // token único por processo

var lockAcquired = await _db.StringSetAsync(
    lockKey, lockToken, TimeSpan.FromSeconds(5), When.NotExists);

if (lockAcquired)
{
    try
    {
        var fresh = await _repository.GetByIdAsync(id);
        await _cache.SetStringAsync(cacheKey, JsonSerializer.Serialize(fresh), options);
        return fresh;
    }
    finally
    {
        // Deleta APENAS se o token ainda é o nosso — operação atômica via Lua
        const string releaseLua = @"
            if redis.call('get', KEYS[1]) == ARGV[1] then
                return redis.call('del', KEYS[1])
            else
                return 0
            end";
        await _db.ScriptEvaluateAsync(releaseLua, [(RedisKey)lockKey], [lockToken]);
    }
}
else
{
    await Task.Delay(50);
    var retry = await _cache.GetStringAsync(cacheKey);
    return retry is not null ? JsonSerializer.Deserialize<Product>(retry) : null;
}

Enter fullscreen mode Exit fullscreen mode

Para distributed locking em ambientes com múltiplas instâncias Redis (cluster ou sentinel), o algoritmo RedLock oferece garantias mais fortes, adquirindo o lock em N instâncias independentes. A biblioteca RedLock.net implementa isso para .NET. Para a maioria dos casos com uma instância Redis, o padrão acima com token é suficiente.


Key Eviction: O Redis Descarta Dados Silenciosamente

Quando a memória do Redis está cheia e maxmemory está configurado, o Redis precisa remover chaves. A política padrão (noeviction) retorna erros de escrita — o que derruba a aplicação silenciosamente se não for tratado.

As políticas mais comuns:

Política Comportamento
noeviction Erros nas escritas quando memória cheia. Seguro para filas.
allkeys-lru Remove as chaves menos recentemente usadas. Boa para cache puro.
volatile-lru LRU apenas em chaves com TTL definido.
allkeys-random Remove aleatoriamente. Raramente é o que você quer.
volatile-ttl Remove as chaves com TTL mais curto primeiro.

Erro comum: usar Redis para cache e para dados persistentes (sessões, filas) na mesma instância sem separar políticas de eviction. Quando a memória enche, o Redis pode descartar uma sessão ativa ou uma mensagem de fila. Use instâncias separadas ou, no mínimo, chaves com TTL apenas no que pode ser descartado.


Hot Keys em Cluster Mode

Em uma instância Redis única, toda a carga vai para o mesmo processo — e ele aguenta bem. Em cluster mode, as chaves são distribuídas entre shards por hash slot. O problema: se uma chave específica é acessada muito mais do que as outras (uma página inicial, um produto em promoção, uma configuração global), ela concentra carga em um único shard enquanto os outros ficam ociosos.

Isso anula o benefício de escala horizontal do cluster.

Como detectar: o comando redis-cli --hotkeys (disponível com maxmemory-policy configurada) ou monitoramento via MONITOR em ambiente de staging.

Como mitigar:

  • Key sharding local: criar múltiplas chaves com sufixos (config:app:1, config:app:2, ...) e distribuir as leituras entre elas.
  • Client-side caching: Redis 6.0 introduziu client-side caching via o protocolo CLIENT TRACKING, permitindo que o cliente mantenha uma cópia local e invalide apenas quando o servidor notifica mudança — eliminando o round-trip para hot keys.
  • Cache em memória local: para dados imutáveis ou de baixíssima variação, um IMemoryCache na aplicação serve como primeira camada antes do Redis.

Memory Fragmentation

Redis aloca e libera memória constantemente. Com o tempo, a fragmentação cresce: o processo ocupa mais RAM do que os dados realmente precisam.

O indicador é o mem_fragmentation_ratio no INFO memory. Valores acima de 1.5 indicam fragmentação significativa. Acima de 2.0, você provavelmente precisa de um restart controlado ou de habilitar activedefrag (disponível desde Redis 4.0):

activedefrag yes
active-defrag-ignore-bytes 100mb
active-defrag-threshold-lower 10

Enter fullscreen mode Exit fullscreen mode


O Problema do KEYS * em Produção

KEYS * bloqueia o event loop single-threaded enquanto escaneia todo o keyspace. Em instâncias com milhões de chaves, isso pode causar segundos de indisponibilidade.

Nunca use KEYS * em produção. Use SCAN com cursor:

var cursor = 0L;
var pattern = "product:*";

do
{
    var result = await _db.ExecuteAsync("SCAN", cursor.ToString(), "MATCH", pattern, "COUNT", "100");
    var array = (RedisResult[])result!;
    cursor = (long)array[0];
    var keys = (RedisResult[])array[1];

    foreach (var key in keys)
        Console.WriteLine(key);

} while (cursor != 0);

Enter fullscreen mode Exit fullscreen mode

SCAN não garante ausência de duplicatas, mas não bloqueia o servidor — e é iterativo, o que permite processar keyspaces enormes sem travar nada.


O que Monitorar em Produção

Redis falha de formas silenciosas: evicta dados sem avisar, fragmenta memória gradualmente, acumula latência em comandos específicos. Sem observabilidade, você descobre o problema quando o usuário reclama.

As métricas essenciais do INFO para monitorar:

Métrica O que indica
evicted_keys Chaves removidas por pressão de memória. Zero é o ideal; qualquer valor crescente é alerta.
used_memory_rss vs used_memory A diferença é a fragmentação. Se rss for muito maior que used_memory, você tem fragmentação.
instantaneous_ops_per_sec Throughput atual. Útil para correlacionar com picos de latência.
latency (via LATENCY HISTORY) Latência por evento. Detecta comandos lentos específicos.
connected_clients Pico de conexões pode indicar connection pool mal configurado.
keyspace_hits vs keyspace_misses A taxa de hit é o indicador mais direto de eficiência do cache.

Em ambientes cloud (ElastiCache, Azure Cache for Redis), essas métricas são exportadas para CloudWatch/Azure Monitor nativamente. Em ambientes self-hosted, ferramentas como Redis Exporter + Prometheus + Grafana são o padrão de mercado.


Quando Não Usar Redis

Redis não é a ferramenta certa quando:

  • Dataset grande e frio: RAM custa muito por GB. Use um banco em disco e faça cache apenas do subconjunto quente.
  • Queries relacionais e agregações complexas: "Todos os usuários da Região A que compraram o Produto B nos últimos 30 dias" exige um banco relacional com suporte a joins e índices compostos. Redis não é otimizado para esse tipo de acesso, ele recupera por chave, não por predicado sobre os dados.
  • Durabilidade forte: Se perder segundos de escrita é inaceitável (transações financeiras, registros médicos), o modelo de persistência do Redis exige configuração cuidadosa, e um banco ACID-compliant é a opção mais segura.
  • Workload stateless e simples: Para sistemas sem cache, sem sessões e sem mensageria, adicionar Redis introduz complexidade operacional sem benefício proporcional.

Redis no Stack Real

Em arquiteturas de microsserviços, o Redis assume um segundo papel como datastore compartilhado rápido: um session store central que todos os frontends consultam, um rate limiter que todos os API gateways respeitam, ou um message broker leve que conecta serviços sem o overhead de Kafka ou RabbitMQ.

Provedores cloud oferecem Redis gerenciado (AWS ElastiCache, Azure Cache for Redis, Google Cloud Memorystore) que cuidam de provisionamento, scaling e failover. Isso facilita inserir Redis numa stack existente sem gerenciar infraestrutura diretamente.

Uma alternativa que ganhou tração recentemente é o Garnet, lançado pela Microsoft em 2024 como projeto open source. É compatível com o protocolo RESP do Redis, escrito em C#, e apresenta throughput superior em alguns benchmarks específicos de .NET. Ainda é jovem e não tem o ecossistema do Redis, mas vale acompanhar se você roda .NET.


Conclusão

Redis é um multiplicador de performance, não um substituto de banco de dados.

Ele não tenta resolver tudo. Resolve um conjunto estreito de problemas com precisão: caching, sessões, rate limiting, mensageria leve.

E é exatamente por isso que está em todo lugar.

Mas há uma armadilha real: Redis é fácil de adicionar e difícil de operar bem. Um cache sem política de eviction bem definida vira um ponto cego. Um Pub/Sub sem tratamento de mensagens perdidas vira um bug silencioso. Um distributed lock sem token de ownership vira uma race condition esperando para acontecer. Um keyspace mal modelado vira um problema de memória que aparece às 2h da manhã.

Usado corretamente, Redis transforma caminhos lentos em rápidos e sistemas frágeis em responsivos.

Usado sem cuidado, vira uma abstração cara e com vazamentos.

A diferença está em entender não só o que ele faz bem, mas onde ele mente para você.


Gostou? Deixa um comentário com o padrão que você usa Redis no seu stack. E se você já teve um cache stampede em produção, conta como resolveu, as histórias de guerra são sempre as mais úteis.