O que é a troca do Redis pelo MySQL no Shopify
O Shopify e uma das maiores plataformas de e-commerce do mundo, processando bilhoes de dólares em vendas - especialmente em eventos como Black Friday e Cyber Monday. Para suportar esse volume, a engenharia deles precisa ser impecável em cada detalhe.
Em 2024, a equipe de engenharia do Shopify tomou uma decisão que surpreendeu muita gente: substituir o Redis pelo MySQL para gerenciar as reservas de estoque. O Redis e famoso por ser um banco em memoria, extremamente rápido para operações de leitura e escrita. Usar MySQL no lugar parecia ir contra o senso comum.
O resultado? A migração funcionou muito bem. A plataforma escalou melhor, ficou mais simples de operar e o time de engenharia ganhou mais confiança no sistema. A historia virou caso de estudo sobre como a escolha certa de tecnologia nem sempre e a mais óbvia.
Como funciona o sistema de reservas de estoque
Quando você adiciona um produto ao carrinho numa loja Shopify, o sistema precisa reservar temporariamente aquele item. Isso evita que dois clientes comprem o mesmo produto ao mesmo tempo. E uma operação crítica: falhar aqui significa vender o que não existe no estoque.
Com Redis, as reservas eram guardadas como entradas temporárias em memoria com tempo de expiração (TTL). E uma abordagem clássica para esse tipo de problema - rápida e sem overhead de disco. O problema e que o Redis exigia sincronização cuidadosa com o banco principal (MySQL), e qualquer falha de rede ou reinicialização do Redis podia causar inconsistências.
Com MySQL, a equipe modelou as reservas como linhas em uma tabela com transações ACID. A consistência passa a ser garantida pelo próprio banco. O MySQL já estava rodando em produção, já tinha backups, monitoramento, equipe experiente e anos de ajuste de performance. Não havia necessidade de operar um segundo sistema separado.
O grande insight do Shopify foi perceber que o gargalo não era a velocidade bruta do banco - era a complexidade operacional de manter dois sistemas de persistência sincronizados em tempo real.
Principais vantagens da abordagem com MySQL
A migração trouxe ganhos em varias frentes que a equipe do Shopify detalhou publicamente:
- Consistência transacional: transações ACID garantem que uma reserva nunca fica em estado intermediário. Ou ela existe, ou não existe.
- Menos surface área operacional: uma tecnologia a menos para monitorar, fazer backup, ajustar em produção e treinar novos engenheiros.
- Queries complexas de graça: com SQL você faz JOINs, agregações e análises diretamente. No Redis, isso exigiria código adicional na aplicação.
- Durabilidade nativa: dados em MySQL sobrevivem a reinicializações e falhas de servidor sem nenhuma configuração extra.
- Melhor debugabilidade: inspecionar o estado das reservas via SELECT e muito mais simples do que navegar em estruturas do Redis.
O ponto central e que o Redis foi criado para casos onde você precisa de velocidade extrema em operações simples. Mas as reservas de estoque do Shopify não eram tao simples assim - elas envolviam lógica de negócio complexa que se encaixava melhor no modelo relacional.
Isso não significa que Redis e ruim. Significa que Redis foi escolhido originalmente por um motivo que, com o tempo, ficou menos relevante do que a consistência e a simplicidade operacional.
Como começar: modelando reservas de estoque com MySQL
Se você quiser aplicar um padrão similar em seus projetos, a estrutura básica de uma tabela de reservas em MySQL ficaria assim:
CREATE TABLE inventory_reservations (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
product_variant_id BIGINT UNSIGNED NOT NULL,
quantity INT NOT NULL,
cart_token VARCHAR(255) NOT NULL,
reserved_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
expires_at DATETIME NOT NULL,
ÍNDEX idx_variant (product_variant_id),
ÍNDEX idx_cart (cart_token),
ÍNDEX idx_expires (expires_at)
);Para reservar um item, você usa uma transação que primeiro verifica o estoque disponível e depois insere a reserva atomicamente. Isso garante que não ha condição de corrida entre dois pedidos simultâneos.
START TRANSACTION;
-- Verifica estoque disponível
SELECT available_quantity INTO @available
FROM inventory
WHERE product_variant_id = 123
FOR UPDATE;
-- Só reserva se tiver quantidade suficiente
IF @available >= 1 THEN
INSERT INTO inventory_reservations
(product_variant_id, quantity, cart_token, expires_at)
VALUES (123, 1, 'abc-cart-token', NOW() + INTERVAL 30 MINUTE);
END IF;
COMMIT;Um job periódico limpa reservas expiradas. Isso e mais previsível e auditavel do que depender de TTLs do Redis.
Exemplo prático: simulando o fluxo de checkout
Imagine uma loja com 5 unidades de um ténis limitado. Dois usuários tentam comprar ao mesmo tempo no Black Friday. Com MySQL e transações, o fluxo fica assim:
Usuário A inicia transação, le 5 unidades disponível, insere reserva de 1 unidade, faz commit. Agora ha 4 disponíveis e 1 reservada. Usuário B inicia transação, le o estado atual (4 disponíveis, pois o FOR UPDATE bloqueia concorrentemente), insere reserva de 1 unidade, faz commit. Ambas as reservas existem sem conflito.
-- Verificar estado das reservas ativas
SELECT
p.name AS produto,
COUNT(r.id) AS reservas_ativas,
SUM(r.quantity) AS unidades_reservadas
FROM inventory_reservations r
JOIN products p ON r.product_variant_id = p.id
WHERE r.expires_at > NOW()
GROUP BY p.id, p.name;Essa query de auditoria e trivial em SQL. No Redis, você precisaria de lógica extra na aplicação para montar o mesmo relatório.
Adicione um índex em expires_at e rode um job de limpeza a cada 5 minutos com DELETE WHERE expires_at < NOW(). Isso mantem a tabela enxuta sem impactar o desempenho das queries de reserva.
Comparação com alternativas
Existem varias abordagens para o problema de reservas de estoque em sistemas de alta demanda. Cada uma tem seu espaço:
- Redis puro: excelente para casos onde velocidade e tudo e a perda de dados em falha e tolerável. Caches de sessão, filas de tarefas, rate limiting. Para reservas financeiras com consistência obrigatória, arriscado sem configuração cuidadosa.
- MySQL (abordagem Shopify): melhor quando você já tem MySQL em produção, precisa de consistência ACID, e a latência extra de microsegundos não e crítica. Simplifica operações dramaticamente.
- PostgreSQL: alternativa robusta ao MySQL com recursos avançados como SELECT FOR UPDATE SKIP LOCKED, ótimo para filas e reservas. Igualmente valido para esse padrão.
- Combinação Redis + MySQL: a abordagem original do Shopify. Mais complexa de operar, mas pode fazer sentido se você precisar de caching agressivo em cima da consistência.
A licao e que não existe bala de prata. O Shopify escolheu MySQL porque ele resolvia todos os requisitos deles sem adicionar complexidade desnecessária.
Pontos positivos e limitações
A abordagem com MySQL tem forcas reais em produção: operabilidade simples, consistência garantida, equipes já familiarizadas, ferramentas de monitoramento maduras. O Shopify reportou menos incidentes relacionados a sincronização após a migração.
As limitações também existem. Se você tiver cargas de escrita extremamente altas (centenas de milhares de reservas por segundo), o MySQL pode atingir seus limites antes do Redis. Para casos assim, você precisaria de sharding, proxies de banco ou uma abordagem diferente.
Não migre um sistema Redis funcionando só porque o Shopify migrou. Avalie seus requisitos específicos: volume de escrita, tolerância a falhas, consistência necessária. A decisão do Shopify faz sentido para o contexto deles.
Outro ponto: o MySQL precisa de índices bem configurados para performar bem com muitas reservas simultâneas. Um índex esquecido em expires_at pode causar table scans caros durante a limpeza das reservas expiradas.
Casos de uso reais
Esse padrão se aplica muito bem em algumas situações específicas do mundo real:
- E-commerce com estoque limitado: produtos de edição limitada, ingressos, vagas em cursos. Qualquer situação onde múltiplos usuários podem tentar comprar o mesmo item simultaneamente e a consistência e crítica.
- Sistemas de agendamento: reserva de horários em clínicas, salas de reunião, slots de entrega. O modelo de reserva temporária com expiração e idêntico ao do e-commerce.
- Plataformas de licitação: reserva de créditos ou garantias durante um lance. Você precisa garantir atomicidade e auditabilidade.
- SaaS com uso metered: reservar cota de API durante uma requisição para evitar ultrapassar limites. MySQL com transações oferece consistência necessária.
Dicas e boas práticas
Defina um tempo de expiração conservador para as reservas (15 a 30 minutos para e-commerce). Muito curto frustra o usuário no checkout; muito longo trava estoque desnecessariamente.
Use SELECT ... FOR UPDATE ao verificar estoque antes de inserir a reserva. Isso garante exclusão mutua sem precisar de locks manuais ou lógica de retry na aplicação.
Monitore o tempo médio de transação. Transações longas seguram locks e podem causar timeouts em cascata durante picos de tráfego. Mantenha a lógica dentro da transação mínima e necessária.
Considere uma tabela separada de reservas em vez de coluna de status no produto. Isso facilita queries de auditoria, histórico e debug sem impactar a tabela principal de inventario.
Vale a pena?
A decisão do Shopify de trocar Redis por MySQL e um estudo de caso poderoso sobre complexidade acidental. Redis foi adicionado porque parecia a escolha óbvia para operações rápidas. Com o tempo, a complexidade de manter dois sistemas sincronizados custava mais do que o beneficio de velocidade.
Para a maioria dos projetos brasileiros de e-commerce, SaaS ou sistemas com reservas, o MySQL (ou PostgreSQL) com transações ACID e a escolha mais solida. Você ganha consistência, operabilidade, ferramentas maduras e uma equipe que já sabe operar o sistema.
Use Redis quando você tem um problema específico que ele resolve melhor: cache de leitura agressivo, filas de tarefas, pub/sub, rate limiting. Não use só porque e mais rápido em benchmarks sintéticos - operações reais em produção tem outros gargalos.
Comentários
Deixar um comentárioVocê precisa ter uma conta no CuritibaBlog para comentar.