Locking optimista vs pesimista — ¿cuándo gana cada uno?
Ambos evitan que dos transacciones pisen la misma fila — solo difieren en si pagan el costo de coordinación por adelantado o solo cuando hay un conflicto real.
APRENDE HACIENDOPon a prueba tu intuición3 pasos
Locking optimista vs pesimista — ¿cuándo gana cada uno?
Usa los ejemplos y la explicación de la lección para comprobar tu idea.
Reflexión personal. No se envía a ningún servicio de IA.#El compromiso central
Tanto el locking optimista como el pesimista resuelven el mismo problema — dos transacciones quieren tocar la misma fila y necesitas evitar que una pise silenciosamente el trabajo de la otra (la anomalía de "actualización perdida" o lost update). Simplemente no se ponen de acuerdo sobre cuándo pagar el costo de coordinarse.
El locking pesimista asume que el conflicto es probable, así que toma un lock antes de hacer cualquier trabajo y lo mantiene hasta que la transacción termina. El locking optimista asume que el conflicto es raro, hace el trabajo sobre una copia privada de los datos, y solo verifica si hubo conflicto al final, justo antes de confirmar.
#Locking pesimista, en SQL
BEGIN;SELECT saldo FROM cuentas WHERE id = 1 FOR UPDATE; -- la fila queda bloqueada-- la aplicación calcula el nuevo saldoUPDATE cuentas SET saldo = saldo - 100 WHERE id = 1;COMMIT; -- se libera el lock
SELECT ... FOR UPDATE toma un lock exclusivo a nivel de fila de inmediato. Cualquier otra transacción que intente hacer SELECT ... FOR UPDATE (o un UPDATE) sobre esa misma fila simplemente queda bloqueada hasta que esta transacción confirme o haga rollback. Ninguna otra transacción puede siquiera leer una versión desactualizada y competir contigo por la actualización.
#Locking optimista, con una columna de versión
-- La tabla tiene una columna entera "version"SELECT id, saldo, version FROM cuentas WHERE id = 1;-- la aplicación lee saldo = 500, version = 7, calcula nuevo saldo = 400UPDATE cuentasSET saldo = 400, version = version + 1WHERE id = 1 AND version = 7;-- la aplicación verifica: ¿este UPDATE afectó 1 fila?
Si otra transacción actualizó la fila (y subió la versión) en el medio, este UPDATE no coincide con ninguna fila — la cláusula WHERE version = 7 falla — y la aplicación sabe que debe reintentar: releer la fila, recalcular, e intentar de nuevo. Nunca se mantuvo ningún lock mientras la aplicación estaba "pensando."
int actualizadas = jdbcTemplate.update("UPDATE cuentas SET saldo = ?, version = version + 1 WHERE id = ? AND version = ?",nuevoSaldo, idCuenta, versionEsperada);if (actualizadas == 0) {throw new OptimisticLockException("La fila cambió de forma concurrente, reintentar");}
#Cuándo gana cada uno
| Pesimista | Optimista | |
|---|---|---|
| Mejor bajo | Alta contención, mismas filas golpeadas constantemente | Baja contención, los conflictos son raros |
| Costo sin conflicto | Paga por un lock de todas formas | Casi no paga nada |
| Costo CON conflicto | La otra transacción simplemente espera | Trabajo desperdiciado + un viaje de reintento |
| Modo de falla | Bloqueo, deadlocks, timeouts de lock | Tormentas de reintentos bajo alta contención |
| Funciona bien con | Una sola base de datos, transacciones cortas | Transacciones largas, clientes desconectados/sin estado (una request web no mantiene ningún lock durante el "tiempo de pensar") |
#Los deadlocks son el impuesto del locking pesimista
El locking pesimista introduce un modo de falla nuevo que el optimista no tiene: el deadlock. Si la transacción A bloquea la fila 1 y después intenta bloquear la fila 2, mientras la transacción B bloquea la fila 2 y después intenta bloquear la fila 1, ninguna de las dos puede avanzar. La base de datos detecta el ciclo y mata una de las dos transacciones (la "víctima del deadlock") para romperlo.
-- Transacción ABEGIN;SELECT * FROM cuentas WHERE id = 1 FOR UPDATE;-- ... hace algo de trabajo ...SELECT * FROM cuentas WHERE id = 2 FOR UPDATE; -- se bloquea, esperando a B-- Transacción B, corriendo al mismo tiempoBEGIN;SELECT * FROM cuentas WHERE id = 2 FOR UPDATE;-- ... hace algo de trabajo ...SELECT * FROM cuentas WHERE id = 1 FOR UPDATE; -- se bloquea, esperando a A -> deadlock