+− THE DAILY DIFFdev & AI news
SHIP IT

Shopify moveu reservas de estoque para o MySQL

A Shopify moveu seu sistema de reserva de estoque do Redis para o banco de dados MySQL que já mantinha o livro-razão de estoque.

A Shopify moveu seu sistema de reserva de estoque do Redis para o banco de dados MySQL que já mantinha o livro-razão de estoque. Um pool limitado de linhas de unidades individualmente bloqueáveis permite que checkouts concorrentes usem SKIP LOCKED para selecionar diferentes unidades elegíveis. O design também depende dos limites da transação, layout da chave primária, regras de reabastecimento e observação do tempo de espera da conexão durante o checkout.

Leia a edição escrita (Inglês) ↗

O que este vídeo aborda

  • O antigo modelo de contador de quantidade do Redis lidava com a concorrência, mas a limpeza da reserva e a atualização do livro-razão do MySQL não podiam compartilhar uma única transação atômica local.
  • A substituição usa uma linha por unidade disponível em um pool limitado a 1.000 por combinação item/local. Um pool vazio pode acionar o reabastecimento em linha com solicitações concorrentes esperando atrás de um bloqueio de reabastecimento.
  • A chave primária composta é (shop_id, inventory_item_id, inventory_group_id, id). A Shopify reduziu a sobrecarga de bloqueio em seu protótipo e usou READ COMMITTED para evitar bloqueios de lacuna que impediam o reabastecimento.
  • A reserva exclui linhas do pool antes de inserir registros de reserva. A confirmação libera os bloqueios do banco de dados enquanto a retenção armazenada persiste durante o pagamento; o pagamento bem-sucedido reivindica o livro-razão e remove a reserva em uma transação atômica posterior.
  • O MySQL documenta SKIP LOCKED como uma visão inconsistente que omite linhas bloqueadas. Não estabelece uma contagem de estoque completa ou turnos justos, aplica-se apenas a bloqueios de nível de linha e é inseguro para replicação baseada em declaração.
  • O exemplo de origem inclui expires_at, mas a Shopify não documenta o algoritmo de limpeza de expiração do MySQL. A ramificação de liberação/expiração do vídeo é um requisito de ciclo de vida explicativo.
  • O tempo de espera da conexão em outro código de checkout foi o gargalo final de throughput. A Shopify escreveu em sombra ambos os sistemas, comparou os resultados e mudou gradualmente com um fallback de "kill-switch" do Redis.

Transcrição traduzida

Traduzido da narração original em inglês. Áudio e legendas disponíveis são controlados pelo YouTube.

Por que mover as reservas para o MySQL?

0:00 Um checkout precisa do Redis para se manter rápido. A Shopify moveu as reservas de estoque para o MySQL usando uma linha por unidade em um pool limitado. Por que escolher o MySQL? Como pular bloqueios com segurança? Este é o The Daily Diff, por trás das cenas. E por que as consultas rápidas ainda atingiram um teto? Shopify é uma plataforma de comércio para vender online e presencialmente. Redis é um armazenamento de dados em memória.

0:21 MySQL é um banco de dados relacional, e a Shopify já mantinha seu livro-razão de estoque lá. Uma reserva retém brevemente o estoque enquanto o comprador paga.

Por que duas lojas eram arriscadas?

0:29 O modelo Redis deles decrementava um contador de itens. Reivindicar um pedido pago significava atualizar o livro-razão do MySQL e limpar o Redis. Essas gravações separadas podiam deixar o estoque vendido duas vezes ou indisponível quando deveria estar disponível para venda. O modelo antigo também não tinha consciência de localização. A substituição deve escolher o estoque de algum lugar que possa atender ao pedido. Um armazém no continente errado é uma excelente entrada de banco de dados e uma péssima promessa de entrega. Tentativas anteriores no MySQL usavam uma linha de quantidade, então checkouts concorrentes faziam fila no

O que se torna a unidade bloqueável?

0:56 mesmo bloqueio. Pense em uma corda de veludo em torno de uma célula de planilha. Adicionar mais trabalhadores apenas alonga a fila. A Shopify mudou o objeto bloqueável. Cada unidade disponível recebe sua própria linha. Uma leitura de bloqueio pula unidades que outra transação mantém e seleciona outras unidades elegíveis. Diferentes trabalhadores podem adquirir diferentes linhas, enquanto o contador quente fica fora do caminho deles.

O que acontece quando o pool se esvazia?

1:15 Esse pool é limitado a mil linhas por item e localização. O reabastecimento é feito a partir do livro-razão. Se esvaziar, o caminho de reserva reabastece em linha, com solicitações concorrentes esperando atrás de um bloqueio de reabastecimento. Pool vazio não significa armazém vazio. A reserva exclui as linhas do pool selecionadas e, em seguida, insere os registros de reserva em uma transação. O commit libera os bloqueios do banco de dados.

1:35 O rollback desfaz as alterações. A reserva sobrevive ao processamento do pagamento como estado armazenado. O pagamento bem-sucedido reivindica o livro-razão e remove a reserva atomicamente. Os bloqueios do banco de dados nunca precisam "cuidar" do formulário de pagamento. Sua chave primária composta começa com loja, item, grupo e depois identidade da unidade. Corresponder à pesquisa reduziu o bloqueio de índice em seu protótipo. Eles também usam "read committed" para evitar os bloqueios de lacuna que bloqueavam o reabastecimento

1:57 do pool, e uma ordem de tabela consistente para evitar esperas circulares. O exemplo publicado registra um tempo de expiração. Pagamentos abandonados precisam que o estoque seja liberado eventualmente, ou o carrinho de compras se torna um senhorio. A postagem da Shopify deixa esse algoritmo de limpeza não especificado, então este diagrama mostra o requisito do ciclo de vida. Aqui está a pegadinha.

O que o SKIP LOCKED deixa de fora?

2:14 Skip locked exclui linhas bloqueadas, então o manual chama seu resultado de "visão inconsistente". Não fornece uma contagem de estoque completa nem turnos justos. Mantenha a decisão de disponibilidade e as regras de reabastecimento em torno dela.

Onde estava o verdadeiro teto?

2:26 E aquele teto? Outro código de checkout estava mantendo as conexões do banco de dados por muito tempo. A Shopify marcou os chamadores e mediu o tempo de retenção da conexão, depois limpou o caminho de checkout e revisitou a concorrência de threads. Consultas rápidas ainda podem fazer fila fora da consulta.

Por que eu enviaria este projeto?

2:38 Eles escreveram em sombra ambos os sistemas com o Redis como autoritário, compararam os resultados e, em seguida, mudaram gradualmente com um "kill switch" (chave de interrupção). Meu veredito é SHIP IT. Eu enviaria o limite de transação compartilhado e essa implementação com rollback, com todo o caminho de checkout instrumentado. Tem alguma pergunta sobre isso? Coloque nos comentários. E esse é o "diff" de hoje.

2:54 Sou Niko da Axrisi. Mescle com responsabilidade.

Fontes

  1. We replaced Redis with MySQL for inventory reservations—and it scaledShopify Engineering — Emilie Noel
  2. Simplified reservation SQL embedded in Shopify's articleShopify Engineering / CourtneySymons on GitHub Gist
  3. MySQL 8.0 — Locking ReadsOracle / MySQL Reference Manual
  4. MySQL 8.0 — Transaction Isolation LevelsOracle / MySQL Reference Manual
  5. What Is Shopify and How Does It Work?Shopify
  6. Redis quick startsRedis documentation
  7. What is MySQL?Oracle / MySQL Reference Manual

Vídeos relacionados