Bases de Datos · Tema 5 · Conectividad y Concurrencia

Estándares de Conectividad
y Concurrencia en Bases de Datos

ODBC · JDBC · JPA Problemas de concurrencia Mecanismos de resolución
Parte I

Estándares de Conectividad

// 01 · Open Database Connectivity

ODBC — Conectividad Abierta de Bases de Datos

ODBC es una API (Application Programming Interface) de bajo nivel, independiente del lenguaje y del SGBD, que define una interfaz estándar en C para acceder a bases de datos relacionales. Fue desarrollada por Microsoft en 1992 y adoptada posteriormente como estándar por el grupo SQL Access Group (SAG) de X/Open.

El objetivo fundamental de ODBC es conseguir la independencia entre la aplicación y el sistema gestor de bases de datos. Sin ODBC, cada aplicación necesitaría una implementación específica para cada SGBD (Oracle, SQL Server, MySQL…). Con ODBC, la aplicación habla con una API genérica y el driver ODBC (proporcionado por el fabricante del SGBD) se encarga de traducir las llamadas al dialecto nativo del motor de base de datos.

ODBC utiliza el concepto de DSN (Data Source Name), un identificador de configuración que agrupa los parámetros de conexión (driver, servidor, base de datos, credenciales) y que la aplicación usa para establecer la conexión sin necesidad de codificar esos parámetros en el código fuente.

Aunque ODBC es un estándar de los años 90, sigue siendo ampliamente utilizado en entornos empresariales para conectar herramientas de reporting y hojas de cálculo (como Excel o Power BI) a bases de datos corporativas, y es la base sobre la que se construyó JDBC.

📱 Aplicación (C, C++, Excel, Power BI…)
ODBC API · Llamadas estándar (SQLConnect, SQLExecDirect…)
Driver Manager · Gestiona y carga los drivers
Driver ODBC específico del SGBD
🗄️ Base de datos (Oracle, SQL Server, MySQL…)
📌 Concepto clave El Driver Manager es el componente central de la arquitectura ODBC. Se encarga de cargar el driver apropiado según el DSN especificado, enrutar las llamadas ODBC al driver correcto y gestionar el pool de conexiones. En Windows está integrado en el sistema operativo como odbcad32.exe.
C · ODBC /* Ejemplo básico de conexión y consulta con ODBC en C */ SQLHENV hEnv; SQLHDBC hDbc; SQLHSTMT hStmt; /* 1. Allocate environment handle */ SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv); SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); /* 2. Connect using DSN */ SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc); SQLConnect(hDbc, (SQLCHAR*)"MiDSN", SQL_NTS, (SQLCHAR*)"usuario", SQL_NTS, (SQLCHAR*)"contraseña", SQL_NTS); /* 3. Execute query */ SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt); SQLExecDirect(hStmt, (SQLCHAR*)"SELECT nombre, salario FROM empleados WHERE dpto = 10", SQL_NTS); /* 4. Fetch results */ while(SQLFetch(hStmt) == SQL_SUCCESS) { SQLGetData(hStmt, 1, SQL_C_CHAR, nombre, 50, &cb); /* procesar fila */ }
🌍

Independencia del SGBD

La misma aplicación puede conectar a Oracle, SQL Server, MySQL o PostgreSQL cambiando únicamente el DSN y el driver instalado, sin recompilar el código.

🪪

DSN (Data Source Name)

Fichero de configuración o entrada en el registro del sistema que almacena parámetros de conexión. Puede ser de usuario (solo accesible por el usuario actual), de sistema (accesible por todos) o de fichero.

⚠️

Limitaciones

API de bajo nivel en C; verbosa y propensa a errores de gestión de memoria. No orientada a objetos. Requiere instalación y configuración de drivers en cada máquina cliente.

API C Driver Manager DSN SQLConnect SQLExecDirect Driver ODBC independencia SGBD Microsoft 1992
// 02 · Java Database Connectivity

JDBC — Conectividad de Bases de Datos en Java

JDBC es la API estándar de Java (parte del Java SE) para acceder a bases de datos relacionales desde aplicaciones Java. Publicada por Sun Microsystems en 1997, define un conjunto de interfaces Java que los fabricantes de SGBD implementan en forma de drivers JDBC. Es, conceptualmente, el equivalente Java de ODBC.

JDBC sigue el mismo principio de independencia que ODBC: la aplicación llama a la API estándar de JDBC (interfaces como Connection, Statement, ResultSet), y el driver JDBC del SGBD proporciona la implementación concreta. Cambiar de base de datos es, en teoría, tan sencillo como cambiar la cadena de conexión y el driver.

JDBC define cuatro tipos de driver según cómo realizan la comunicación con el SGBD: Type 1 (puente JDBC-ODBC, obsoleto), Type 2 (native-API, usa librerías nativas del cliente), Type 3 (network protocol, traducción en un servidor intermedio) y Type 4 (pure Java driver), el más común hoy en día: implementado completamente en Java, se comunica directamente con el SGBD usando su protocolo de red nativo sin necesidad de software adicional en el cliente.

Una mejora fundamental respecto a ODBC es el soporte para PreparedStatement, que precompila la consulta SQL y permite reutilizarla con distintos parámetros. Además de mejorar el rendimiento, los PreparedStatements son la principal defensa contra los ataques de SQL Injection, ya que los parámetros se envían separados del código SQL y nunca se interpolan directamente en la cadena.

Java · JDBC // Ejemplo completo de operación JDBC con PreparedStatement import java.sql.*; public class EjemploJDBC { public static void main(String[] args) { // Cadena de conexión: protocolo:subprotocolo://host:puerto/BD String url = "jdbc:postgresql://localhost:5432/empresa"; String user = "admin"; String pass = "secreto"; // try-with-resources — cierra Connection, Statement y ResultSet automáticamente try (Connection conn = DriverManager.getConnection(url, user, pass)) { conn.setAutoCommit(false); // gestión manual de transacciones // PreparedStatement: parámetros separados → previene SQL Injection String sql = "UPDATE empleados SET salario = ? WHERE id = ?"; try (PreparedStatement ps = conn.prepareStatement(sql)) { ps.setBigDecimal(1, new BigDecimal("52000.00")); ps.setInt(2, 1042); int rows = ps.executeUpdate(); if (rows == 1) { conn.commit(); // ✓ confirmar transacción } else { conn.rollback(); // ✗ deshacer si algo fue mal } } // Consulta con ResultSet try (Statement st = conn.createStatement(); ResultSet rs = st.executeQuery("SELECT id, nombre, salario FROM empleados")) { while (rs.next()) { System.out.printf("ID: %d | %s | %.2f%n", rs.getInt("id"), rs.getString("nombre"), rs.getBigDecimal("salario").doubleValue()); } } } catch (SQLException e) { e.printStackTrace(); } } }
✅ Buena práctica Usa siempre Connection Pooling (HikariCP, c3p0, Apache DBCP) en aplicaciones en producción. Crear y cerrar una conexión JDBC tiene un coste elevado; un pool mantiene un conjunto de conexiones reutilizables, reduciendo drásticamente la latencia y el consumo de recursos tanto en la aplicación como en el SGBD.
🛡️

PreparedStatement vs Statement

Statement construye y envía la consulta SQL como texto plano. PreparedStatement precompila la consulta con marcadores de posición (?). Ventajas: previene SQL Injection, mejor rendimiento en consultas repetidas y manejo automático del escapado de tipos.

🔁

Batch Processing

JDBC permite agrupar múltiples operaciones en un lote (addBatch() / executeBatch()). En lugar de enviar 1000 INSERTs individuales, se envían en un solo mensaje de red, reduciendo la latencia en un orden de magnitud.

🔄

Tipos de driver

Type 4 (Pure Java) es el estándar actual: se descarga como un JAR, no requiere instalación nativa en el cliente y se comunica directamente con el protocolo del SGBD. Ejemplos: postgresql-42.x.jar, mysql-connector-java.jar.

♻️

Gestión de transacciones

Por defecto, JDBC opera en modo auto-commit (cada sentencia es una transacción). Para operaciones que deben ser atómicas, se desactiva con setAutoCommit(false) y se controla manualmente con commit() / rollback().

Java SE Driver Type 4 PreparedStatement ResultSet Connection Pool HikariCP SQL Injection Batch processing auto-commit
// 03 · Java Persistence API

JPA — API de Persistencia de Java

JPA (Java Persistence API) es la especificación estándar de Java EE / Jakarta EE para el mapeo objeto-relacional (ORM). Define cómo se mapean las clases Java a tablas de base de datos y cómo se gestionan las operaciones de persistencia (consulta, inserción, actualización, eliminación) de forma transparente, sin que el desarrollador tenga que escribir SQL directamente en la mayoría de los casos.

JPA introduce un nivel de abstracción superior al de JDBC. El desarrollador trabaja con objetos Java (Entidades) anotados con metadatos JPA, y el proveedor JPA (Hibernate, EclipseLink, OpenJPA) se encarga de generar y ejecutar el SQL necesario sobre la base de datos. Este enfoque se denomina ORM (Object-Relational Mapping).

El objeto central de JPA es el EntityManager: gestiona el ciclo de vida de las entidades (nuevo, gestionado, desacoplado, eliminado), sincroniza el estado en memoria con la base de datos y coordina las transacciones. Actúa como una caché de primer nivel: las entidades recuperadas se almacenan en el contexto de persistencia y, si se solicita la misma entidad dos veces en la misma transacción, JPA devuelve el objeto en memoria sin ir a la base de datos.

Para las consultas, JPA define JPQL (Java Persistence Query Language), un lenguaje similar a SQL pero orientado a objetos: se opera sobre entidades y sus propiedades, no sobre tablas y columnas. Esto permite que las consultas sean independientes del SGBD subyacente. Para consultas complejas también se puede usar la Criteria API (consultas programáticas tipadas) o SQL nativo cuando sea necesario.

Java · JPA / Hibernate // 1. ENTIDAD JPA — clase Java mapeada a tabla de base de datos @Entity @Table(name = "empleados") public class Empleado { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "nombre", nullable = false, length = 100) private String nombre; @Column(name = "salario", precision = 10, scale = 2) private BigDecimal salario; @ManyToOne // relación N:1 con Departamento @JoinColumn(name = "id_dpto") private Departamento departamento; // getters y setters... } // 2. OPERACIONES CRUD con EntityManager @Transactional public class EmpleadoService { @PersistenceContext private EntityManager em; // CREATE: persist guarda la entidad y la gestiona public void crear(Empleado e) { em.persist(e); } // READ: find usa la caché de 1er nivel public Empleado buscar(Long id) { return em.find(Empleado.class, id); } // UPDATE: merge sincroniza una entidad desacoplada public Empleado actualizar(Empleado e) { return em.merge(e); } // DELETE public void eliminar(Long id) { Empleado e = em.find(Empleado.class, id); em.remove(e); } // JPQL query — opera sobre la clase Java, no sobre la tabla public List<Empleado> porDepartamento(String nomDpto) { return em.createQuery( "SELECT e FROM Empleado e WHERE e.departamento.nombre = :dpto", Empleado.class) .setParameter("dpto", nomDpto) .getResultList(); } }
ODBC
Lenguaje: C / cualquiera
Nivel: Bajo (SQL directo)
Paradigma: Procedimental
SQL: Siempre explícito
ORM: No
Portabilidad: Driver en cliente
Uso típico: Herramientas BI, Excel
JDBC
Lenguaje: Java
Nivel: Medio (SQL directo)
Paradigma: OO básico
SQL: Siempre explícito
ORM: No
Portabilidad: JAR como dependencia
Uso típico: Acceso directo a BD
JPA
Lenguaje: Java / Jakarta EE
Nivel: Alto (abstracción ORM)
Paradigma: OO completo
SQL: Generado automáticamente
ORM: Sí (Hibernate, EclipseLink)
Portabilidad: Total entre SGBDs
Uso típico: Aplicaciones empresariales
⚠️ Atención — N+1 Problem El problema de las consultas N+1 es el fallo de rendimiento más frecuente en JPA. Si se cargan 100 pedidos y cada uno carga su cliente de forma perezosa (lazy loading), JPA genera 1 consulta para los pedidos + 100 consultas para los clientes = 101 consultas. La solución es usar @EntityGraph o cláusulas JOIN FETCH en JPQL para cargar las asociaciones en una única consulta.
ORM @Entity EntityManager JPQL Hibernate Criteria API Lazy / Eager loading N+1 problem @Transactional Jakarta EE
Parte II

Concurrencia y Mecanismos de Resolución

// 04 · Anomalías de concurrencia

Problemas de Concurrencia de Acceso

Cuando múltiples transacciones acceden y modifican los mismos datos de forma simultánea, pueden aparecer anomalías de concurrencia: situaciones en las que el resultado de las operaciones es incorrecto o inconsistente respecto a lo que se obtendría si las transacciones se ejecutasen de forma estrictamente secuencial. El estándar SQL define cuatro problemas clásicos.

🔴

Problema 1 · Lectura Sucia (Dirty Read)

Una transacción lee datos que han sido modificados por otra transacción que aún no ha confirmado (commit). Si la segunda transacción hace rollback, la primera habrá operado con datos que nunca existieron de forma definitiva.

Tiempo
Transacción A
Transacción B
t1
BEGIN
t2
UPDATE stock SET cant = 80
WHERE prod = 'X'
(era 100)
t3
BEGIN
t4
SELECT cant FROM stock
→ lee 80 (sin commit de A)
t5
ROLLBACK (error)
stock vuelve a 100
t6
B tomó decisiones basadas en 80,
pero el stock real es 100 → ERROR
Consecuencia: B ha leído un valor transitorio que nunca se consolidó. Puede llevar a decisiones de negocio incorrectas, por ejemplo, autorizar una venta basándose en un stock que en realidad nunca se actualizó.
🟠

Problema 2 · Lectura No Repetible (Non-Repeatable Read)

Una transacción lee el mismo registro dos veces y obtiene valores distintos, porque otra transacción lo modificó y confirmó entre las dos lecturas.

Tiempo
Transacción A
Transacción B
t1
SELECT precio WHERE id=5 → 100€
t2
UPDATE precios SET precio=120 WHERE id=5; COMMIT
t3
SELECT precio WHERE id=5 → 120€ (?)
Consecuencia: La misma consulta dentro de la misma transacción devuelve resultados diferentes. Afecta a informes y cálculos que asumen consistencia durante la transacción (p. ej., un informe contable que lee el mismo saldo dos veces obtendrá cifras distintas).
👻

Problema 3 · Lectura Fantasma (Phantom Read)

Una transacción ejecuta dos veces la misma consulta con condición de rango y obtiene filas adicionales (o menos filas) en la segunda ejecución, porque otra transacción insertó o eliminó filas que satisfacen esa condición.

Tiempo
Transacción A
Transacción B
t1
SELECT * WHERE edad > 30 → 5 filas
t2
INSERT empleado edad=35; COMMIT
t3
SELECT * WHERE edad > 30 → 6 filas (?)
Consecuencia: Las filas "fantasma" hacen que operaciones como COUNT o SUM produzcan resultados inconsistentes dentro de la misma transacción. Particularmente problemático en aplicaciones financieras donde se suman importes en una operación larga.
💥

Problema 4 · Actualización Perdida (Lost Update)

Dos transacciones leen el mismo valor, lo modifican de forma independiente y la segunda escritura sobreescribe la primera sin haber tenido en cuenta el cambio anterior. La actualización de la primera transacción se pierde.

Tiempo
Transacción A (descuento 10%)
Transacción B (descuento 20%)
t1
SELECT precio → lee 100
t2
SELECT precio → lee 100
t3
UPDATE SET precio = 90; COMMIT
t4
UPDATE SET precio = 80; COMMIT
(calculado sobre 100, no sobre 90)
t5
El descuento del 10% de A se perdió.
El precio final es 80, no 72.
Consecuencia: Es el problema de concurrencia más peligroso. Produce inconsistencias silenciosas: ninguna transacción falla, pero el resultado es incorrecto. Clásico en operaciones de actualización acumulativa (saldos bancarios, contadores de stock, likes de redes sociales).
Dirty Read Non-Repeatable Read Phantom Read Lost Update Transacciones concurrentes Anomalías ANSI SQL
// 05 · Control de concurrencia pesimista

Mecanismo de Bloqueos (Locking) — Concurrencia Pesimista

El control de concurrencia pesimista asume que los conflictos entre transacciones son frecuentes y los previene adquiriendo bloqueos antes de acceder a los datos. Una transacción que no puede obtener un bloqueo queda en espera hasta que la transacción que lo retiene lo libera.

El sistema de bloqueos es el mecanismo de control de concurrencia más antiguo y extendido en los SGBDs relacionales. Se basa en el principio de que una transacción debe declarar su intención sobre un recurso antes de acceder a él, y el gestor de bloqueos garantiza que los accesos incompatibles no se ejecuten simultáneamente.

Existen dos tipos fundamentales de bloqueo. El bloqueo compartido (S — Shared) se adquiere para operaciones de lectura: múltiples transacciones pueden tener bloqueos compartidos sobre el mismo recurso simultáneamente, ya que las lecturas no se interfieren entre sí. El bloqueo exclusivo (X — Exclusive) se adquiere para operaciones de escritura: solo una transacción puede tener un bloqueo exclusivo, y es incompatible con cualquier otro bloqueo (compartido o exclusivo) sobre el mismo recurso.

La granularidad del bloqueo es la decisión más importante en el diseño del control de concurrencia: se puede bloquear a nivel de base de datos completa, tabla, página de disco, fila individual o incluso celda. Mayor granularidad (fila) implica mayor concurrencia pero mayor overhead; menor granularidad (tabla) implica menor overhead pero más contención.

Tipo de bloqueo Lectura simultánea Escritura simultánea Cuándo se usa
🔵 Compartido (S) ✅ Permitida ❌ Bloqueada SELECT en modo serializable / LOCK IN SHARE MODE
🔴 Exclusivo (X) ❌ Bloqueada ❌ Bloqueada UPDATE, DELETE, INSERT; SELECT FOR UPDATE
🟡 Intención (IS/IX) A nivel tabla A nivel tabla Protocolo interno para bloqueos jerárquicos
🟣 Actualización (U) ✅ con bloqueos S ❌ con otros U o X Patrón READ-THEN-MODIFY para evitar deadlock

Protocolo de Bloqueo en Dos Fases (2PL — Two-Phase Locking): para garantizar la serializabilidad, una transacción debe seguir dos fases estrictas: (1) fase de crecimiento: solo puede adquirir bloqueos, nunca liberarlos; (2) fase de contracción: solo puede liberar bloqueos, nunca adquirir nuevos. El 2PL garantiza schedules serializables pero no previene los deadlocks.

🚨 Problema crítico — Deadlock (Interbloqueo) Un deadlock ocurre cuando dos o más transacciones esperan indefinidamente que la otra libere un bloqueo que necesitan. Ejemplo: T1 tiene bloqueada la fila A y espera la fila B; T2 tiene bloqueada la fila B y espera la fila A. El SGBD detecta el ciclo en el grafo de espera y mata a una de las transacciones (victim), que debe ser reiniciada por la aplicación. Prevención: adquirir bloqueos siempre en el mismo orden en todas las transacciones; usar timeouts; minimizar la duración de las transacciones.
SQL · Bloqueos explícitos -- Bloqueo exclusivo: ninguna otra transacción puede leer ni escribir BEGIN; SELECT saldo FROM cuentas WHERE id = 1001 FOR UPDATE; -- adquiere bloqueo X sobre la fila UPDATE cuentas SET saldo = saldo - 500 WHERE id = 1001; COMMIT; -- libera el bloqueo -- Bloqueo compartido: permite lecturas concurrentes, bloquea escrituras SELECT * FROM inventario WHERE categoria = 'ELECTRONICA' LOCK IN SHARE MODE; -- Timeout para evitar esperas indefinidas (PostgreSQL) SET lock_timeout = '5s';
Bloqueo compartido (S) Bloqueo exclusivo (X) 2PL Deadlock SELECT FOR UPDATE Granularidad Lock timeout Grafo de espera
// 06 · Control de concurrencia optimista

Concurrencia Optimista — MVCC y Control por Versiones

El control de concurrencia optimista asume que los conflictos entre transacciones son infrecuentes. En lugar de bloquear los recursos preventivamente, permite que las transacciones procedan sin bloqueos y verifica al final si ha habido conflicto. Si lo hay, la transacción que pierde debe reintentarse.

La implementación más extendida de la concurrencia optimista en los SGBDs modernos es MVCC (Multi-Version Concurrency Control). En lugar de bloquear las filas modificadas, el SGBD mantiene múltiples versiones de cada fila. Cuando una transacción modifica una fila, no sobreescribe la versión existente, sino que crea una versión nueva marcada con el timestamp o número de transacción. Las transacciones que leen ven la versión que era vigente en el momento en que comenzaron, independientemente de las modificaciones posteriores.

MVCC es el mecanismo de concurrencia de PostgreSQL, Oracle y MySQL/InnoDB. Sus ventajas son notables: las lecturas nunca bloquean las escrituras y viceversa, lo que elimina la contención de bloqueos en la mayoría de los escenarios y permite un rendimiento mucho mayor en cargas de trabajo de lectura intensiva.

En el contexto de aplicaciones Java/JPA, la concurrencia optimista se implementa con versioning: la entidad tiene un campo @Version que JPA incrementa en cada actualización. Antes de confirmar, JPA verifica que la versión en la base de datos coincide con la versión que se leyó. Si no coincide (otro cliente actualizó el registro mientras tanto), lanza una OptimisticLockException.

Java · JPA @Version (Concurrencia Optimista) @Entity public class Producto { @Id private Long id; private String nombre; private BigDecimal precio; private int stock; @Version // JPA gestiona este campo automáticamente private Long version; // se incrementa en cada UPDATE } // UPDATE generado por JPA incluye siempre la versión como condición: // UPDATE productos SET stock=?, version=version+1 // WHERE id=? AND version=? ← si version no coincide → 0 filas → excepción // Manejo de conflicto en la capa de servicio public void actualizarStock(Long idProducto, int cantidad) { int reintentos = 3; while (reintentos-- > 0) { try { Producto p = em.find(Producto.class, idProducto); p.setStock(p.getStock() - cantidad); em.flush(); // lanza OptimisticLockException si hubo conflicto return; } catch (OptimisticLockException e) { em.clear(); // descarta la caché, reintentará con datos frescos } } throw new RuntimeException("No se pudo actualizar tras 3 intentos"); }

MVCC — Ventajas

Lecturas sin bloqueo: los SELECT no compiten con los UPDATE. Alta concurrencia en aplicaciones de lectura intensiva. Sin deadlocks por bloqueos de lectura. Cada transacción ve una instantánea consistente de los datos en el momento de su inicio (snapshot isolation).

🗑️

MVCC — Vacuuming

El mantenimiento de múltiples versiones consume espacio en disco. Los SGBDs necesitan procesos de garbage collection periódicos para eliminar versiones antiguas que ya no son necesarias para ninguna transacción activa. En PostgreSQL este proceso se llama VACUUM.

🔁

Cuándo usar optimista

Adecuado cuando los conflictos son raros (baja contención), las transacciones son cortas, y el coste de reintentar es bajo. Ideal para aplicaciones web con muchos usuarios leyendo y pocas escrituras sobre los mismos registros simultáneamente.

🔒

Cuándo usar pesimista

Adecuado cuando los conflictos son frecuentes (alta contención), el coste de reintentar es alto (procesamiento intensivo), o se requiere una garantía inmediata de exclusividad (reservas de asientos, transferencias bancarias en tiempo real).

MVCC @Version JPA OptimisticLockException Snapshot isolation VACUUM PostgreSQL Versioning Retry pattern
// 07 · Niveles de aislamiento ANSI SQL

Niveles de Aislamiento — El Equilibrio entre Consistencia y Rendimiento

El estándar SQL-92 (ANSI) define cuatro niveles de aislamiento que permiten a los diseñadores de sistemas elegir el equilibrio deseado entre consistencia de los datos y rendimiento / concurrencia. A mayor nivel de aislamiento, mayor consistencia pero menor rendimiento (más bloqueos, menor throughput); a menor nivel, mayor rendimiento pero mayor riesgo de anomalías.

La elección del nivel de aislamiento es una decisión arquitectónica crítica que debe tomarse a nivel de transacción o de sesión, dependiendo de los requisitos de negocio de cada operación. No existe un nivel universalmente correcto: una operación de lectura informativa puede tolerrar un nivel bajo, mientras que una transferencia bancaria requiere el nivel máximo.

Nivel de aislamiento Dirty Read Non-Repeatable Read Phantom Read Rendimiento Cuándo usar
READ UNCOMMITTED ✗ Posible ✗ Posible ✗ Posible ⚡⚡⚡⚡ Máximo Estadísticas aproximadas; nunca en transacciones de negocio
READ COMMITTED
(predeterminado en Oracle, PostgreSQL, SQL Server)
✓ Prevenido ✗ Posible ✗ Posible ⚡⚡⚡ Alto La mayoría de aplicaciones web; buen equilibrio general
REPEATABLE READ
(predeterminado en MySQL InnoDB)
✓ Prevenido ✓ Prevenido ✗ Posible ⚡⚡ Medio Informes que leen los mismos datos varias veces
SERIALIZABLE ✓ Prevenido ✓ Prevenido ✓ Prevenido ⚡ Bajo Transacciones financieras críticas; máxima consistencia
📚 Para recordar — La regla mnemotécnica Los cuatro problemas y los cuatro niveles forman una matriz: cada nivel añade un problema más prevenido que el anterior. READ UNCOMMITTED no previene nada. READ COMMITTED previene los Dirty Reads (solo lee datos confirmados). REPEATABLE READ además previene las Lecturas No Repetibles (bloquea las filas leídas). SERIALIZABLE además previene los Fantasmas (bloquea los rangos enteros, como si las transacciones fuesen secuenciales).
SQL · Configuración del nivel de aislamiento -- A nivel de sesión (afecta a todas las transacciones de la conexión) SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; -- A nivel de transacción individual (recomendado — más control) BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ; SELECT SUM(importe) FROM pedidos WHERE mes = 6; -- Se garantiza que SUM será el mismo aunque se reejecutara COMMIT; -- En Spring / JPA @Transactional(isolation = Isolation.SERIALIZABLE) public void transferenciaFondos(Long origen, Long destino, BigDecimal importe) { // Operación crítica → máximo aislamiento } @Transactional(isolation = Isolation.READ_COMMITTED) public List<Pedido> listarPedidos() { // Consulta informativa → nivel por defecto es suficiente }
🏦

Caso: Banca

Las transferencias bancarias deben usar SERIALIZABLE. El riesgo de cualquier anomalía es inaceptable. El menor rendimiento se justifica por la criticidad de la operación.

🛒

Caso: E-commerce

READ COMMITTED para la mayoría de operaciones. REPEATABLE READ o bloqueo optimista para la reserva de stock. Balance entre consistencia y escalabilidad.

📊

Caso: Reporting

READ COMMITTED o incluso snapshots de solo lectura (réplica). Los informes analíticos toleran cierta inconsistencia temporal. El rendimiento es prioritario.

READ UNCOMMITTED READ COMMITTED REPEATABLE READ SERIALIZABLE ANSI SQL-92 @Transactional isolation Snapshot isolation Consistencia vs. rendimiento