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

推荐订阅源

N
News and Events Feed by Topic
Malwarebytes
Malwarebytes
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
Cybersecurity and Infrastructure Security Agency CISA
F
Future of Privacy Forum
C
Cisco Blogs
T
The Exploit Database - CXSecurity.com
A
Arctic Wolf
S
Securelist
K
Kaspersky official blog
S
Schneier on Security
T
ThreatConnect
T
Tenable Blog
Spread Privacy
Spread Privacy
T
True Tiger Recordings
AWS News Blog
AWS News Blog
F
Fox-IT International blog
量子位
T
Threatpost
V
Vulnerabilities – Threatpost
C
CERT Recently Published Vulnerability Notes
Cisco Talos Blog
Cisco Talos Blog
GbyAI
GbyAI
宝玉的分享
宝玉的分享
腾讯CDC
G
Google Developers Blog
aimingoo的专栏
aimingoo的专栏
Cyberwarzone
Cyberwarzone
有赞技术团队
有赞技术团队
S
SegmentFault 最新的问题
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Visual Studio Blog
U
Unit 42
雷峰网
雷峰网
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
O
OpenAI News
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The GitHub Blog
The GitHub Blog
The Register - Security
The Register - Security
MyScale Blog
MyScale Blog
小众软件
小众软件
A
About on SuperTechFans
Last Week in AI
Last Week in AI
Y
Y Combinator Blog
博客园 - 三生石上(FineUI控件)
美团技术团队
Google Online Security Blog
Google Online Security Blog
P
Proofpoint News Feed
MongoDB | Blog
MongoDB | Blog

DEV Community

Why We Deliberately Crush Lithium Batteries (UN38.3 Crush Testing Explained) Command History & Completion The Three-Body Problem: AI Code, Supply Chain Attacks, and the Talent Exodus 로컬 LLM 셋업 가이드 (v27) Building Better .NET Worker Services with Cursor Rules Generate Professional PDF Invoices via REST API — JSON In, PDF Out Agentic AI for Cybersecurity: Autonomous Threat Detection and Response How to Automate Android Without Appium Cron vs systemd daemon: which one for Node.js? Designing XSLT transforms with parameters and multiple inputs I Downloaded Gemma4:e2b On My Macbook in 2 steps Building an Autonomous SRE Agent: From Raw Telemetry to Safe, AI-Driven Remediation The EU AI Act in 2026: Reading the Law After the Omnibus I had zero coding knowledge. Here is "RetroTube", a 2010 YouTube sandbox prototype I built using AI! How to Validate Environment Variables in TypeScript (and Why You Should) I Built a CLI Tool That Writes Better Git Commits Than I Do Transfer Fees, Metadata, and Soulbound Tokens: My First Real Token Experiments on Solana Stop Using Fetch() in React: A Better Way To Call Your Backend Creando un Tetris con JavaScript VI: Complicando el juego. DeepSeek's API Price Cut Changed My Claude Code and ChatGPT Math [Boost] Perl 🐪 Weekly #774 - Perl is too HOT How to Track AI Usage Without Losing Revenue (Complete Guide) 77 Rules Later: What Graduating Our First Stack Actually Looked Like RAG 시스템 실전 구축 (v26) When Premature Scaling Leads to Operator Burnout Multi-Repo Microservice Changes Are a Coordination Problem. I Solved It With AI Agent Teams. The Next Frontier: How Multi-Agent Systems are Redefining Productivity The Kimwolf Bust Just Outed Android Webcams as Botnet Fodder — Here's the Question Every Repurposed-Phone Camera Setup Has to Answer I'm an autonomous AI agent. I shipped 18 fixes to myself in one session. Building a Secure Future with Zero Trust Security Architecture Asynchronous Functions in Dart How I migrated magic-link login from Resend to AWS SES + Lambda five days before launch Edge Computing He creado una empresa ficticia IT/OT para poder encontrar sus vulnerabilidades y reforzar su seguridad en sus activos críticos Why I Built @editora/react I built a tiny UGC script generator because hooks are the hardest part The Phone Is Becoming the New Terminal Why Most AI Music Tools Feel Wrong to Developers Goroutines vs. Promises: Why Go and JavaScript Look at Concurrency Completely Differently How I Use Antigravity 2.0 to Navigate Open-Source Codebases and Make Better Technical Decisions Understanding Basic HTML & CSS Concepts for Beginners Go Error Handling: Annoying or Awesome? Your To-Do List Doesn't Know You — So I Gave Mine Three Brains Shell Basics (Bash, Zsh, Sh) Free MongoDB GUI Tool for Developers, Students, and Teams Designing High-Performance Blockchain Indexers Choosing Models for an Agentic Chat App on Amazon Bedrock How Smart Growth Teams Automate Their Marketing Stack in 2026 (Without Hiring More People) What I Learned About Memory-Augmented AI Agents Seven Docker Tips Every Engineer Should Know (from Docker Captains) Welcome to the Fast-Food Era of Testing: Over-Weight by Tests How to use Claude in vscode? Prompt Engineering for Automated Evaluation: Making LLMs the Judge in AI Builder Solutions Full Stack Projects Are Not Enough Anymore Virtualization & Cloud Basics Orakle: Turning Raw Blockchain Data into Intelligence with Gemma 4 Building an Autoposting Pipeline with Hermes Agent: Why Waterfall Beats Parallel, and the Edge Cases Nobody Talks About OpenShift Virtualization Migration Advisor — Local-First, Powered by Gemma 4 26B MoE WebMCP is coming — so I’m building webmcp.js I Disappeared for 4 Months After Launch - Here's What Brought Me Back Jira Is Turing-Complete (And You've Been Coding in It) NyayAI: Building an AI Legal Assistant for 1.4 Billion People — A Technical Deep Dive E-commerce Order Automation: Stripe + Invoice + Shipping Workflow How to Evaluate AI Agents: LLM-as-Judge Tutorial The Interview Prep Stack I Used as a Senior Software Engineer Targeting Big Tech Gemma4 Challenge OptiLearn - Powered by Google Gemma 4 Aura — The Gemma 4 Powered Agentic Web Copilot & Self-Healing Accessibility Engine I built a tool that catches misleading charts using Gemma 4 running locally Worklog companion with Gemma4 GBase: Building LLM Agents That Actually Learn from Their Mistakes Blossom — a small step toward student mental wellbeing WordPress Performance Monitoring: A Complete Guide Principal Components in TypeScript (Part 4) When three sharp wallets agree: what consensus signals on Polymarket actually mean I Built a Fail-Fast Rust Scheduler with Background OAuth Auto-Refresh (Part 2) Sharing is caring How Putting Faces (Literally) to My AI Garden Images Gave It a Personality Sofi Log #001: Thailand's Tourism Tax & the 180-Day AI Surveillance Wall Sofi Log #006: Decentralized IP-Address Obfuscation Specs Sofi Log #008: Bypassing Legacy Cross-Border Bank Fee Traps Secret Rotation Automation: The Operational Cost of Security Sofi Log #009: Portable Identity & DID Passport Framework Sofi Log #011: Autonomous Smart Treasury Repatriation Specs History of Linux & Unix I asked Claude if my plan was on track for the goal — and got an honest 'No' PHPStan 'expects X, Y given' — the trace it doesn't give you Using Gemma4 2B to Assist Community Health Workers Open-source Playwright wrapper that passes bot.sannysoft.com, pixelscan, and CreepJS in headless mode Policy Storyteller: Turning Nepali Bills into Human Stories with Gemma 4 Avoid Cross Module Dependencies with Dependency Cruiser Invariant-Driven Architecture: 20M transactions on a €80/mo Cloud VM. Stop using external npm packages just to generate a UUID v4 Choosing the Right Gemma 4 Model Matters More Than Choosing the Best One Your LLM Is Not an Agent. Your Framework Is Not Enough. You Need a Harness. From HTTPS to UCP: Shopping Is About to Stop Being Your Problem From Creation to Consumption: How Antigravity 2.0 and Gemini Spark Are Defining the Agentic Era 10 Mistakes I Wish I Knew Before Taking the CKA Exam AI That Actually Does Stuff: Autonomous Agents Explained
Redis: Big Keys Destroem o Desempenho Compartilhado
Lincoln Zoca · 2026-05-25 · via DEV Community

Introdução

Se você usa Redis em um ambiente compartilhado — seja uma instância usada por múltiplos microserviços, múltiplos times ou múltiplos tenants — uma única chave grande pode degradar o desempenho de todos os outros consumidores ao mesmo tempo. Não é exagero: é como o Redis funciona internamente, e ignorar esse comportamento em produção custa caro.

O problema das chaves grandes (big keys) no Redis existe porque o servidor é single-threaded para operações de dados. Quando um comando acessa ou manipula uma chave que contém centenas de megabytes — seja uma lista com 500 mil elementos, um hash com 200 mil campos ou um string binário de 50 MB — o event loop inteiro fica bloqueado processando essa operação. Todos os outros clientes, de outros serviços completamente diferentes, ficam esperando na fila. Em ambientes compartilhados isso é fatal.

Neste artigo vou mostrar o que caracteriza uma chave grande no Redis, por que o impacto é amplificado em ambientes multi-tenant, como detectar big keys em produção, e as estratégias concretas para eliminar ou mitigar o problema — com exemplos em C# usando StackExchange.Redis.

O que é uma “Big Key” no Redis

O Redis não tem um limite fixo que define quando uma chave é “grande”. A comunidade e a documentação oficial usam o termo big key para descrever chaves cujo custo de processamento ou memória é desproporcional em relação ao workload geral da instância.

Na prática, os limites que costumam surgir como referência são:

Tipo de Dado Considerado “grande” a partir de
String / Binary > 5 MB
List > 10.000 elementos ou > 10 MB
Hash > 10.000 campos
Set > 10.000 membros
Sorted Set > 10.000 membros
Stream > 10.000 mensagens pendentes

Esses números não são absolutos — dependem do hardware, da versão do Redis e do padrão de acesso. Mas são referências sólidas para alertas de monitoramento.

ℹ️ Informação: O Redis 7.0+ introduziu melhorias nas estruturas de dados internas (como Listpack substituindo ZipList), o que mudou ligeiramente os limiares de quando estruturas “promovem” para representações maiores. O conceito de big key, no entanto, permanece o mesmo.

Diferença entre chave grande e chave quente

É comum confundir big key com hot key. São problemas diferentes:

  • Big key: chave com muito volume de dados — o problema é o custo de CPU/memória para processar o dado.
  • Hot key: chave acessada com altíssima frequência — o problema é a contenção no slot de hash (relevante em Redis Cluster). Um artigo pode ter ambos os problemas ao mesmo tempo, mas as soluções são distintas. Este artigo foca em big keys.

Como o Redis Processa Comandos (e por que isso importa)

Para entender o impacto de big keys, é preciso entender como o Redis executa comandos. O Redis usa um event loop single-threaded para processar operações de dados. Isso significa:

  • O servidor recebe um comando de um cliente.
  • O event loop executa o comando até o fim antes de processar qualquer outro.
  • Somente depois o próximo comando da fila é processado. Essa arquitetura é uma das razões pelo qual o Redis é tão rápido para operações simples: sem locks, sem context-switching entre threads. O custo é que uma operação lenta bloqueia tudo.
Cliente A: GET user:123          → resposta em 0,1ms ✅
Cliente B: LRANGE big-list 0 -1  → 2.3s processando 500k elementos ⚠️
Cliente C: SET config:timeout 30  → aguardando na fila... ⌛
Cliente D: GET session:abc123     → aguardando na fila... ⌛

Enter fullscreen mode Exit fullscreen mode

⚠️ Atenção: Comandos como LRANGE key 0 -1, SMEMBERS key, HGETALL key e KEYS * são O(N) — o custo cresce linearmente com o tamanho da estrutura. Em uma chave com 500 mil elementos, isso pode levar segundos.

O modelo de I/O do Redis não é bloqueante — mas o processamento é

É importante separar dois conceitos:

  • I/O: o Redis usa multiplexação de I/O (epoll/kqueue), então aceitar conexões e ler dados de rede é não bloqueante.
  • Processamento de comandos: a execução do comando em si é single-threaded e bloqueante para os outros clientes. O Redis 6.0 introduziu threads de I/O para leitura e escrita de rede, mas a execução dos comandos ainda é single-threaded no thread principal. O problema das big keys não foi resolvido por essa mudança.

Impacto em Ambientes Compartilhados

Em um ambiente onde uma única instância Redis serve múltiplos microserviços ou múltiplos tenants de um SaaS, o impacto de uma big key é amplificado porque:

1. A latência afeta todos, não só o culpado

Se o serviço de relatórios lê uma chave com 300 mil registros toda hora, isso introduz picos de latência para o serviço de autenticação, o serviço de notificações e qualquer outro que compartilhe a mesma instância — mesmo que esses serviços estejam funcionando perfeitamente.

2. O consumo de memória é disputado

Redis mantém todos os dados em memória. Uma big key que ocupa 500 MB comprime o espaço disponível para todos os outros tenants. Quando o Redis atinge maxmemory, inicia a política de eviction (expulsão de chaves), que pode descartar dados críticos de outros serviços.

3. Operações de snapshot e replicação ficam mais lentas

Quando o Redis gera um RDB snapshot (persistência) ou replica dados para um replica, big keys aumentam o tempo do fork() e o tamanho do arquivo. Em instâncias cloud (AWS ElastiCache, Azure Cache for Redis), isso pode causar falhas de replicação ou penalidades de performance durante a sincronização.

4. Dificuldade de debug e rastreabilidade

Em um ambiente compartilhado, identificar quem criou a big key e qual serviço é o responsável exige instrumentação cuidadosa. Sem namespacing adequado nas chaves, rastrear a origem do problema pode levar horas.

Como Detectar Big Keys em Produção

O Redis oferece ferramentas nativas para detectar big keys. Veja as principais abordagens:

redis-cli –bigkeys

O comando mais simples para uma varredura inicial:

# Varredura completa — use com cuidado em produção (pode ser lenta)
redis-cli --bigkeys

# Com host e porta específicos
redis-cli -h redis.example.com -p 6379 --bigkeys

Enter fullscreen mode Exit fullscreen mode

A saída mostra as maiores chaves por tipo de dado. O problema é que esse comando usa SCAN internamente e pode demorar em instâncias grandes. Em produção, prefira executá-lo em horários de menor carga.

MEMORY USAGE — verificação pontual

Para checar o tamanho de uma chave específica:

# Retorna o tamanho em bytes, incluindo overhead de metadados
MEMORY USAGE minha-chave

# Com nested sampling (para estruturas complexas)
MEMORY USAGE minha-chave SAMPLES 10

Enter fullscreen mode Exit fullscreen mode

SCAN + MEMORY USAGE — varredura automatizada em C

Para monitoramento programático com StackExchange.Redis:

public class RedisBigKeyScanner
{
    private const long LimiarBigKeyBytes = 10 * 1024 * 1024; // 10 MB
    private readonly IDatabase _db;
    private readonly IServer _server;
    private readonly ILogger<RedisBigKeyScanner> _logger;

    public RedisBigKeyScanner(IConnectionMultiplexer redis, ILogger<RedisBigKeyScanner> logger)
    {
        _db = redis.GetDatabase();
        _server = redis.GetServer(redis.GetEndPoints().First());
        _logger = logger;
    }

    public async Task VarrerBigKeysAsync(string padrao = "*", int tamanhoPagina = 100,
        CancellationToken ct = default)
    {
        // SCAN é não bloqueante — itera em lotes (tamanhoPagina por vez)
        await foreach (var key in _server.KeysAsync(pattern: padrao, pageSize: tamanhoPagina)
                                         .WithCancellation(ct))
        {
            var sizeBytes = await _db.ExecuteAsync("MEMORY", "USAGE", key, "SAMPLES", "5");
            if (sizeBytes.IsNull) continue;

            var size = (long)sizeBytes;
            if (size >= LimiarBigKeyBytes)
                _logger.LogWarning("Big key: {Key} | {SizeMB:F2} MB", key, size / (1024.0 * 1024.0));
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

📂 Código Fonte: O exemplo completo está disponível no repositório de exemplos do blog:
BlogSamples/Cache/Redis/

📝 Exemplo: Em um ambiente com 2 milhões de chaves, uma varredura com pageSize = 500 e pattern = "relatorio:*" limita o escopo apenas ao namespace problemático, reduzindo o tempo de varredura de horas para minutos.

Redis Latency Monitoring

Para detectar o impacto em tempo real, ative o monitoramento de latência:

# Ativar no redis.conf ou via CONFIG SET
CONFIG SET latency-monitor-threshold 100

# Ver eventos de latência
LATENCY LATEST
LATENCY HISTORY command

Enter fullscreen mode Exit fullscreen mode

Estratégias para Evitar Big Keys

Detectar o problema é o primeiro passo. Resolver é mais interessante. Existem quatro abordagens principais:

1. Fragmentação de dados (Sharding)

Em vez de uma única chave grande, distribuir os dados em múltiplas chaves menores usando um campo de particionamento:

public class RedisHashSharded
{
    private readonly IDatabase _db;
    private const int QuantidadeShards = 16; // potência de 2 para distribuição uniforme

    public RedisHashSharded(IConnectionMultiplexer redis) => _db = redis.GetDatabase();

    private string ObterChaveShard(string chaveBase, string campo)
    {
        var indiceShard = Math.Abs(campo.GetHashCode()) % QuantidadeShards;
        return $"{chaveBase}:shard:{indiceShard}";
    }

    public async Task GravarAsync(string chaveBase, string campo, string valor)
    {
        var chaveShard = ObterChaveShard(chaveBase, campo);
        await _db.HashSetAsync(chaveShard, campo, valor);
    }

    public async Task<string?> LerAsync(string chaveBase, string campo)
    {
        var chaveShard = ObterChaveShard(chaveBase, campo);
        var resultado = await _db.HashGetAsync(chaveShard, campo);
        return resultado.HasValue ? resultado.ToString() : null;
    }

    // LerTodosAsync paralleliza a leitura dos QuantidadeShards shards — código completo no repositório
}

Enter fullscreen mode Exit fullscreen mode

2. Serialização eficiente com compressão

Um dos maiores culpados por strings grandes é a serialização ingênua de objetos complexos. Trocar JSON por um formato binário + compressão reduz dramaticamente o tamanho:

public static class RedisSerializerExtensions
{
    public static async Task GravarComprimidoAsync<T>(
        this IDatabase db, string chave, T valor, TimeSpan? expiracao = null)
    {
        using var stream = new MemoryStream();
        await using (var gzip = new GZipStream(stream, CompressionLevel.Optimal))
            await JsonSerializer.SerializeAsync(gzip, valor);

        await db.StringSetAsync(chave, stream.ToArray(), expiracao);
    }

    public static async Task<T?> LerComprimidoAsync<T>(this IDatabase db, string chave)
    {
        var valor = await db.StringGetAsync(chave);
        if (!valor.HasValue) return default;

        using var stream = new MemoryStream((byte[])valor!);
        await using var gzip = new GZipStream(stream, CompressionMode.Decompress);
        return await JsonSerializer.DeserializeAsync<T>(gzip);
    }
}

Enter fullscreen mode Exit fullscreen mode

💡 Dica: Compressão GZip típica reduz payloads JSON em 60–80%. Um objeto de 5 MB pode cair para 800 KB — o que muda a classificação de “big key” para um tamanho gerenciável. Mas atenção: compressão tem custo de CPU, avalie o trade-off para dados acessados com alta frequência.

3. TTL obrigatório para chaves de cache

Chaves sem TTL em ambientes compartilhados tendem a acumular dados indefinidamente. Implemente uma política de TTL obrigatória:

public class RedisCacheComPoliticaTtl
{
    private readonly IDatabase _db;
    private static readonly TimeSpan TtlPadrao = TimeSpan.FromHours(1);
    private static readonly TimeSpan TtlMaximo = TimeSpan.FromHours(24);

    public RedisCacheComPoliticaTtl(IConnectionMultiplexer redis)
        => _db = redis.GetDatabase();

    public async Task GravarAsync(string chave, string valor, TimeSpan? ttl = null)
    {
        // Nunca permite TTL nulo — usa o padrão se não especificado
        var ttlEfetivo = ttl.HasValue
            ? TimeSpan.FromTicks(Math.Min(ttl.Value.Ticks, TtlMaximo.Ticks))
            : TtlPadrao;

        await _db.StringSetAsync(chave, valor, ttlEfetivo);
    }
}

Enter fullscreen mode Exit fullscreen mode

4. Namespacing e segregação por tenant

Em ambientes multi-tenant, use prefixos obrigatórios para identificar a origem das chaves. Isso facilita o diagnóstico e permite políticas de limpeza seletivas:

// Padrão de chave: {ambiente}:{tenant}:{dominio}:{entidade}:{id}
// Exemplo: prod:acme-corp:relatorio:mensal:2026-05

public static class RedisKeyBuilder
{
    public static string Construir(
        string tenant,
        string dominio,
        string entidade,
        string id,
        string ambiente = "prod")
    {
        return $"{ambiente}:{Sanitizar(tenant)}:{Sanitizar(dominio)}:{Sanitizar(entidade)}:{id}";
    }

    private static string Sanitizar(string segmento) =>
        segmento.ToLowerInvariant().Replace(" ", "-").Replace(":", "");
}

Enter fullscreen mode Exit fullscreen mode

Exemplo Prático

Vou mostrar um cenário real: um serviço de relatórios que carregava todos os registros de transações do mês em uma única chave Redis para “cache” — e estava derrubando o desempenho dos outros serviços no mesmo cluster.

O problema original

// ❌ CÓDIGO PROBLEMÁTICO — NÃO FAÇA ISSO
public async Task<List<Transacao>> ObterTransacoesMensaisAsync(
    int clienteId,
    int mes,
    int ano)
{
    var cacheKey = $"transacoes:{clienteId}:{mes}/{ano}";

    var cached = await _cache.StringGetAsync(cacheKey);
    if (cached.HasValue)
    {
        // Desserializa 300.000 transações em memória de uma vez
        return JsonSerializer.Deserialize<List<Transacao>>(cached!)!;
    }

    // Busca 300.000 registros do banco
    var transacoes = await _db.Transacoes
        .Where(t => t.ClienteId == clienteId
                 && t.Data.Month == mes
                 && t.Data.Year == ano)
        .ToListAsync();

    // Serializa 300.000 objetos para um JSON de ~80 MB e salva no Redis
    // Sem TTL → fica para sempre
    await _cache.StringSetAsync(
        cacheKey,
        JsonSerializer.Serialize(transacoes));

    return transacoes;
}

Enter fullscreen mode Exit fullscreen mode

Esse código cria uma chave que pode ter 80 MB no Redis, sem TTL, e é lida inteira a cada acesso. Cada leitura bloqueia o event loop por centenas de milissegundos.

A solução refatorada

// ✅ SOLUÇÃO CORRETA — Cache paginado com TTL obrigatório

public class TransacaoCache
{
    private readonly IDatabase _redis;
    private const int TamanhoPagina = 100;
    private const string PrefixoChave = "tx";

    public TransacaoCache(IConnectionMultiplexer redis) => _redis = redis.GetDatabase();

    public async Task GravarPaginaAsync(int clienteId, int mes, int ano, int pagina,
        IEnumerable<Transacao> transacoes)
    {
        var chave = ConstruirChavePagina(clienteId, mes, ano, pagina);
        var json = JsonSerializer.Serialize(transacoes.Take(TamanhoPagina));
        // TTL de 30 minutos — dado de relatório, não precisa ser eterno
        await _redis.StringSetAsync(chave, json, TimeSpan.FromMinutes(30));
    }

    public async Task<List<Transacao>?> ObterPaginaAsync(int clienteId, int mes, int ano, int pagina)
    {
        var chave = ConstruirChavePagina(clienteId, mes, ano, pagina);
        var cache = await _redis.StringGetAsync(chave);
        return cache.HasValue ? JsonSerializer.Deserialize<List<Transacao>>((string)cache!) : null;
    }

    // Cada chave contém apenas TamanhoPagina transações (~30 KB)
    private static string ConstruirChavePagina(int clienteId, int mes, int ano, int pagina) =>
        $"{PrefixoChave}:{clienteId}:{mes}-{ano}:p{pagina}";
}

Enter fullscreen mode Exit fullscreen mode

Com essa abordagem, cada chave passa de 80 MB para ~30 KB. O event loop é liberado em microssegundos, e os outros serviços compartilhados deixam de sofrer latência por conta do serviço de relatórios.

Dicas e Boas Práticas

  • Defina limiares de alerta no CI/CD: antes de um deploy, verifique se novos padrões de chave respeitam os limites de tamanho. Uma pipeline de testes de carga com redis-cli --bigkeys pode prevenir o problema antes de chegar à produção.

  • Use TTL em absolutamente todas as chaves: em ambientes compartilhados, uma chave sem TTL é uma chave que vai crescer indefinidamente. Configure maxmemory-policy allkeys-lru no Redis para que, mesmo que alguma chave escape sem TTL, o Redis possa descartá-la quando necessário.

  • Nunca use KEYS * em produção: o comando KEYS bloqueia o Redis enquanto varre todo o keyspace. Use sempre SCAN com MATCH e COUNT para varreduras iterativas e não bloqueantes. O StackExchange.Redis expõe esse comportamento através de IServer.KeysAsync().

  • Monitore redis-cli --latency-history: latências acima de 50ms recorrentes são sinal de big keys ou hot keys. Configure alertas de latência no Redis e correlacione com os padrões de chave que estão sendo acessados no momento.

  • Separe workloads em instâncias distintas quando possível: se um serviço de relatórios inevitavelmente precisa de chaves grandes, isole-o em uma instância Redis dedicada. O custo de uma instância extra é muito menor que o impacto de latência nos serviços críticos de um ambiente compartilhado.

  • Prefira estruturas nativas do Redis ao invés de strings JSON grandes: um HASH com 1.000 campos é mais eficiente que um STRING com um JSON de 1.000 propriedades. O Redis pode acessar campos individuais do hash sem deserializar a estrutura toda, economizando CPU e memória.

  • Revise o design ao usar Redis como banco de dados: Redis é excelente como cache de curta duração, pub/sub e contadores. Quando começa a receber coleções completas de entidades para “persistência temporária”, é um sinal de que o design precisa ser revisado — provavelmente um banco relacional ou documento é a ferramenta certa para aquele dado.

Resumo Objetivo

  • Big key no Redis — chave cujo volume de dados é desproporcional ao workload; limites práticos são 5 MB para strings, 10.000 elementos para listas/hashes/sets. A partir desses valores, operações O(N) bloqueiam o event loop por centenas de milissegundos.
  • Event loop single-threaded — o Redis processa um comando por vez no thread principal (mesmo no Redis 6.0+ com I/O threads). Uma big key bloqueia todos os outros clientes da instância enquanto é processada.
  • Ambientes compartilhados — em instâncias Redis usadas por múltiplos microserviços ou tenants, uma big key criada por um único serviço introduz latência para todos os outros, independentemente do seu comportamento.
  • Comandos O(N) perigososLRANGE key 0 -1, SMEMBERS key, HGETALL key, KEYS * e SUNIONSTORE são os principais vetores de bloqueio quando aplicados a chaves grandes. Prefira variantes paginadas como HSCAN, SSCAN e ZSCAN.
  • Detecção em produçãoredis-cli --bigkeys realiza varredura por tipo; MEMORY USAGE key SAMPLES N retorna o tamanho exato em bytes incluindo overhead; LATENCY LATEST identifica comandos com latência anômala.
  • Fragmentação como solução — dividir uma big key em múltiplas chaves menores usando sharding por campo elimina o bloqueio e permite paralelismo na leitura. Um hash com 100.000 campos pode ser dividido em 16 shards de ~6.250 campos cada.
  • TTL obrigatório — toda chave em ambiente compartilhado deve ter expiração. A ausência de TTL é a principal causa de acúmulo de big keys em produção. Configure maxmemory-policy allkeys-lru como rede de segurança.

Leia Também

  • Gargalo em Banco de Dados: Mensageria e Paginação — estratégias para resolver gargalos de escrita e leitura com mensageria e paginação eficiente no EF Core 8+.
  • .NET Worker e Background Service: Processamento de Alto Volume — como processar grandes volumes de dados em background sem sobrecarregar os serviços principais.
  • Programação Assíncrona em C#: async/await e Threads — fundamentos de programação assíncrona em C# para evitar bloqueios em operações de I/O, incluindo acesso a caches.
  • Paginação em APIs REST com C# e EF Core: Guia Prático — padrões de paginação para evitar retornar grandes volumes de dados de uma vez.

Referências

  • Redis Big Keys — Documentação Oficial — especificações de tipos de dados e comportamento interno do Redis por versão.
  • Redis Memory Optimization — Best Practices — guia oficial de otimização de memória no Redis, cobrindo encoding interno de estruturas de dados.
  • StackExchange.Redis — Documentação — documentação da biblioteca cliente .NET para Redis, incluindo uso de SCAN e MEMORY USAGE.
  • Redis Latency Problems — Troubleshooting Guide — guia oficial de diagnóstico de latência no Redis, com foco em comandos bloqueantes.
  • Anti-Patterns in Redis Usage — Redis Labs Blog — anti-padrões comuns de uso do Redis identificados pela equipe da Redis Ltd., incluindo big keys e hot keys.
  • Redis SCAN command — documentação do comando SCAN com exemplos de varredura não bloqueante para uso em produção. 📬

👉 Artigo completo com todos os exemplos de código: Redis: Big Keys Destroem o Desempenho Compartilhado