Compartir tecnología

¿Cómo manejar la resolución de conflictos para actualizaciones simultáneas de datos en PostgreSQL?

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

hermosa línea divisoria

PostgreSQL


En un entorno de operación simultánea de bases de datos, varias transacciones que intentan actualizar los mismos datos al mismo tiempo pueden causar conflictos. PostgreSQL proporciona una serie de mecanismos para manejar estos conflictos de actualizaciones simultáneas para garantizar la coherencia e integridad de los datos.

hermosa línea divisoria

1. Escenarios de conflicto de actualizaciones simultáneas

Pueden ocurrir conflictos de actualización simultánea cuando dos o más transacciones intentan modificar la misma fila de datos al mismo tiempo. Los escenarios comunes incluyen:

  1. Modificar diferentes columnas de la misma fila al mismo tiempo
  2. Actualiza diferentes valores de la misma columna al mismo tiempo

hermosa línea divisoria

2. Mecanismo de control de concurrencia en PostgreSQL

PostgreSQL utiliza principalmente MVCC (Control de concurrencia multiversión) para manejar transacciones concurrentes. MVCC permite que una transacción lea una versión de datos que cumpla con sus requisitos de nivel de aislamiento sin bloquear para bloquear las operaciones de lectura de otras transacciones. Sin embargo, es posible que aún se produzcan conflictos durante las operaciones de escritura.

(1) Mecanismo de bloqueo

PostgreSQL utiliza varios tipos de bloqueos para controlar el acceso simultáneo a los datos. Los tipos de bloqueo comunes incluyen:

  1. Bloqueo compartido: permite que otras transacciones adquieran bloqueos compartidos, pero evita que se adquieran bloqueos exclusivos. Comúnmente utilizado para operaciones de lectura.
  2. Bloqueo exclusivo: evita que otras transacciones adquieran cualquier tipo de bloqueo, utilizado a menudo para operaciones de escritura.

La granularidad del bloqueo puede ser a nivel de fila (Nivel de fila), a nivel de página (Nivel de página) y a nivel de tabla (Nivel de tabla).

(2) Nivel de aislamiento de transacciones

PostgreSQL admite cuatro niveles de aislamiento de transacciones:

  1. Leer no comprometido: Este es el nivel de aislamiento más bajo. Una transacción puede leer modificaciones de datos no confirmadas de otras transacciones, lo que puede provocar problemas como lecturas sucias, lecturas no repetibles y lecturas fantasma.
  2. Leer comprometido: Las transacciones solo pueden leer los datos enviados, evitando lecturas sucias, pero aún pueden ocurrir lecturas no repetibles y lecturas fantasma.
  3. Lectura repetible: Leer los mismos datos varias veces dentro de una transacción obtendrá el mismo resultado, evitando lecturas no repetibles, pero pueden ocurrir lecturas fantasmas.
  4. Serializable: El nivel de aislamiento más alto, que garantiza la ejecución en serie de transacciones mediante un estricto control de concurrencia, evitando lecturas sucias, lecturas no repetibles y lecturas fantasma.

hermosa línea divisoria

3. Solución a conflictos de actualizaciones simultáneas

(1) Mecanismo de reintento

Un método sencillo es hacer que la transacción se vuelva a intentar cuando se produzca un conflicto. Los ejemplos son los siguientes:

DO
$$
DECLARE
    conflict_detected BOOLEAN := FALSE;
BEGIN
    LOOP
        -- 尝试执行更新操作
        UPDATE products SET price = 100 WHERE id = 1;

        -- 检查是否有冲突(例如,通过检查受影响的行数)
        IF NOT FOUND THEN
            conflict_detected := TRUE;
        ELSE
            EXIT;
        END IF;

        -- 若有冲突,等待一段时间并重试
        IF conflict_detected THEN
            PERFORM pg_sleep(1);
        END IF;
    END LOOP;
END;
$$;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

En el ejemplo anterior, si la operación de actualización no afecta a ninguna fila (lo que indica que puede haber un conflicto), se establece un indicador, se espera un período de tiempo y luego se vuelve a intentar.

(2) Utilice un control de concurrencia optimista

El control de concurrencia optimista supone que rara vez ocurren conflictos de concurrencia. En este método, la transacción no se bloquea al actualizar los datos, pero verifica si los datos han sido modificados por otras transacciones al confirmar. Si no hay conflictos, la transacción se confirma correctamente; si hay conflictos, la transacción se revierte y se vuelve a intentar según sea necesario.

-- 获取数据的初始版本
SELECT price AS original_price FROM products WHERE id = 1;

-- 进行业务处理和修改
UPDATE products SET price = 100 WHERE id = 1 AND price = original_price;
  • 1
  • 2
  • 3
  • 4
  • 5

En el ejemplo anterior, la operación de actualización tiene éxito sólo si los datos no han sido modificados por otras transacciones.

(3) Utilice control de concurrencia pesimista

El control de concurrencia pesimista supone que es probable que se produzcan conflictos de concurrencia y adquiere los bloqueos necesarios durante la ejecución de la transacción para bloquear otras transacciones potencialmente conflictivas.

BEGIN;

-- 获取排他锁
LOCK TABLE products IN SHARE ROW EXCLUSIVE MODE;

-- 进行数据更新
UPDATE products SET price = 100 WHERE id = 1;

COMMIT;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

(4) Campo de versión de la aplicación

Agregue un campo de versión a la tabla para realizar un seguimiento de los cambios en los datos.

CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    price DECIMAL(10, 2),
    version INT DEFAULT 0
);
  • 1
  • 2
  • 3
  • 4
  • 5

Al actualizar datos, incremente también el campo de versión:

UPDATE products SET price = 100, version = version + 1 WHERE id = 1 AND version = <expected_version>;
  • 1

Si el número de filas afectadas por la actualización es 0, existe un conflicto porque la versión esperada no coincide con la versión real.

(5) Resolución de conflictos basada en marca de tiempo

Agregue un campo de marca de tiempo a cada fila de datos para registrar la hora de la última modificación de los datos.

CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    price DECIMAL(10, 2),
    last_modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
  • 1
  • 2
  • 3
  • 4
  • 5

Al actualizar, actualice solo los datos con una marca de tiempo anterior a la marca de tiempo leída por la transacción actual:

UPDATE products SET price = 100 WHERE id = 1 AND last_modified <= <read_timestamp>;
  • 1

hermosa línea divisoria

4. Consideraciones en aplicaciones prácticas

(1) Impacto en el rendimiento

  1. Los diferentes métodos de resolución de conflictos tienen diferentes impactos en el rendimiento de la base de datos. Por ejemplo, el uso del bloqueo puede hacer que otras transacciones esperen, aumente el tiempo de bloqueo del sistema y, por lo tanto, afecte la concurrencia. El control de concurrencia optimista funciona mejor cuando los conflictos rara vez ocurren, pero cuando los conflictos ocurren con frecuencia, puede generar una gran cantidad de reintentos de transacción, lo que aumenta el tiempo de ejecución general.
  2. La aplicación de campos de versión o métodos basados ​​en marcas de tiempo puede requerir espacio de almacenamiento adicional para mantener la información de la versión o la marca de tiempo y realizar juicios y procesamientos adicionales al actualizar.

(2) Adaptabilidad de la lógica empresarial

  1. Ciertos escenarios comerciales pueden ser más adecuados para un método de resolución de conflictos específico. Por ejemplo, si la empresa tiene requisitos muy altos en cuanto a la coherencia de los datos y no puede tolerar ninguna inconsistencia, entonces el control de concurrencia pesimista o el nivel de aislamiento de serialización pueden ser una mejor opción.
  2. El control de concurrencia optimista puede ser más apropiado para escenarios donde los conflictos son menos frecuentes y los requisitos de tiempo de respuesta son mayores.

(3) Distribución de datos y patrones de acceso.

  1. Si el acceso a los datos es altamente concurrente y múltiples transacciones a menudo acceden a las mismas filas de datos al mismo tiempo, los métodos de resolución de conflictos deben elegirse con más cuidado para evitar bloqueos y conflictos excesivos.
  2. Para situaciones en las que la distribución de datos es relativamente uniforme y la probabilidad de conflicto es baja, se pueden utilizar métodos relativamente simples y eficientes, como el control de concurrencia optimista.

hermosa línea divisoria

5. Análisis de ejemplo

Supongamos que tenemos un sistema de gestión de inventario para una tienda online con un inventory tabla para almacenar cantidades de inventario de artículos.

CREATE TABLE inventory (
    product_id INT PRIMARY KEY,
    quantity INT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
  • 1
  • 2
  • 3
  • 4
  • 5

Ahora hay dos transacciones simultáneas:

Transacción 1:

BEGIN;
SELECT * FROM inventory WHERE product_id = 1;
-- 假设读取到的数量为 10
UPDATE inventory SET quantity = 5 WHERE product_id = 1 AND last_updated <= <read_timestamp>;
COMMIT;
  • 1
  • 2
  • 3
  • 4
  • 5

Transacción 2:

BEGIN;
SELECT * FROM inventory WHERE product_id = 1;
-- 假设也读取到的数量为 10
UPDATE inventory SET quantity = 8 WHERE product_id = 1 AND last_updated <= <read_timestamp>;
COMMIT;
  • 1
  • 2
  • 3
  • 4
  • 5

Si estas dos transacciones se ejecutan casi al mismo tiempo, puede ocurrir un conflicto.

Si utilizamos la resolución de conflictos basada en marcas de tiempo:

  1. La transacción 1 obtuvo la marca de tiempo actual al leer los datos (T1)。
  2. La transacción 2 obtuvo una marca de tiempo ligeramente posterior al leer los datos (T2)。

Cuando la transacción 1 intenta actualizar, si ninguna otra transacción ha modificado los datos desde que los leyó (es decir, last_updated <= T1), la actualización se realizó correctamente.

Cuando la transacción 2 intenta actualizar, si encuentra que los datos last_updated más que elT2(Lo que indica que se modificó después de que la transacción 2 lo leyó), la actualización falla y la transacción 2 puede optar por revertir e intentar nuevamente, o realizar otro procesamiento de acuerdo con la lógica empresarial.


hermosa línea divisoria

🎉相关推荐

PostgreSQL