Cómo las bases de datos gestionan las transacciones simultáneas sin corromper los datos
El control de concurrencia es el conjunto de mecanismos que un sistema de gestión de bases de datos (DBMS) utiliza para gestionar transacciones simultáneas sin corromper los datos. Cuando varios usuarios o procesos leen y escriben al mismo tiempo, el acceso no controlado conduce a anomalías en los datos, como actualizaciones perdidas, lecturas sucias y resultados inconsistentes que pueden propagarse a través de sistemas posteriores.
El control de concurrencia aplica la "I" (Aislamiento) de las transacciones ACID: las transacciones concurrentes deben producir el mismo resultado que si se ejecutaran una tras otra, una propiedad conocida como serializabilidad. Sin él, una aplicación bancaria podría perder transferencias, un sistema de inventario podría vender productos en exceso y un pipeline de análisis podría producir informes basados en datos a medio escribir.
Esta guía cubre los problemas centrales de concurrencia, los principales mecanismos para resolverlos —bloqueo, control de concurrencia multiversión (MVCC) y estrategias optimistas y pesimistas— junto con los niveles de aislamiento, el manejo de interbloqueos y la orientación práctica para elegir el enfoque correcto para su carga de trabajo.
Los sistemas de bases de datos modernos manejan rutinariamente miles a millones de transacciones concurrentes por segundo. Cada vez que un usuario envía una consulta, actualiza un registro o inicia un pipeline de ETL, el DBMS debe coordinar esa operación junto con todas las demás operaciones que ocurren en el mismo momento. Sin control de concurrencia, las transacciones intercaladas producen resultados impredecibles e incorrectos.
Considere un escenario concreto: dos usuarios actualizan el saldo de la misma cuenta bancaria simultáneamente. El usuario A lee el saldo (1.000 $), resta 200 $ y escribe 800 $. El usuario B también lee 1.000 $, suma 500 $ y escribe 1.500 $. El resultado correcto depende del orden —ya sea 800 $ o 1.500 $— pero sin coordinación, una escritura simplemente sobrescribe la otra. Esta es una actualización perdida clásica, una de varias anomalías que el control de concurrencia existe para prevenir.
El control de concurrencia impone la serializabilidad: el resultado de la ejecución concurrente debe coincidir con algún orden serial de esas mismas transacciones. Esto apoya directamente las cuatro garantías ACID: atomicidad (todo o nada), consistencia (transiciones de estado válidas), aislamiento (sin interferencia) y durabilidad (los cambios confirmados son permanentes). Esto garantiza la base sobre la cual cada aplicación construye la confianza en sus datos.
Antes de explorar las soluciones, es útil comprender las anomalías específicas que surgen cuando las transacciones concurrentes se ejecutan sin los controles adecuados.
Estas cuatro anomalías son el "por qué" detrás de cada mecanismo de control de concurrencia. Cada mecanismo previene algunas o todas ellas, dependiendo del nivel de aislamiento que impone.
El bloqueo es el enfoque más antiguo e intuitivo para el control de concurrencia. El DBMS asigna bloqueos a elementos de datos —filas, páginas o tablas enteras— antes de permitir que las transacciones accedan a ellos. Un bloqueo actúa como un portero: si otra transacción ya tiene un bloqueo conflictivo, la transacción solicitante debe esperar.
La granularidad del bloqueo introduce una compensación importante. Los bloqueos a nivel de fila maximizan la concurrencia porque las filas no relacionadas permanecen accesibles, pero agregan sobrecarga porque el sistema debe rastrear muchos bloqueos individuales. Los bloqueos a nivel de tabla son más simples y menos costosos de administrar, pero obligan a las transacciones no relacionadas a esperar, lo que reduce el rendimiento.
El bloqueo de dos fases es el protocolo estándar para garantizar la serializabilidad a través de bloqueos. Divide cada transacción en dos fases. Durante la fase de crecimiento, una transacción adquiere todos los bloqueos que necesita pero nunca libera ninguno. Una vez que libera su primer bloqueo, entra en la fase de encogimiento y solo puede liberar bloqueos, nunca adquirir nuevos. Esta regla garantiza que el orden en que las transacciones bloquean los elementos de datos sea consistente, lo que a su vez garantiza un programa serializable.
Strict 2PL es una variante común que mantiene todos los bloqueos hasta que la transacción se confirma. Esto previene reversiones en cascada, una situación en la que una transacción abortada obliga a otras transacciones que leyeron sus datos no confirmados a abortar también. La mayoría de las bases de datos relacionales que utilizan control de concurrencia basado en bloqueos implementan alguna forma de strict 2PL.
La desventaja de 2PL es que introduce bloqueos —las transacciones deben esperar por los bloqueos que tienen otras— y crea las condiciones para los interbloqueos.
MVCC adopta un enfoque fundamentalmente diferente: en lugar de bloquear el acceso, mantiene múltiples versiones de cada elemento de datos para que cada transacción vea una instantánea coherente de la base de datos. Cuando una transacción escribe una fila, MVCC crea una nueva versión en lugar de sobrescribir la existente. Los lectores continúan viendo la versión que estaba vigente cuando comenzó su transacción.
La principal ventaja es que los lectores nunca bloquean a los escritores y los escritores nunca bloquean a los lectores. Este es un beneficio de rendimiento importante para cargas de trabajo con muchas lecturas, razón por la cual MVCC es el mecanismo de control de concurrencia dominante en las bases de datos modernas. PostgreSQL, MySQL/InnoDB, Oracle y la mayoría de los sistemas contemporáneos de OLTP y OLAP utilizan alguna forma de MVCC.
Los conflictos de escritura se detectan en el momento de la confirmación. Si dos transacciones modifican la misma fila, la primera en confirmarse gana y la segunda debe reintentar. Este enfoque optimista para la resolución de conflictos de escritura mantiene un alto rendimiento cuando los conflictos son raros, como suele ser el caso.
Aislamiento de instantáneas es el nivel de aislamiento más común basado en MVCC. Cada transacción ve la base de datos como estaba cuando comenzó la transacción, lo que previene lecturas sucias y lecturas no repetibles. Sin embargo, el aislamiento de instantáneas permite una anomalía sutil llamada sesgo de escritura, donde dos transacciones leen datos superpuestos y luego realizan escrituras basadas en lo que leyeron, lo que resulta en un estado que ninguna de las transacciones habría producido por sí sola. El aislamiento de instantáneas serializable (SSI) aborda esta brecha a costa de una sobrecarga adicional.
MVCC introduce sus propias compensaciones. Mantener múltiples versiones consume almacenamiento adicional y el sistema debe recolectar periódicamente versiones obsoletas, un proceso conocido como VACUUM en PostgreSQL. Bajo una alta contención de escritura, el costo de reintento y la sobrecarga de gestión de versiones pueden volverse significativos.
Más allá de los mecanismos específicos de bloqueo y MVCC, las estrategias de control de concurrencia se dividen en dos campos filosóficos: pesimista y optimista. Comprender qué campo se adapta a su carga de trabajo es una de las decisiones arquitectónicas más importantes que puede tomar un equipo de datos.
El control de concurrencia pesimista asume que los conflictos son probables y los previene adquiriendo bloqueos antes de acceder a los datos. Este enfoque es mejor para cargas de trabajo con muchas escrituras donde los conflictos son frecuentes y el costo de revertir y reintentar una transacción es alto. Los sistemas de libros de contabilidad financieros, donde cada escritura debe secuenciarse y verificarse, son un caso de uso clásico.
La desventaja es el bloqueo. Bajo alta concurrencia, las transacciones se ponen en cola esperando bloqueos, lo que reduce el rendimiento. En el peor de los casos, las solicitudes de bloqueo en competencia crean interbloqueos.
El control de concurrencia optimista asume que los conflictos son raros. Las transacciones se ejecutan libremente sin adquirir bloqueos, operando en sus propias copias privadas de los datos. En el momento de la confirmación, el sistema valida si las lecturas y escrituras de la transacción entran en conflicto con alguna otra transacción confirmada. Si no se detectan conflictos, la transacción se confirma. Si se detecta un conflicto, la transacción se revierte y se reintenta.
Este modelo de tres fases — leer, validar, escribir — sobresale en entornos donde la mayoría de las transacciones no tocan los mismos datos. Los sistemas de gestión de contenido, las aplicaciones de navegación de catálogos y los paneles de informes son ejemplos típicos.
La desventaja es el trabajo desperdiciado. Cuando los conflictos son frecuentes, las transacciones ejecutan repetidamente su lógica completa solo para ser revertidas en la validación, consumiendo recursos sin producir resultados.
La elección entre control pesimista y optimista depende de las características de la carga de trabajo. La alta contención de escritura, las transacciones cortas y la baja tolerancia a reintentos apuntan hacia el control pesimista. Las cargas de trabajo de lectura intensiva con baja probabilidad de conflicto y altos requisitos de concurrencia favorecen los enfoques optimistas o basados en MVCC.
En la práctica, muchos sistemas de producción combinan ambas estrategias. Un patrón común utiliza MVCC para lecturas y escrituras generales, mientras aplica bloqueos pesimistas en filas de puntos calientes conocidos; por ejemplo, usando SELECT ... FOR UPDATE para bloquear una fila específica que múltiples transacciones probablemente contendrán simultáneamente.
Los niveles de aislamiento definen qué anomalías de concurrencia puede encontrar una transacción. Los niveles más estrictos evitan más anomalías pero reducen la concurrencia. El estándar SQL define cuatro niveles, y la mayoría de las bases de datos implementan alguna variante de ellos.
| Nivel de aislamiento | Lecturas sucias | Lecturas no repetibles | Lecturas fantasma | Caso de uso típico |
|---|---|---|---|---|
| Lectura sin confirmar | Posible | Posible | Posible | Rara vez se usa en producción |
| Lectura confirmada | Evitado | Posible | Posible | Predeterminado en PostgreSQL, Oracle |
| Lectura repetible | Evitado | Evitado | Posible | Predeterminado en MySQL/InnoDB |
| Serializable | Evitado | Evitado | Evitado | Financiero, cumplimiento, seguridad crítica |
La mayoría de las aplicaciones operan de forma segura en Lectura Confirmada o Lectura Repetible. Elegir Serializable es una compensación deliberada que debe basarse en requisitos comerciales específicos en lugar de usarse como predeterminado.
Un interbloqueo ocurre cuando dos o más transacciones retienen bloqueos que la otra necesita, creando un ciclo de espera mutua. Ninguna transacción puede continuar y, sin intervención, ambas esperarían indefinidamente.
Consejos prácticos para minimizar interbloqueos: mantenga las transacciones lo más cortas posible, acceda a los recursos en un orden predecible, establezca valores apropiados de tiempo de espera de bloqueo y monitoree la frecuencia de interbloqueos como una señal de salud del sistema.
No hay una respuesta universal. El mecanismo adecuado depende de las características de la carga de trabajo, los requisitos de consistencia y las compensaciones operativas. Varios factores guían la decisión.
Muchos sistemas de producción combinan estrategias: MVCC como predeterminado, bloqueos pesimistas en filas de puntos calientes conocidos y lógica de reintento a nivel de aplicación para la resolución de conflictos optimista.
Las bases de datos distribuidas enfrentan desafíos de coordinación adicionales. La latencia de red entre nodos, la necesidad de tolerancia a particiones y los requisitos de consenso aumentan el costo y la complejidad del control de concurrencia. Los enfoques incluyen 2PL distribuido, MVCC distribuido con marcas de tiempo globales — como se usa en el sistema TrueTime de Google Spanner — y protocolos de consenso como Raft y Paxos.
Las plataformas analíticas y de lakehouse manejan la concurrencia de manera diferente a las bases de datos OLTP tradicionales. Estos sistemas están optimizados para cargas de trabajo de escaneo grande y lectura intensiva con aislamiento de instantáneas en lugar de los patrones de bloqueo a nivel de fila comunes en los sistemas transaccionales.
Databricks y Delta Lake utilizan control de concurrencia optimista para escrituras concurrentes en la misma tabla. Las escrituras siguen un proceso de tres etapas: leer la última versión de la tabla para identificar los archivos afectados, escribir nuevos archivos de datos y luego validar en el momento de la confirmación que no ocurrieron cambios conflictivos. Si las transacciones no entran en conflicto, tienen éxito. Si se detecta un conflicto, la reintentación automática o la resolución de conflictos se encargan del resto. Delta Lake implementa un modelo MVCC sin bloqueo, por lo que los lectores siempre ven una instantánea consistente mientras los escritores proceden de forma independiente.
Las transacciones ACID en tablas de Delta Lake garantizan la consistencia de los datos incluso cuando múltiples pipelines, usuarios y consultas acceden a los mismos datos simultáneamente. Para una mirada detallada a cómo funciona la concurrencia a nivel de fila en la práctica, el blog de ingeniería de Databricks ofrece una inmersión profunda en la mecánica subyacente.
Este enfoque extiende las garantías de concurrencia más allá del OLTP tradicional a cargas de trabajo de ingeniería de datos, ETL y ML donde múltiples escritores y lectores operan en conjuntos de datos compartidos. Al combinar el control de concurrencia optimista, el aislamiento de instantáneas y la resolución automática de conflictos, las plataformas modernas de lakehouse brindan la confiabilidad de las transacciones de nivel de base de datos a la escala y flexibilidad de los lagos de datos en la nube.
Comprender la teoría del control de concurrencia es un desafío. Implementarlo de manera confiable en datos distribuidos, múltiples escritores y cargas de trabajo diversas es otro. Los equipos que gestionan sus propias estrategias de concurrencia dedican un tiempo de ingeniería significativo a configurar niveles de aislamiento, ajustar el comportamiento de bloqueo y crear lógica de reintento, tiempo que no avanza su trabajo real.
Databricks Lakebase elimina esa carga operativa. Construido sobre la Plataforma de Inteligencia de Datos de Databricks, Lakebase es una base de datos totalmente administrada y nativa de la nube que brinda confiabilidad transaccional al lakehouse sin requerir que los equipos implementen o configuren el control de concurrencia ellos mismos. Tal como está, proporciona control de concurrencia optimista, aislamiento de instantáneas para lecturas y aislamiento serializable de escritura para escrituras, los mismos mecanismos que cubre este artículo, aplicados automáticamente.
Debido a que Lakebase utiliza control de concurrencia optimista en Delta Lake, no hay bloqueos que administrar ni deadlocks que depurar. Las escrituras concurrentes siguen el patrón de lectura-validación-confirmación descrito anteriormente en esta guía: cada transacción lee la última versión de la tabla, escribe nuevos archivos de datos y valida en el momento de la confirmación que no ocurrieron cambios conflictivos. Cuando múltiples pipelines, usuarios o consultas escriben en la misma tabla, la detección de conflictos a nivel de fila de Delta Lake resuelve automáticamente los cambios no superpuestos, incluso para operaciones MERGE, UPDATE y DELETE concurrentes. Los lectores siempre ven una instantánea consistente, no afectada por las escrituras en curso.
Esto no se limita a cargas de trabajo analíticas. Lakebase extiende las garantías transaccionales a cargas de trabajo operativas, ingeniería de datos, pipelines ETL y ML, todo en la misma plataforma. En lugar de mantener una base de datos OLTP separada junto a un lakehouse y unirlos con código de integración personalizado, los equipos ejecutan todo a través de una única arquitectura gobernada. Los datos permanecen en formatos abiertos en almacenamiento en la nube de bajo costo, con la capa de cómputo transaccional ejecutándose de forma independiente encima.
El resultado: cada concepto de control de concurrencia cubierto en este artículo — MVCC, validación optimista, aislamiento de instantáneas, resolución de conflictos — funciona por defecto en Databricks. Los equipos pueden centrarse en sus datos y sus cargas de trabajo, no en su estrategia de concurrencia. Explore Lakebase para ver cómo funciona en la práctica.
(Esta entrada del blog ha sido traducida utilizando herramientas basadas en inteligencia artificial) Publicación original
Suscríbete a nuestro blog y recibe las últimas publicaciones directamente en tu bandeja de entrada.