2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
In einer Umgebung mit gleichzeitigem Datenbankbetrieb können mehrere Transaktionen, die gleichzeitig versuchen, dieselben Daten zu aktualisieren, zu Konflikten führen. PostgreSQL bietet eine Reihe von Mechanismen zur Behandlung dieser Konflikte bei gleichzeitigen Aktualisierungen, um die Datenkonsistenz und -integrität sicherzustellen.
Konflikte bei der gleichzeitigen Aktualisierung können auftreten, wenn zwei oder mehr Transaktionen gleichzeitig versuchen, dieselbe Datenzeile zu ändern. Zu den häufigsten Szenarien gehören:
PostgreSQL verwendet hauptsächlich MVCC (Multiversion Concurrency Control), um gleichzeitige Transaktionen abzuwickeln. MVCC ermöglicht es einer Transaktion, eine Datenversion zu lesen, die ihre Isolationsstufenanforderungen erfüllt, ohne die Lesevorgänge anderer Transaktionen zu blockieren, ohne sie zu sperren. Bei Schreibvorgängen kann es jedoch dennoch zu Konflikten kommen.
PostgreSQL verwendet mehrere Arten von Sperren, um den gleichzeitigen Zugriff auf Daten zu steuern. Zu den gängigen Schlosstypen gehören:
Die Sperrgranularität kann auf Zeilenebene (Row-Level), Seitenebene (Page-Level) und Tabellenebene (Table-Level) liegen.
PostgreSQL unterstützt vier Transaktionsisolationsstufen:
Ein einfacher Ansatz besteht darin, die Transaktion erneut zu versuchen, wenn ein Konflikt auftritt. Beispiele sind wie folgt:
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;
$$;
Wenn sich der Aktualisierungsvorgang im obigen Beispiel auf keine Zeilen auswirkt (was darauf hindeutet, dass möglicherweise ein Konflikt vorliegt), wird ein Flag gesetzt, eine Zeit lang gewartet und es dann erneut versucht.
Die optimistische Parallelitätskontrolle geht davon aus, dass Parallelitätskonflikte selten auftreten. Bei dieser Methode wird die Transaktion beim Aktualisieren der Daten nicht gesperrt, sondern beim Festschreiben geprüft, ob die Daten durch andere Transaktionen geändert wurden. Wenn es keine Konflikte gibt, wird die Transaktion erfolgreich festgeschrieben. Wenn es Konflikte gibt, wird die Transaktion zurückgesetzt und bei Bedarf erneut versucht.
-- 获取数据的初始版本
SELECT price AS original_price FROM products WHERE id = 1;
-- 进行业务处理和修改
UPDATE products SET price = 100 WHERE id = 1 AND price = original_price;
Im obigen Beispiel ist der Aktualisierungsvorgang nur dann erfolgreich, wenn die Daten nicht durch andere Transaktionen geändert wurden.
Die pessimistische Parallelitätskontrolle geht davon aus, dass Parallelitätskonflikte wahrscheinlich auftreten, und erwirbt während der Transaktionsausführung die erforderlichen Sperren, um andere potenziell widersprüchliche Transaktionen zu blockieren.
BEGIN;
-- 获取排他锁
LOCK TABLE products IN SHARE ROW EXCLUSIVE MODE;
-- 进行数据更新
UPDATE products SET price = 100 WHERE id = 1;
COMMIT;
Fügen Sie der Tabelle ein Versionsfeld hinzu, um Änderungen an den Daten zu verfolgen.
CREATE TABLE products (
id SERIAL PRIMARY KEY,
price DECIMAL(10, 2),
version INT DEFAULT 0
);
Erhöhen Sie beim Aktualisieren von Daten auch das Versionsfeld:
UPDATE products SET price = 100, version = version + 1 WHERE id = 1 AND version = <expected_version>;
Wenn die Anzahl der von der Aktualisierung betroffenen Zeilen 0 beträgt, liegt ein Konflikt vor, da die erwartete Version nicht mit der tatsächlichen Version übereinstimmt.
Fügen Sie jeder Datenzeile ein Zeitstempelfeld hinzu, um den Zeitpunkt der letzten Änderung der Daten aufzuzeichnen.
CREATE TABLE products (
id SERIAL PRIMARY KEY,
price DECIMAL(10, 2),
last_modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Aktualisieren Sie beim Aktualisieren nur Daten mit einem Zeitstempel, der älter ist als der von der aktuellen Transaktion gelesene Zeitstempel:
UPDATE products SET price = 100 WHERE id = 1 AND last_modified <= <read_timestamp>;
Angenommen, wir haben ein Bestandsverwaltungssystem für einen Online-Shop mit einem inventory
Tabelle zum Speichern der Lagerbestandsmengen von Artikeln.
CREATE TABLE inventory (
product_id INT PRIMARY KEY,
quantity INT,
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Es gibt jetzt zwei gleichzeitige Transaktionen:
Transaktion 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;
Transaktion 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;
Wenn diese beiden Transaktionen nahezu gleichzeitig ausgeführt werden, kann es zu einem Konflikt kommen.
Wenn wir eine zeitstempelbasierte Konfliktlösung verwenden:
T1
)。T2
)。Wenn Transaktion 1 eine Aktualisierung versucht und keine andere Transaktion die Daten geändert hat, seit sie sie gelesen hat (d. h. last_updated <= T1
), ist das Update erfolgreich.
Wenn Transaktion 2 versucht, die Daten zu aktualisieren, werden diese festgestellt last_updated
mehr als dieT2
(Dies zeigt an, dass es geändert wurde, nachdem Transaktion 2 es gelesen hat.) Die Aktualisierung schlägt fehl und Transaktion 2 kann ein Rollback durchführen und es erneut versuchen oder eine andere Verarbeitung entsprechend der Geschäftslogik durchführen.
🎉相关推荐