2015-03-30 6 views
15

In PostgreSQL gibt es einen Sperrmechanismus namens advisory locking. Es bietet die folgenden API functions.Wie verwende ich String als Schlüssel zur PostgreSQL-Advisory-Sperre?

Die Funktion, die uns ermöglicht, eine solche Sperre zu erhalten, akzeptiert ein großes Integer-Argument: pg_advisory_lock(key bigint) oder zwei Integer-Schlüssel: pg_advisory_lock(key1 int, key2 int) (zweite Form).

Welcher Abstraktionsmechanismus kann verwendet werden, um String-Schlüssel anstelle von Ganzzahlen zu verwenden? Vielleicht können einige Hash-Funktionen die Aufgabe übernehmen?

Ist es möglich, dies nur in PostgreSQL zu implementieren, ohne die Zeichenfolge in Integer auf Anwendungsebene umwandeln zu müssen?

Wenn das gewünschte Ziel schwer zu erreichen ist, kann ich vielleicht zwei Integer verwenden, um die Zeile in der Tabelle zu identifizieren. Die zweite Ganzzahl könnte ein Primärschlüssel der Zeile sein, aber welche Ganzzahl kann ich als Tabellenkennung verwenden?

Antwort

13

Sie haben bereits den wahrscheinlichsten Kandidaten gefunden: Verwenden eines synthetischen Primärschlüssels einer Tabelle und einer Tabellenkennung als Schlüssel.

Sie können die Tabelle oid (Objekt-ID) von pg_class verwenden, um die Tabelle anzugeben. Die Bequemlichkeit, die zum Pseudotyp regclass umgewandelt wird, sucht dieses für Sie, oder Sie können select c.oid from pg_class c inner join pg_namespace n where n.nspname = 'public' and c.relname = 'mytable', um es by-schema zu erhalten.

Es gibt ein kleines Problem, weil oid intern ist ein unsigned 32-Bit-Integer, aber die beiden argument Form von pg_advisory_lock nimmt eine integer unterzeichnet. Dies ist wahrscheinlich kein Problem in der Praxis, da Sie ein Los von OIDs durchlaufen müssen, bevor das ein Problem ist.

z.B.

SELECT pg_advisory_lock('mytable'::regclass::integer, 42); 

Wenn Sie jedoch das zu tun, gehen, bist du im Grunde Reihe emuliert mit beratenden Schlössern verriegelt. Warum also nicht einfach die Zeilensperrung verwenden?

SELECT 1 
FROM mytable 
WHERE id = 42 
FOR UPDATE OF mytable; 

Nun, wenn Sie wirklich einen String-Schlüssel verwenden musste, wirst du akzeptieren müssen, dass es Kollisionen sein werde, weil Sie eine ziemlich kleine Hash verwenden werden.

PostgreSQL hat eingebaute Hash-Funktionen, die für Hash-Joins verwendet werden. Sie sind keine kryptografischen Hashes - sie sind so entworfen, dass sie schnell sind und ein ziemlich kleines Ergebnis liefern. Das brauchen Sie für diesen Zweck.

Sie Hash tatsächlich zu int4, und Sie würden wirklich int8 bevorzugen, so dass Sie ein noch höheres Risiko von Kollisionen laufen. Die Alternative ist, einen langsamen kryptografischen Hash wie md5 zu nehmen und ihn zu kürzen, und das ist nur hässlich.

Also, wenn Sie wirklich, wirklich das Gefühl, Sie müssen Sie so etwas wie tun könnte:

select pg_advisory_lock(hashtext('fredfred')); 

... aber nur, wenn Ihre Anwendung kann mit der Tatsache fertig werden, dass es unvermeidlich ist, dass andere Saiten die produzieren kann der gleiche Hash, sodass Sie eine Zeile als "gesperrt" sehen, die nicht wirklich gesperrt ist.

+0

Dieser Thread hat Details für 'hashtext()' - warum es nicht dokumentiert ist: http: //www.postgresql.org/message-id/flat/AB615962-28B4-4396-9270-AB56B08BCA8C @ justatheory.com # AB615962-28B4-4396-9270-AB56B08BCA8C @ justatheory.com Versionsunabhängige Funktion von Peter Eisentraut: https://github.com/peter/pgvihash –

+0

Danke für eine klare Erklärung! Ich habe bereits von Tisch OID selbst. Sieht so aus, als wäre es eine bestmögliche Lösung für dieses Problem. –

+0

@ErwinBrandstetter Guter Punkt. Es ist am wichtigsten, sie nicht für Dinge wie benutzerdefinierte Indizes zu verwenden, aber ja, es könnte einige BC-Auswirkungen geben. –

Verwandte Themen