minhas informações de contato
Correspondência[email protected]
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Em um ambiente de operação simultânea de banco de dados, múltiplas transações que tentam atualizar os mesmos dados ao mesmo tempo podem causar conflitos. O PostgreSQL fornece uma série de mecanismos para lidar com esses conflitos de atualização simultâneos para garantir a consistência e integridade dos dados.
Podem ocorrer conflitos de atualização simultâneos quando duas ou mais transações tentam modificar a mesma linha de dados ao mesmo tempo. Os cenários comuns incluem:
O PostgreSQL usa principalmente MVCC (Controle de simultaneidade multiversão) para lidar com transações simultâneas. O MVCC permite que uma transação leia uma versão de dados que atenda aos seus requisitos de nível de isolamento sem bloquear para bloquear as operações de leitura de outras transações. No entanto, ainda podem ocorrer conflitos durante as operações de gravação.
O PostgreSQL usa vários tipos de bloqueios para controlar o acesso simultâneo aos dados. Os tipos de bloqueio comuns incluem:
A granularidade do bloqueio pode ser em nível de linha (nível de linha), nível de página (nível de página) e nível de tabela (nível de tabela).
PostgreSQL oferece suporte a quatro níveis de isolamento de transação:
Uma abordagem simples é fazer com que a transação seja repetida quando ocorrer um conflito. Os exemplos são os seguintes:
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;
$$;
No exemplo acima, se a operação de atualização não afetar nenhuma linha (indicando que pode haver um conflito), um sinalizador será definido, aguardará um período de tempo e tentará novamente.
O controle de simultaneidade otimista pressupõe que conflitos de simultaneidade raramente ocorrem. Neste método, a transação não é bloqueada ao atualizar os dados, mas verifica se os dados foram modificados por outras transações no momento do commit. Se não houver conflitos, a transação será confirmada com êxito; se houver conflitos, a transação será revertida e repetida conforme necessário;
-- 获取数据的初始版本
SELECT price AS original_price FROM products WHERE id = 1;
-- 进行业务处理和修改
UPDATE products SET price = 100 WHERE id = 1 AND price = original_price;
No exemplo acima, a operação de atualização só será bem-sucedida se os dados não tiverem sido modificados por outras transações.
O controle de simultaneidade pessimista assume que é provável que ocorram conflitos de simultaneidade e adquire os bloqueios necessários durante a execução da transação para bloquear outras transações potencialmente conflitantes.
BEGIN;
-- 获取排他锁
LOCK TABLE products IN SHARE ROW EXCLUSIVE MODE;
-- 进行数据更新
UPDATE products SET price = 100 WHERE id = 1;
COMMIT;
Adicione um campo de versão à tabela para rastrear alterações nos dados.
CREATE TABLE products (
id SERIAL PRIMARY KEY,
price DECIMAL(10, 2),
version INT DEFAULT 0
);
Ao atualizar os dados, incremente também o campo de versão:
UPDATE products SET price = 100, version = version + 1 WHERE id = 1 AND version = <expected_version>;
Se o número de linhas afetadas pela atualização for 0, há um conflito porque a versão esperada é inconsistente com a versão real.
Adicione um campo de carimbo de data/hora a cada linha de dados para registrar a hora da última modificação dos dados.
CREATE TABLE products (
id SERIAL PRIMARY KEY,
price DECIMAL(10, 2),
last_modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Ao atualizar, atualize apenas os dados com um carimbo de data/hora anterior ao carimbo de data/hora lido pela transação atual:
UPDATE products SET price = 100 WHERE id = 1 AND last_modified <= <read_timestamp>;
Suponha que temos um sistema de gerenciamento de estoque para uma loja online com um inventory
tabela para armazenar quantidades de itens em estoque.
CREATE TABLE inventory (
product_id INT PRIMARY KEY,
quantity INT,
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Existem agora duas transações simultâneas:
Transação 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;
Transação 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;
Se essas duas transações forem executadas quase ao mesmo tempo, poderá ocorrer um conflito.
Se usarmos resolução de conflitos baseada em carimbo de data/hora:
T1
)。T2
)。Quando a transação 1 tenta atualizar, se nenhuma outra transação modificou os dados desde que os leu (ou seja, last_updated <= T1
), a atualização foi bem-sucedida.
Quando a transação 2 tenta atualizar, se descobrir que os dados last_updated
mais do que oT2
(Indicando que foi modificado após a leitura da transação 2), a atualização falha e a transação 2 pode optar por reverter e tentar novamente ou realizar outro processamento de acordo com a lógica de negócios.
🎉相关推荐