Explica cómo índices, transacciones y niveles de aislamiento protegen rendimiento e invariantes, y qué anomalías aparecen bajo concurrencia real.
Última actualización
Actualizada
Nivel
Aplicación
Los índices aceleran accesos concretos; las transacciones agrupan cambios; el aislamiento controla qué pueden observar operaciones concurrentes. Juntos determinan si una base puede responder rápido sin romper integridad.
Una base relacional no garantiza automáticamente buen rendimiento ni concurrencia correcta. El resultado depende de cómo se modelan consultas, índices, transacciones y conflictos.
Tres preguntas guían esta nota:
¿Cómo encuentra el motor los datos?
¿Cómo agrupa modificaciones relacionadas?
¿Qué ocurre cuando varias operaciones compiten al mismo tiempo?
Un B-tree mantiene claves ordenadas y permite buscar, recorrer rangos y ordenar.
Es apropiado para condiciones como:
SQL
WHERE tenant_id = :tenant
AND created_at >= :fromORDERBY created_at DESC
También puede ayudar con igualdad, rangos y prefijos, según columnas y operador.
Otros tipos —hash, inverted, spatial, BRIN, GIN, GiST u opciones propias del motor— optimizan access patterns diferentes. Debe revisarse la documentación concreta antes de asumir comportamiento.
El orden de columnas representa cómo se navega la estructura.
Supón:
SQL
CREATEINDEX idx_orders_tenant_status_created
ON orders (tenant_id,status, created_at DESC);
Puede favorecer:
SQL
WHERE tenant_id = ?
WHERE tenant_id = ? ANDstatus= ?
WHERE tenant_id = ? ANDstatus= ? ORDERBY created_at DESC
No necesariamente favorece una búsqueda solo por created_at o solo por status. El motor no puede aprovechar todas las combinaciones con la misma eficiencia.
La heurística del prefijo izquierdo ayuda, pero no sustituye revisar planes de ejecución y comportamiento específico.
Una transacción agrupa operaciones que deben confirmarse o abortarse como unidad lógica.
Texto
BEGIN
→ leer y validar estado
→ modificar recursos
→ registrar movimiento
→ COMMIT o ROLLBACK
La frontera debe seguir la invariante. Una transacción demasiado pequeña permite estados inválidos; una demasiado grande mantiene locks, consume conexiones y aumenta conflicto.
Los cambios de la transacción se confirman juntos o se deshacen. No significa que los efectos externos —correo, API de pago— puedan revertirse automáticamente.
La base pasa entre estados que respetan constraints y reglas ejecutadas. ACID no conoce todas las reglas del negocio si no están expresadas o protegidas por la aplicación.
Después del commit, los cambios sobreviven según configuración y garantías del motor. Replicación asíncrona, buffers o hardware pueden modificar el riesgo real.
Multi-Version Concurrency Control conserva versiones para que lecturas y escrituras interfieran menos.
Modelo mental:
Texto
fila lógica
├── versión visible para transacción A
└── versión nueva creada por transacción B
Cada transacción observa versiones válidas para su snapshot o statement, según aislamiento.
MVCC mejora concurrencia, pero genera versiones antiguas que deben limpiarse. Transacciones abiertas durante mucho tiempo pueden impedir cleanup y causar bloat.
Un lock pesimista puede ser correcto cuando el conflicto es probable y el recurso debe reservarse antes de actuar.
SQL
SELECT*FROM inventory
WHERE tenant_id = :tenant
AND product_id = :product
FORUPDATE;
Después la transacción valida y modifica. Sin embargo, mantener el lock mientras se llama una API externa sería peligroso: aumenta espera y riesgo de deadlock.
La reserva puede expresarse como una única modificación condicional:
SQL
UPDATE inventory
SET available = available - :qty,
reserved = reserved + :qty
WHERE tenant_id = :tenant
AND product_id = :product
AND available >= :qty;
Flujo:
La condición comprueba tenant, producto y disponibilidad en la misma operación.
El motor obtiene los locks necesarios.
Solo una secuencia válida de actualizaciones puede consumir las unidades.
El número de filas afectadas indica éxito o stock insuficiente.
La misma transacción registra el movimiento.
SQL
INSERTINTO inventory_movements (...)VALUES(...);
Si el insert falla, se revierte la actualización. Esta estrategia evita el patrón vulnerable leer–calcular–guardar.
Replicación, particionamiento y sharding explica qué cambia cuando los datos dejan de vivir en un único nodo y aparecen lag, failover, distribución y consultas cruzadas.