+− THE DAILY DIFFdev & AI news
SHIP IT

Shopify movió las reservas de inventario a MySQL

Shopify movió su sistema de reserva de inventario de Redis a la base de datos MySQL que ya contenía el libro mayor de inventario.

Shopify movió su sistema de reserva de inventario de Redis a la base de datos MySQL que ya contenía el libro mayor de inventario. Un grupo limitado de filas de unidades individualmente bloqueables permite que los pagos concurrentes usen SKIP LOCKED para seleccionar diferentes unidades elegibles. El diseño también depende de los límites de las transacciones, el diseño de la clave principal, las reglas de reabastecimiento y la observación del tiempo de retención de la conexión durante el proceso de pago.

Leer la edición escrita (inglés) ↗

Lo que cubre este video

  • El antiguo modelo de contador de cantidad de Redis manejaba la concurrencia, pero la limpieza de reservas y la actualización del libro mayor de MySQL no podían compartir una única transacción atómica local.
  • El reemplazo usa una fila por unidad disponible en un grupo limitado a 1,000 por combinación de artículo/ubicación. Un grupo vacío puede activar el reabastecimiento en línea con solicitudes concurrentes esperando detrás de un bloqueo de reabastecimiento.
  • La clave principal compuesta es (shop_id, inventory_item_id, inventory_group_id, id). Shopify redujo la sobrecarga de bloqueos en su prototipo y usó READ COMMITTED para evitar bloqueos de huecos que bloqueaban el reabastecimiento.
  • La reserva elimina filas del grupo antes de insertar registros de reserva. La confirmación libera los bloqueos de la base de datos mientras la retención almacenada persiste durante el pago; el pago exitoso reclama el libro mayor y elimina la reserva en una transacción atómica posterior.
  • MySQL documenta SKIP LOCKED como una vista inconsistente que omite filas bloqueadas. No establece un recuento completo de existencias ni un turno justo, se aplica solo a bloqueos a nivel de fila y no es seguro para la replicación basada en sentencias.
  • El ejemplo de la fuente incluye expires_at, pero Shopify no documenta el algoritmo de limpieza de expiración de MySQL. La rama de liberación/expiración del video es un requisito de ciclo de vida explicativo.
  • El tiempo de retención de la conexión en otro código de pago fue el último cuello de botella de rendimiento. Shopify escribió en paralelo ambos sistemas, comparó los resultados y cambió gradualmente con un respaldo de "kill switch" de Redis.

Transcripción traducida

Traducido de la narración original en inglés. El audio y los subtítulos disponibles son controlados por YouTube.

¿Por qué mover las reservas a MySQL?

0:00 Un pago necesita que Redis se mantenga rápido. Shopify movió las reservas de inventario a MySQL usando una fila por unidad en un grupo limitado. ¿Por qué elegir MySQL? ¿Cómo se omiten los bloqueos de forma segura? Esto es The Daily Diff, detrás de escena. ¿Y por qué las consultas rápidas seguían llegando a un límite? Shopify es una plataforma de comercio para vender en línea y en persona. Redis es un almacén de datos en memoria.

0:21 MySQL es una base de datos relacional, y Shopify ya guardaba su libro mayor de inventario allí. Una reserva retiene brevemente el stock mientras un comprador paga.

¿Por qué eran riesgosas dos tiendas?

0:29 Su modelo de Redis decrementaba un contador de artículos. Reclamar un pedido pagado significaba actualizar el libro mayor de MySQL y limpiar Redis. Esas escrituras separadas podían dejar el stock vendido dos veces o no disponible cuando debería ser vendible. El modelo antiguo también carecía de conciencia de ubicación. El reemplazo debe elegir el stock de algún lugar que pueda cumplir con el pedido. Un almacén en el continente equivocado es una excelente entrada de base de datos y una terrible promesa de entrega. Los intentos anteriores de MySQL usaban una fila de cantidad, por lo que los pagos concurrentes hacían fila en el

¿Cuál se convierte en la unidad bloqueable?

0:56 mismo bloqueo. Piense en una cuerda de terciopelo alrededor de una celda de hoja de cálculo. Agregar más trabajadores solo alarga la fila. Shopify cambió el objeto bloqueable. Cada unidad disponible obtiene su propia fila. Una lectura de bloqueo omite unidades que otra transacción retiene y selecciona otras unidades elegibles. Diferentes trabajadores pueden adquirir diferentes filas, mientras el contador "caliente" se mantiene fuera de su camino.

¿Qué sucede cuando el grupo se vacía?

1:15 Ese grupo está limitado a mil filas por artículo y ubicación. El reabastecimiento se extrae del libro mayor. Si se vacía, la ruta de reserva se reabastece en línea, con solicitudes concurrentes esperando detrás de un bloqueo de reabastecimiento. Grupo vacío no significa almacén vacío. La reserva elimina las filas del grupo seleccionadas, luego inserta registros de reserva en una transacción. La confirmación libera los bloqueos de la base de datos.

1:35 La reversión deshace los cambios. La reserva sobrevive al procesamiento de pagos como estado almacenado. El pago exitoso reclama el libro mayor y elimina la reserva atómicamente. Los bloqueos de la base de datos nunca necesitan supervisar el formulario de pago. Su clave principal compuesta comienza con tienda, artículo, grupo, luego identidad de la unidad. La coincidencia de la búsqueda redujo el bloqueo de índices en su prototipo. También usan lectura confirmada para evitar los bloqueos de huecos que bloqueaban el

1:57 reabastecimiento del grupo, y un orden de tabla consistente para evitar esperas circulares. El ejemplo publicado registra un tiempo de caducidad. Los pagos abandonados necesitan que el stock se libere eventualmente, o el carrito de compras se convierte en un arrendador. La publicación de Shopify deja ese algoritmo de limpieza sin especificar, por lo que este diagrama muestra el requisito del ciclo de vida. Aquí está el truco.

¿Qué omite SKIP LOCKED?

2:14 Skip locked excluye las filas bloqueadas, por lo que el manual llama a su resultado una vista inconsistente. No proporciona un recuento completo de existencias ni un turno justo. Mantenga la decisión de disponibilidad y las reglas de reabastecimiento a su alrededor.

¿Dónde estaba el verdadero límite?

2:26 ¿Y ese límite? Otro código de pago estaba manteniendo las conexiones a la base de datos demasiado tiempo. Shopify etiquetó a los llamadores y midió el tiempo de retención de la conexión, luego limpió la ruta de pago y revisó la concurrencia de hilos. Las consultas rápidas aún pueden hacer fila fuera de la consulta.

¿Por qué implementaría este diseño?

2:38 Escribieron en paralelo ambos sistemas con Redis como autoridad, compararon los resultados y luego cambiaron gradualmente con un "kill switch". Mi veredicto es SHIP IT (implementar). Implementaría el límite de transacción compartido y esa implementación reversible, con toda la ruta de pago instrumentada. ¿Tienes alguna pregunta sobre esto? Ponla en los comentarios. Y esa es la diferencia por hoy.

2:54 Soy Niko de Axrisi. Fusiona responsablemente.

Fuentes

  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

Videos relacionados