Los bloqueos pesimistas y optimistas son enfoques conceptuales para manejar recursos concurrentes en bases de datos. No deben confundirse con los mecanismso de bloqueo específicos de MySQL (bloqueos de tabla, de fila, exclusivos, compartidos).
Bloqueo Pesimista
El bloqueo pesimista asume que ocurrirán conflictos de concurrencia al acceder y modificar datos. Por lo tanto, durante todo el proceso de manipulación de datos, estos deben mantenerse bloqueados.
La implementación del bloqueo pesimista generalmente utiliza los mecanismos de bloqueo proporcionados por la base de datos, como el bloqueo exclusivo de MySQL medianet la cláusula SELECT ... FOR UPDATE.
Ejemplo: Control de inventario en un sistema de ventas para evitar sobreventa.
CREATE TABLE `inventario_productos` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`producto_id` bigint(20) unsigned DEFAULT '0',
`cantidad_disponible` int(11) unsigned DEFAULT '0',
`fecha_creacion` datetime DEFAULT NULL,
`fecha_actualizacion` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `producto_id` (`producto_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Tabla de inventario de productos';
El campo cantidad_disponible se define como unsigned para evitar valores negativos a nivel de base de datos.
Importante: Para utilizar bloqueos pesimistas, es necesario desactivar el autocommit de MySQL con set autocommit = 0;
Es fundamental recordar que los bloqueos a nivel de fila en MySQL se basan en índices. Si una consulta no utiliza índices, se aplicará un bloqueo a toda la tabla.
- Iniciar una transacción y consultar el producto bloqueando el registro:
BEGIN;
SELECT cantidad_disponible FROM inventario_productos WHERE producto_id = {$id_producto} FOR UPDATE;
- Verificar si hay stock suficiente. Si no hay suficiente, revertir la transacción.
- Si hay stock suficiente, actualizar el inventario y confirmar la transacción:
UPDATE inventario_productos SET cantidad_disponible = cantidad_disponible - {$cantidad_solicitada}
WHERE producto_id = {$id_producto} AND cantidad_disponible >= {$cantidad_solicitada};
COMMIT;
Los bloqueos se liberan al confirmar la transacción.
El bloqueo pesimista adopta un enfoque conservador que prioriza la seguridad sobre el rendimiento, lo que puede reducir la eficiencia del sistema.
Bloqueo Optimista
El bloqueo optimista parte de la premisa de que generalmente no ocurrirán conflictos de concurrencia. Solo se verifica si hay conflictos en el momento de actualizar los datos.
Si se detecta un conflicto, se informa al usuario para que decida cómo proceder.
La implementación del bloqueo optimista no depende de los mecanismos de bloqueo de la base de datos, sino que requiere implementación personalizada, generalmente mediante versiones de datos (números de versión o marcas de tiempo).
Se añade un campo de versión o timestamp a la tabla. Al leer los datos, se obtiene también el número de versión. Al actualizar, se incrementa la versión.
Al enviar la actualización, se verifica si la versión actual coincide con la versión obtenida inicialmente. Si coincide, se permite la actualización; de lo contrario, se considera que los datos están desactualizados y se rechaza la actualización.
CREATE TABLE `inventario_productos` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`producto_id` bigint(20) unsigned DEFAULT '0',
`cantidad_disponible` int(11) unsigned DEFAULT '0',
`fecha_creacion` datetime DEFAULT NULL,
`fecha_actualizacion` datetime DEFAULT NULL,
`version_registro` bigint(20) unsigned DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `producto_id` (`producto_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Tabla de inventario con control de versiones';
- Consultar el producto obteniendo también el número de versión:
BEGIN;
SELECT cantidad_disponible, version_registro FROM inventario_productos WHERE producto_id = {$id_producto};
- Verificar si hay stock suficiente. Si no hay suficiente, revertir la transacción.
- Si hay stock suficiente, actualizar el invantario verificando que la versión no haya cambiado:
UPDATE inventario_productos
SET cantidad_disponible = cantidad_disponible - {$cantidad_solicitada},
version_registro = version_registro + 1
WHERE producto_id = {$id_producto}
AND version_registro = {$version_obtenida}
AND cantidad_disponible >= {$cantidad_solicitada};
- Verificar si la actualización fue exitosa. Si lo fue, confirmar la transacción; de lo contrario, revertirla.
El bloqueo optimista se implementa a nivel de aplicación, por lo que no hay riesgo de deadlocks. Es adecuado para escenarios con más lecturas que escrituras. Sin embargo, si los conflictos son frecuentes, el rendimiento puede degradarse debido a los reintentos, en cuyo caso el bloqueo pesimista sería más adecuado.