моя контактная информация
Почтамезофия@protonmail.com
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
В среде одновременной работы базы данных несколько транзакций, пытающихся одновременно обновить одни и те же данные, могут вызвать конфликты. PostgreSQL предоставляет ряд механизмов для обработки этих конфликтов одновременных обновлений, чтобы обеспечить согласованность и целостность данных.
Конфликты одновременного обновления могут возникнуть, когда две или более транзакций пытаются одновременно изменить одну и ту же строку данных. Общие сценарии включают в себя:
PostgreSQL в основном использует MVCC (управление многоверсионным параллелизмом) для обработки параллельных транзакций. MVCC позволяет транзакции читать версию данных, которая соответствует требованиям уровня изоляции, без блокировки операций чтения других транзакций. Однако во время операций записи по-прежнему могут возникать конфликты.
PostgreSQL использует несколько типов блокировок для управления одновременным доступом к данным. К распространенным типам замков относятся:
Степень детализации блокировки может быть на уровне строки (Row-Level), на уровне страницы (Page-Level) и на уровне таблицы (Table-Level).
PostgreSQL поддерживает четыре уровня изоляции транзакций:
Простой подход заключается в том, чтобы при возникновении конфликта повторить транзакцию. Примеры следующие:
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;
$$;
В приведенном выше примере, если операция обновления не затрагивает ни одну строку (что указывает на возможный конфликт), устанавливается флаг, происходит некоторое время ожидания, а затем повторяется попытка.
Оптимистическое управление параллелизмом предполагает, что конфликты параллелизма возникают редко. В этом методе транзакция не блокируется при обновлении данных, а проверяет, были ли данные изменены другими транзакциями при фиксации. Если конфликтов нет, транзакция фиксируется успешно; если конфликты есть, транзакция откатывается и повторяется при необходимости.
-- 获取数据的初始版本
SELECT price AS original_price FROM products WHERE id = 1;
-- 进行业务处理和修改
UPDATE products SET price = 100 WHERE id = 1 AND price = original_price;
В приведенном выше примере операция обновления завершается успешно, только если данные не были изменены другими транзакциями.
Пессимистический контроль параллелизма предполагает вероятность возникновения конфликтов параллелизма и устанавливает необходимые блокировки во время выполнения транзакции, чтобы заблокировать другие потенциально конфликтующие транзакции.
BEGIN;
-- 获取排他锁
LOCK TABLE products IN SHARE ROW EXCLUSIVE MODE;
-- 进行数据更新
UPDATE products SET price = 100 WHERE id = 1;
COMMIT;
Добавьте в таблицу поле версии, чтобы отслеживать изменения данных.
CREATE TABLE products (
id SERIAL PRIMARY KEY,
price DECIMAL(10, 2),
version INT DEFAULT 0
);
При обновлении данных также увеличьте поле версии:
UPDATE products SET price = 100, version = version + 1 WHERE id = 1 AND version = <expected_version>;
Если количество строк, затронутых обновлением, равно 0, существует конфликт, поскольку ожидаемая версия не соответствует фактической версии.
Добавьте поле метки времени в каждую строку данных, чтобы записать время последнего изменения данных.
CREATE TABLE products (
id SERIAL PRIMARY KEY,
price DECIMAL(10, 2),
last_modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
При обновлении обновляйте данные только с отметкой времени, более старой, чем отметка времени, считанная текущей транзакцией:
UPDATE products SET price = 100 WHERE id = 1 AND last_modified <= <read_timestamp>;
Предположим, у нас есть система управления запасами для интернет-магазина с inventory
Таблица для хранения инвентарных количеств предметов.
CREATE TABLE inventory (
product_id INT PRIMARY KEY,
quantity INT,
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Теперь есть две одновременные транзакции:
Транзакция 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;
Транзакция 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;
Если эти две транзакции выполняются почти одновременно, может возникнуть конфликт.
Если мы используем разрешение конфликтов на основе временных меток:
T1
)。T2
)。Когда транзакция 1 пытается обновить данные, если ни одна другая транзакция не изменила данные с момента их чтения (т. last_updated <= T1
), обновление прошло успешно.
Когда транзакция 2 пытается обновиться, если она обнаруживает, что данные last_updated
больше, чемT2
(Указывая, что он был изменен после того, как транзакция 2 прочитала его), обновление завершается неудачей, и транзакция 2 может выбрать откат и попытку еще раз или выполнить другую обработку в соответствии с бизнес-логикой.
🎉相关推荐