2017-02-18 6 views
1

Fragenzusammenfassung

Dies ist eine Frage zur Serialisierbarkeit von Abfragen innerhalb einer SQL-Transaktion.Transaktionsisolation über mehrere Tabellen mit PostgreSQL MVCC

Insbesondere verwende ich PostgreSQL. Es kann davon ausgegangen werden, dass ich die aktuellste Version von PostgreSQL verwende. Von dem, was ich gelesen habe, glaube ich, dass die Technologie, die verwendet wird, um zu unterstützen, was ich versuche, als "MultiVersion Concurrency Control" oder "MVCC" bekannt ist.

Um es zusammenzufassen: Wenn ich eine primäre Tabelle und mehr als 1 mit Fremdschlüsseln verknüpfte Tabelle mit dieser primären Tabelle verbunden haben, wie garantiere ich, dass für einen bestimmten Schlüssel in den Tabellen und alle Anzahl von SELECT-Anweisungen, die diesen Schlüssel innerhalb einer Transaktion verwenden, von denen jede SELECT von einer der verknüpften Tabellen ist, erhalte ich Daten, wie sie zu dem Zeitpunkt existierten, als ich die Transaktion startete?

Andere Fragen

Diese Frage ist ähnlich, aber breiter, und die Frage und Antwort beziehen sich nicht speziell auf PostgreSQL: Transaction isolation and reading from multiple tables on SQL Server Express and SQL Server 2005

Beispiel

Sagen wir, ich habe 3 Tabellen:

bricks 
    brickworks (primary key) 
    completion_time (primary key) 
    has_been_sold 

brick_colors 
    brickworks (primary key, foreign key pointing to "bricks") 
    completion_time (primary key, foreign key pointing to "bricks") 
    quadrant (primary key) 
    color 

brick_weight 
    brickworks (primary key, foreign key pointing to "bricks") 
    completion_time (primary key, foreign key pointing to "bricks") 
    weight 

Eine Ziegelei produziert einen Stein auf einmal. Es erzeugt Ziegelsteine, die in jedem ihrer 4 Quadranten verschiedene Farben haben können.

Jemand analysiert später die Bausteine, um ihre Farbkombination zu bestimmen, und schreibt die Ergebnisse in die Tabelle brick_colors.

Jemand anders analysiert die Bausteine, um ihr Gewicht zu bestimmen, und schreibt die Ergebnisse in die Tabelle brick_weight.

Zu einem bestimmten Zeitpunkt kann ein vorhandener Baustein eine aufgezeichnete Farbe haben oder nicht und kann ein aufgezeichnetes Gewicht aufweisen oder nicht.


Eine Anwendung vorhanden ist, und diese Anwendung erhält Wort, dass jemand will (an dieser Stelle auf die Anwendung von seiner Ziegelei/completion_time zusammengesetzte Schlüssel bereits bekannt) einen bestimmten Ziegel kaufen.

Die Anwendung möchte alle bekannten Eigenschaften des Bausteins auswählen. DIE GENAUE ABFRAGE WIRD BEGINNT.

Wenn Farbe oder Gewicht Informationen MID-TRANSACTION hinzugefügt wird, will die Anwendung nicht darüber wissen.

Die Anwendung möchte SEPARATE QUERIES ausführen (kein SELECT mit mehreren JOINs zu den Tabellen mit Fremdschlüsselverknüpfungen, die aufgrund der Tabelle brick_colors möglicherweise mehrere Zeilen zurückgeben).


Dieses Beispiel ist absichtlich einfach; Der Wunsch, dies ohne einen SELECT mit mehreren JOINs zu tun, wäre klarer, wenn mein Beispiel beispielsweise 10 mit Fremdschlüssel verknüpfte Tabellen enthält und viele oder alle von ihnen mehrere Zeilen für denselben Primärschlüssel zurückgeben könnten (wie z. B. brick_colors in der Beispiel wie ich es oben habe).

Versuchte Lösung

Hier ist, was ich mit so weit habe kommen:

BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY ; 

-- All this statement accomplishes is telling the database what rows should be returned from the present point-in-time in future queries within the transaction 
SELECT DISTINCT true 
FROM bricks b 
LEFT JOIN brick_colors bc ON bc.brickworks = b.brickworks AND bc.completion_time = b.completion_time 
LEFT JOIN brick_weight bw ON bw.brickworks = b.brickworks AND bw.completion_time = b.completion_time 
WHERE b.brickworks = 'Brick-o-Matic' AND b.completion_time = '2017-02-01T07:35:00.000Z' ; 

SELECT * FROM brick_colors WHERE b.brickworks = 'Brick-o-Matic' AND b.completion_time = '2017-02-01T07:35:00.000Z' ; 
SELECT * FROM brick_weight WHERE b.brickworks = 'Brick-o-Matic' AND b.completion_time = '2017-02-01T07:35:00.000Z' ; 

COMMIT ; 

Es scheint nur verschwenderisch, dass zuerst verwenden SELECT mit der Serialisierung ausschließlich für Zwecke der Sicherstellung beitritt.

Gibt es eine andere Möglichkeit, dies zu tun?

Referenzen

PostgreSQL Concurrency Control

PostgreSQL Transcation Isolation

PostgreSQL SET TRANSACTION statement

Antwort

1

Dies ist die Essenz Ihrer Frage:

Wie garantiere ich, dass für ..... beliebig viele SELECT-Anweisungen ..... innerhalb einer Transaktion ....... Ich erhalte Daten , wie es bei die Zeit gab, die ich die Transaktion startete?


Das ist genau das, was Repeatable Read Isolation Level garantiert:

Die Isolierung Repeatable Read-Ebene nur Daten vor die Transaktion begann verpflichtet sieht; Es werden weder nicht festgeschriebene Daten noch Änderungen angezeigt, die während der Transaktionsausführung durch gleichzeitige Transaktionen ausgeführt wurden. (Die Abfrage sieht jedoch die Auswirkungen der vorherigen Updates innerhalb seiner eigenen Transaktion ausgeführt, obwohl sie noch Committed sind.) Dies ist eine stärkere Garantie als von der SQL-Standard für diese Isolationsstufe erforderlich ist, und verhindert alle Phänomene in Tabelle 13-1 beschrieben. Wie oben erwähnt, ist dies ausdrücklich erlaubt durch den Standard, der nur die minimalen Schutzvorrichtungen beschreibt, die jede Isolationsstufe bereitstellen muss.

Diese Ebene unterscheidet sich von Lesen in die eine Abfrage Committed in einer wiederholbare Lesetransaktion sieht eine Momentaufnahme ab dem Beginn der Transaktion nicht als der Anfang der aktuellen Abfrage innerhalb der Transaktion. Somit sehen aufeinanderfolgende SELECT-Befehle innerhalb einer einzigen Transaktion die gleichen Daten, d. H. Sie sehen keine Änderungen, die durch andere Transaktionen gemacht werden, die nach dem Start ihrer eigenen Transaktion festgeschrieben wurden.


Ein praktisches Beispiel - lassen Sie sagen, dass wir zwei einfache Tabellen haben:

CREATE TABLE t1(x int); 
INSERT INTO t1 VALUES (1),(2),(3); 
CREATE TABLE t2(y int); 
INSERT INTO t2 VALUES (1),(2),(3); 

Eine Reihe von Tabellen, deren Strukturen, Primärschlüssel, Fremdschlüssel usw. sind hier unwichtig.

Ermöglicht eine erste Sitzung zu öffnen, wiederholbare Leseisolationsstufe starten, und führen Sie zwei einfache und getrennte SELECT-Anweisungen:

test=# START TRANSACTION ISOLATION LEVEL REPEATABLE READ; 
START TRANSACTION 
test=# SELECT * FROM t1; 
x 
--- 
1 
2 
3 
(3 wiersze) 


test=# SELECT * FROM t2; 
y 
--- 
1 
2 
3 
(3 wiersze) 

Beachten Sie, dass START TRANSACTION Befehl deaktiviert automatisch Autocommitmodus in der Sitzung.


nun in einer anderen Sitzung (mit Standard Autocommitmodus aktiviert) ein paar Datensätze in t1 einfügen:

test2=# INSERT INTO t1 VALUES(10),(11); 

Neue Werte wurden inserded und automatisch verpflichtet (weil autocommit eingeschaltet ist).


nun zum ersten Sitzung zurück und laufen wieder SELECT: test = # select * from t1;

x 
--- 
1 
2 
3 
(3 wiersze) 

Wie Sie sehen, session1 (mit aktiver wiederholbarer Lesetransaktion) sieht keine nach dem Start der transation commited Änderungen.


Ermöglicht das gleiche Experiment Jota Tisch tun t2 - auf der zweiten Sitzung und Ausgabe gehen:

test2=# DELETE FROM t2 WHERE y = 2; 
DELETE 1 

nun zur ersten Sitzung zurück und führen Sie wieder SELECT:

test=# SELECT * FROM t2; 
y 
--- 
1 
2 
3 
(3 wiersze) 

Wie Sie sehen, sieht session1 (mit aktiver wiederholbarer Lese-Transaktion) keine Änderungen nach dem Start der Übertragung vor.


Und jetzt, in session1, beenden die Transaktion Ausgabe COMMIT, und dann SELECT:

test=# SELECT * FROM t1; 
x 
--- 
1 
2 
3 
(3 wiersze) 

test=# SELECT * FROM t2; 
y 
--- 
1 
2 
3 
(3 wiersze) 

test=# COMMIT; 
COMMIT 

test=# select * from t1; 
x 
---- 
    1 
    2 
    3 
10 
11 
(5 wierszy) 


test=# select * from t2; 
y 
--- 
1 
3 
(2 wiersze) 

Wie Sie sehen, wenn die wiederholbare Lesetransaktion gestartet und aktiv ist, können Sie viele sind getrennte Auswahl Anweisung, und alle diese Select-Anweisungen sehen die gleiche stabile Momentaufnahme der Daten wie beim Start der Transaktion, unabhängig von den in anderen Sitzungen übergebenen Daten.

+0

Danke! Ich habe festgestellt, dass ich in meiner Frage eine falsche Annahme gemacht habe. Ich denke, Sie haben völlig Recht, dass "Repeatable Read" der Weg ist, hier zu gehen. Ich stimme auch zu, dass die Anzahl der Tabellen und ihre Verknüpfung irrelevant ist - ich hielt sie nur aufgrund meiner schlechten Annahme für relevant. Ich werde eine weitere Frage stellen, zu der mich deine Antwort geführt hat. Danke nochmal für die ausführliche Antwort! – rvrabbit

+0

Neue Frage geschrieben! http://stackoverflow.com/questions/42322730/time-when-postgresql-isolation-level-takes-effect-seem-to-bea-after-first-select – rvrabbit

Verwandte Themen