Ir al contenido principal

Control de concurrencia en DBMS: cómo el bloqueo, MVCC y las estrategias optimistas mantienen la coherencia de los datos

Cómo las bases de datos gestionan las transacciones simultáneas sin corromper los datos

por Personal de Databricks

  • El control de concurrencia evita que las transacciones simultáneas corrompan los datos; sin él, el acceso no controlado conduce a anomalías como lecturas sucias, actualizaciones perdidas y lecturas fantasma que se propagan a través de los sistemas posteriores.
  • El bloqueo y MVCC adoptan enfoques opuestos para el mismo problema: el bloqueo pesimista bloquea el acceso por adelantado para prevenir conflictos, mientras que MVCC mantiene múltiples versiones de datos para que los lectores y escritores nunca se bloqueen entre sí.
  • La estrategia correcta depende de tu carga de trabajo: los sistemas con muchas escrituras favorecen el bloqueo pesimista, las cargas de trabajo con muchas lecturas o pocos conflictos se benefician de enfoques optimistas o basados en MVCC, y la mayoría de los sistemas de producción combinan ambos.

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.

Por qué es importante el control de concurrencia

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.

Problemas de concurrencia a resolver

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.

  • Lectura sucia. Una transacción lee datos escritos por otra transacción no confirmada. Si esa segunda transacción se revierte, la primera transacción basó su lógica en datos que en realidad nunca existieron. Las lecturas sucias se encuentran entre las anomalías más peligrosas porque introducen un estado fantasma en la toma de decisiones.
  • Actualización perdida. Dos transacciones leen el mismo elemento de datos y luego ambas escriben valores actualizados. La segunda escritura sobrescribe la primera sin incorporar sus cambios, borrando efectivamente una actualización válida. El escenario de la cuenta bancaria anterior es un ejemplo clásico.
  • Lectura no repetible. Una transacción lee la misma fila dos veces y obtiene valores diferentes porque otra transacción modificó y confirmó la fila entre lecturas. Esto rompe cualquier lógica que dependa de datos estables dentro de una sola transacción.
  • Lectura fantasma. Una transacción vuelve a ejecutar una consulta de rango y obtiene un conjunto diferente de filas porque otra transacción insertó o eliminó filas coincidentes en el intervalo. Los fantasmas son particularmente problemáticos para las consultas agregadas y las cargas de trabajo de informes.

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.

Control de concurrencia basado en bloqueos

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.

Bloqueos compartidos y exclusivos

  • Los bloqueos compartidos (lectura) permiten que varias transacciones lean los mismos datos simultáneamente. Dado que nadie está modificando los datos, las lecturas concurrentes son seguras.
  • Los bloqueos exclusivos (escritura) otorgan a una sola transacción acceso exclusivo a un elemento de datos. Mientras se mantiene un bloqueo exclusivo, ninguna otra transacción puede leer o escribir ese elemento. Esto previene actualizaciones perdidas y lecturas sucias, pero también limita la concurrencia.

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.

Bloqueo de dos fases (2PL)

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.

Control de concurrencia multiversión (MVCC)

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.

Control de concurrencia optimista vs. pesimista

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.

Control pesimista

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.

Control optimista

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.

Cuándo usar cada uno

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.

Informe

La guía de IA agéntica para la empresa

Niveles de aislamiento y sus compensaciones

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 aislamientoLecturas suciasLecturas no repetiblesLecturas fantasmaCaso de uso típico
Lectura sin confirmarPosiblePosiblePosibleRara vez se usa en producción
Lectura confirmadaEvitadoPosiblePosiblePredeterminado en PostgreSQL, Oracle
Lectura repetibleEvitadoEvitadoPosiblePredeterminado en MySQL/InnoDB
SerializableEvitadoEvitadoEvitadoFinanciero, cumplimiento, seguridad crítica
  • Lectura sin confirmar ofrece máxima concurrencia pero permite todas las anomalías, incluidas las lecturas sucias. Raramente se usa en producción porque los riesgos superan con creces las ganancias de rendimiento.
  • Lectura confirmada evita las lecturas sucias asegurando que una transacción solo vea datos confirmados antes de que se ejecute cada sentencia. Permite lecturas no repetibles y fantasma, lo cual es una compensación aceptable para la mayoría de las aplicaciones web y cargas de trabajo de propósito general. PostgreSQL y Oracle usan este nivel como predeterminado.
  • Lectura repetible evita lecturas sucias y no repetibles al garantizar que cualquier fila leída durante una transacción devolverá el mismo valor si se lee de nuevo. Aún pueden ocurrir fantasma. MySQL/InnoDB usa este nivel por defecto.
  • Serializable evita todas las anomalías. Las transacciones se comportan como si se ejecutaran una a la vez, en algún orden serial. Esta es la garantía de consistencia más alta pero viene con la menor concurrencia. Típicamente se reserva para cargas de trabajo financieras, de cumplimiento o de seguridad crítica donde la corrección absoluta no es negociable.

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.

Interbloqueos: prevención y resolución

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.

  • Detección. La mayoría de las bases de datos detectan interbloqueos comprobando periódicamente ciclos en un grafo de espera, una estructura de datos que mapea qué transacción está esperando qué bloqueo. Cuando se encuentra un ciclo, el DBMS selecciona una transacción “víctima” y la aborta, liberando los bloqueos para que las transacciones restantes puedan continuar.
  • Prevención. La estrategia de prevención más efectiva es adquirir bloqueos en un orden consistente y predecible en todas las transacciones. Si cada transacción bloquea recursos en la misma secuencia, no se pueden formar condiciones de espera circulares. Los tiempos de espera de bloqueo proporcionan una red de seguridad adicional: si una transacción espera demasiado, se aborta en lugar de contribuir a un posible interbloqueo.
  • Resolución. La transacción víctima abortada se revierte y típicamente es reintentada automáticamente por la aplicación. Dado que los interbloqueos suelen ser infrecuentes, el costo del reintento es modesto.

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.

Elegir el enfoque de control de concurrencia adecuado

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.

  • Relación lectura-escritura. Las cargas de trabajo dominadas por lecturas se benefician del MVCC o del control optimista, que permiten lecturas concurrentes sin bloqueo. Las cargas de trabajo de escritura intensiva a menudo necesitan bloqueo pesimista para evitar el costo de reintentos frecuentes.
  • Frecuencia de conflictos. Cuando los conflictos son raros, como en la mayoría de las cargas de trabajo analíticas y web, el control optimista ofrece un mayor rendimiento. Cuando los conflictos son frecuentes, la sobrecarga de reintentos hace que el control pesimista sea más eficiente en general.
  • Duración de la transacción. Las transacciones cortas funcionan bien con el bloqueo porque los bloqueos se mantienen brevemente. Las transacciones de larga duración se benefician del MVCC, que evita bloqueos prolongados.
  • Requisitos de consistencia. Los requisitos estrictos (financieros, de cumplimiento) favorecen el aislamiento Serializable con 2PL. Los requisitos moderados (aplicaciones web, catálogos) se satisfacen bien con Lectura Confirmada con MVCC.
  • Distribuido vs. nodo único. Los sistemas distribuidos introducen sobrecarga de coordinación — latencia de red, tolerancia a particiones y requisitos de consenso — que hace que el control de concurrencia sea más complejo y costoso.

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.

Control de concurrencia en sistemas distribuidos y analíticos

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.

Control de concurrencia sin la complejidad: cómo lo maneja Databricks

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

Recibe las últimas publicaciones en tu bandeja de entrada

Suscríbete a nuestro blog y recibe las últimas publicaciones directamente en tu bandeja de entrada.