Gestión de transacciones en PostgreSQL

PostgreSQLBeginner
Practicar Ahora

Introducción

En este laboratorio explorará la gestión de transacciones en PostgreSQL, un aspecto fundamental para garantizar la integridad de los datos. Aprenderá a iniciar y confirmar transacciones para tratar una serie de operaciones como una única unidad de trabajo. También aprenderá a revertir transacciones fallidas, establecer niveles de aislamiento y simular bloqueos mediante actualizaciones concurrentes.

Iniciar y confirmar una transacción

En este paso aprenderá a iniciar y confirmar una transacción en PostgreSQL. Las transacciones garantizan la integridad de los datos al tratar una serie de operaciones como una única unidad de trabajo. Si falla alguna operación de la transacción, se revierte la transacción completa. Así se evitan las actualizaciones parciales y se mantiene la coherencia.

Primero, conéctese a la base de datos PostgreSQL como el usuario postgres. Abra un terminal y ejecute este comando:

sudo -u postgres psql

Debería ver el prompt postgres=#.

A continuación, cree una tabla llamada accounts para practicar las transacciones.

CREATE TABLE accounts (
    id SERIAL PRIMARY KEY,
    name VARCHAR(50),
    balance DECIMAL(10, 2)
);

Inserte algunos datos iniciales en la tabla accounts.

INSERT INTO accounts (name, balance) VALUES ('Alice', 100.00);
INSERT INTO accounts (name, balance) VALUES ('Bob', 50.00);

Ahora, inicie una transacción con el comando BEGIN.

BEGIN;

Dentro de la transacción, transfiera $20 de la cuenta de Alice a la de Bob.

UPDATE accounts SET balance = balance - 20.00 WHERE name = 'Alice';
UPDATE accounts SET balance = balance + 20.00 WHERE name = 'Bob';

Para hacer permanentes estos cambios, confirme la transacción con el comando COMMIT.

COMMIT;

Compruebe que la transacción se haya realizado correctamente consultando la tabla accounts.

SELECT * FROM accounts;

Debería ver que el saldo de Alice ha disminuido $20 y el de Bob ha aumentado $20.

Salida de ejemplo de una transacción en PostgreSQL

Por último, salga del shell de psql.

\q

Revertir una transacción fallida

En este paso aprenderá a revertir una transacción fallida en PostgreSQL. Revertir una transacción es fundamental cuando se produce un error durante una serie de operaciones en la base de datos, ya que permite mantener la base de datos en un estado coherente.

Debería seguir conectado a la base de datos PostgreSQL desde el paso anterior. Si no es así, vuelva a conectarse con este comando:

sudo -u postgres psql

Inicie una nueva transacción.

BEGIN;

Dentro de esta transacción, intentaremos ejecutar una operación que fallará intencionadamente. Vamos a insertar una clave primaria duplicada. Primero, averigüe cuál es el siguiente valor disponible para id.

SELECT MAX(id) FROM accounts;

Supongamos que el resultado es 2. Ahora, intente insertar una cuenta nueva con id = 1, que ya existe.

INSERT INTO accounts (id, name, balance) VALUES (1, 'Eve', 25.00);

Este comando generará el error ERROR: duplicate key value violates unique constraint "accounts_pkey".

Como se produjo un error dentro de la transacción, reviértala para descartar los cambios realizados. Use el comando ROLLBACK.

ROLLBACK;

Compruebe que ROLLBACK se haya ejecutado correctamente consultando la tabla accounts.

SELECT * FROM accounts;

Debería ver que la tabla todavía contiene únicamente las cuentas de Alice y Bob, con los saldos que tenían al final del paso 1. La operación INSERT fallida se revirtió correctamente.

Ejemplo de reversión de una transacción en PostgreSQL

Por último, salga del shell de psql.

\q

Establecer y probar los niveles de aislamiento

En este paso aprenderá qué son los niveles de aislamiento de las transacciones en PostgreSQL y cómo establecerlos y probarlos. Los niveles de aislamiento determinan hasta qué punto las transacciones concurrentes quedan aisladas unas de otras. Los niveles de aislamiento más altos protegen mejor contra la corrupción de datos, pero pueden reducir la concurrencia.

Debería seguir conectado a la base de datos PostgreSQL desde el paso anterior. Si no es así, vuelva a conectarse con este comando:

sudo -u postgres psql

Abra dos terminales distintos. En cada uno, conéctese a la base de datos PostgreSQL como el usuario postgres. Debería ver dos prompts postgres=#.

Terminal 1:

sudo -u postgres psql

Terminal 2:

sudo -u postgres psql

En Terminal 1, inicie una transacción y establezca su nivel de aislamiento en READ COMMITTED (el valor predeterminado). Debe ejecutar SET TRANSACTION dentro de la transacción y antes de su primera consulta.

BEGIN;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

En Terminal 1, consulte el saldo de Alice.

SELECT balance FROM accounts WHERE name = 'Alice';

Anote el saldo. Ahora, en Terminal 2, inicie una transacción y actualice el saldo de Alice.

BEGIN;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
UPDATE accounts SET balance = 90.00 WHERE name = 'Alice';
COMMIT;

En Terminal 1, vuelva a consultar el saldo de Alice.

SELECT balance FROM accounts WHERE name = 'Alice';

Como el nivel de aislamiento es READ COMMITTED, verá el saldo actualizado (90.00) que confirmó Terminal 2.

Saldo de Alice actualizado con READ COMMITTED

Ahora, pruebe el nivel de aislamiento REPEATABLE READ. En Terminal 1, revierta la transacción actual, inicie una nueva y establezca su nivel de aislamiento en REPEATABLE READ antes de leer ningún dato.

ROLLBACK;
BEGIN;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

En Terminal 1, vuelva a consultar el saldo de Alice.

SELECT balance FROM accounts WHERE name = 'Alice';

Anote el saldo. Ahora, en Terminal 2, inicie una transacción y vuelva a actualizar el saldo de Alice.

BEGIN;
UPDATE accounts SET balance = 100.00 WHERE name = 'Alice';
COMMIT;

En Terminal 1, vuelva a consultar el saldo de Alice.

SELECT balance FROM accounts WHERE name = 'Alice';

Como el nivel de aislamiento es REPEATABLE READ, seguirá viendo 90.00, el saldo de la instantánea establecida por la primera consulta de esta transacción, aunque Terminal 2 haya confirmado 100.00.

Por último, confirme la transacción en Terminal 1.

COMMIT;

Ahora, vuelva a consultar el saldo de Alice en Terminal 1. Como la transacción ya ha finalizado, verá el último valor confirmado (100.00).

SELECT balance FROM accounts WHERE name = 'Alice';

Salga de ambos shells de psql.

\q

Simular bloqueos con actualizaciones concurrentes

En este paso simulará bloqueos con actualizaciones concurrentes en PostgreSQL. Los bloqueos impiden que las transacciones concurrentes interfieran entre sí y ayudan a garantizar la integridad de los datos.

Debería seguir conectado a la base de datos PostgreSQL desde el paso anterior. Si no es así, vuelva a conectarse con este comando:

sudo -u postgres psql

Abra dos terminales distintos. En cada uno, conéctese a la base de datos PostgreSQL como el usuario postgres. Debería ver dos prompts postgres=#.

Terminal 1:

sudo -u postgres psql

Terminal 2:

sudo -u postgres psql

En Terminal 1, inicie una transacción y actualice el saldo de Alice. Es importante que use SELECT ... FOR UPDATE para bloquear la fila.

BEGIN;
SELECT balance FROM accounts WHERE name = 'Alice' FOR UPDATE;

Este comando muestra el saldo de Alice y bloquea la fila. Así impide que otras transacciones la modifiquen hasta que esta transacción se confirme o se revierta.

En Terminal 2, inicie una transacción e intente actualizar el saldo de Alice.

BEGIN;
UPDATE accounts SET balance = balance + 10 WHERE name = 'Alice';

Observe que el comando de Terminal 2 parecerá quedarse esperando. Esto ocurre porque está a la espera de que Terminal 1 libere el bloqueo.

Ahora, confirme la transacción en Terminal 1.

COMMIT;

Después de confirmar la transacción en Terminal 1, se ejecutará el comando UPDATE de Terminal 2.

En Terminal 2, confirme la transacción.

COMMIT;

Ahora, consulte la tabla accounts desde cualquiera de los dos terminales para comprobar los cambios.

SELECT * FROM accounts;

Debería ver que la transacción de Terminal 2 actualizó el saldo de Alice después de que Terminal 1 liberara el bloqueo.

Simulación de una actualización concurrente en PostgreSQL

Por último, salga de ambos shells de psql.

\q

Este ejemplo muestra cómo usar SELECT ... FOR UPDATE para simular bloqueos y evitar que las actualizaciones concurrentes interfieran entre sí. Sin el bloqueo, ambas transacciones podrían leer el mismo saldo inicial y aplicar sus actualizaciones basándose en ese valor, lo que provocaría una actualización perdida.

Resumen

En este laboratorio aprendió a gestionar transacciones en PostgreSQL. Primero, se conectó a la base de datos PostgreSQL con psql y creó una tabla de ejemplo accounts con datos iniciales.

A continuación, practicó el uso de transacciones. Aprendió a iniciar una transacción con el comando BEGIN, realizar varias operaciones en la base de datos (actualizar los saldos de Alice y Bob) y confirmar la transacción con el comando COMMIT para que los cambios fueran permanentes. Así pudo observar el principio fundamental de las transacciones: tratar una serie de operaciones como una única unidad atómica. También aprendió a revertir transacciones y establecer niveles de aislamiento. Por último, simuló bloqueos con actualizaciones concurrentes para comprender cómo evitar la corrupción de datos.