Según tengo entendido, postgres usa dos campos adicionales, Xmin y Xmax, para implementar mvcc. Digamos que tenemos una tabla de empleados con columnas de identificación y nombre.
A continuación se muestran algunas operaciones crudas y cómo funcionan simultáneamente (considerando el nivel de aislamiento = READ_COMMITTED) y la pregunta es cuándo y dónde se adquiere el bloqueo real.
id | name | Xmin | Xmax ------------------------------ 1 | aa | 100 | nullActualizar con lectura simultánea -
un). Una nueva transacción comenzó a actualizar el nombre a "bb" (para id = 1). Al mismo tiempo, se inició otra transacción para leer los mismos datos.
b). Se crea una nueva tupla (objeto inmutable en postgres que representa una fila) con Xmin = id de transacción actual (digamos 200) y Xmax = nulo junto con id = 1, nombre = bb. Además, la versión anterior de id = 1 se actualiza para tener Xmax = 200. La transacción de lectura ve la versión anterior de los datos con Xmin = 100 y regresa. ¿Se requiere algún bloqueo en este caso? Creo que no, pero podría actualizar el Xmax de la tupla anterior.
A continuación se muestra el mismo registro con varias versiones (solo con fines explicativos) y la última versión tiene Xmax = nulo.
id | name | Xmin | Xmax ------------------------------ 1 | aa | 100 | 200 1 | bb | 200 | nullActualizar con actualización concurrente -
un). La transacción (con txn id = 300) comenzó a actualizar id = 1 a name = cc. Otra transacción (txn id = 400) comenzó a actualizar el mismo registro (id = 1) a nombre = dd. Si este escenario también procede de la misma manera creando una nueva tupla y marcando el Xmax de la tupla anterior, entonces creo que crearía problemas porque tanto 300 como 400 crearán una nueva tupla y marcarán el Xmax = txn de la tupla anterior. Una actualización podría perderse en este caso.
En este escenario, ¿el primer txn adquiere el bloqueo exclusivo y otros txn de actualización simultánea esperan hasta que se complete cualquier txn en curso o hay alguna otra forma en que postgres lo maneja?
Insertar -> Una nueva transacción inserta un nuevo registro que no es visible para otras transacciones hasta que se confirma, por lo que no hay problemas en este caso y no se requiere bloqueo ni control de versión.
Esto no es verdad. La tupla insertada se bloquea tras la inserción. Esto importa si, por ejemplo, hay una restricción única y alguien más intenta insertar una tupla en conflicto.
Actualizar con lectura concurrente... ¿Se requiere algún bloqueo en este caso?
Mientras se actualiza el xmax, hay un bloqueo de "peso ligero" en el búfer que contiene la tupla que se liberará tan pronto como se actualice el campo (no retenido durante la transacción). Este bloqueo liviano incluye una barrera para asegurarse de que cualquier otro proceso vea el cambio realizado, en lugar de ver una versión obsoleta en caché.
El lector verá, dependiendo de cuándo llegó allí, que xmax es 0 y devolverá la tupla, o verá que xmax es 200 y verá que 200 aún no está comprometido, y devolverá la tupla de todos modos porque conoce el bloqueo representado por xmax = 200 no se aplica a él, un mero lector.
Actualizar con actualización simultánea...
Ganará el primer proceso que escriba su id en xmax de la tupla a ser obsoleta. El segundo verá la identificación válida de otra persona en xmax y bloqueará hasta que esa otra transacción se confirme o revierta, luego decidirá qué hacer. Debido al bloqueo liviano en el búfer que contiene la tupla, ambos no pueden actualizar xmax sin notar los cambios de los demás.