26 Stimmen

Der beste Weg, eine PostgreSQL-Datenbank als einfachen Schlüssel-Wert-Speicher zu verwenden

Ich muss eine postgreSQL-Datenbank verwenden, die meine derzeitige Verwendung von berkeleyDB ersetzen wird. Mir ist zwar klar, dass dies keine ideale Situation ist, aber es liegt außerhalb meiner Kontrolle.

Die Frage ist also... Wenn Sie postgreSQL in einen Key-Value-Store umwandeln müssten, wie würden Sie vorgehen, um dies zu tun und es gleichzeitig so effizient wie möglich zu machen?

Meine Werte sind Byte-Arrays und meine Schlüssel sind Strings, ich könnte einige Einschränkungen für die Länge dieser Strings festlegen.

Ich nehme an, ich sollte einen Blob für meinen Wert und Primärschlüssel Spalte halten den Schlüssel verwenden, aber wie ich gerade in dieser Reise bin ich neugierig, wenn jemand in der Stapelüberlauf-Community dies getan hat, oder wenn es irgendwelche spezifischen "gotchas" ich sollte aufpassen.

32voto

Stradas Punkte 1678

Die Erweiterung in Postgresql, mit der dies möglich ist, heißt hstore. Sie funktioniert ähnlich wie bei anderen Key-Value-Store-Systemen. Laden Sie einfach die Erweiterung. Die Syntax ist einzigartig, aber wenn Sie schon einmal Redis oder Mongo benutzt haben, werden Sie es schnell verstehen. Machen Sie es nicht schwieriger als es ist. Ich verstehe, dass wir uns unsere Werkzeuge oft nicht aussuchen können und uns damit begnügen müssen.
Hier ist die Dokumentenseite:

http://www.postgresql.org/docs/9.1/static/hstore.html

8voto

Lukasz Madon Punkte 14194

Eine andere Möglichkeit ist die Verwendung von JSON oder JSONB mit einem eindeutigen Hash-Index für den Schlüssel.

CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

CREATE TABLE key_values (
    key uuid DEFAULT uuid_generate_v4(),
    value jsonb
);

CREATE INDEX idx_key_values ON key_values USING hash (key);

Einige Rückfragen

SELECT * FROM key_values WHERE key = '1cfc4dbf-a1b9-46b3-8c15-a03f51dde891';
Time: 0.514 ms
postgres=# SELECT * FROM key_values WHERE key = '1cfc4dbf-a1b9-46b3-8c15-a03f51dde890';
Time: 1.747 ms

postgres=# do $$
begin
for r in 1..1000 loop
INSERT INTO key_values (value)
VALUES ('{"somelarge_json": "bla"}');
end loop;
end;
$$;
DO
Time: 58.327 ms

Sie können keine effizienten Bereichsabfragen wie mit B-Tree durchführen, aber es sollte eine bessere Lese-/Schreibleistung haben. Der Index sollte etwa 60 % kleiner sein.

4voto

Jiri Klouda Punkte 1352

Wenn Sie gezwungen sind, eine relationale Datenbank zu verwenden, würde ich vorschlagen, dass Sie versuchen, eine Struktur in Ihren Daten zu finden, um die Vorteile dieser Tatsache zu nutzen, da Sie auf den Geschwindigkeitsvorteil verzichten, den Sie mit unstrukturierten Daten und Key-Value-Speichern haben. Je mehr Struktur Sie finden, desto größer ist der Vorteil, den Sie aus Ihrer misslichen Lage ziehen. Selbst wenn Sie die Struktur nur in den Schlüsseln finden.

Überlegen Sie auch, ob Sie nur sequentiellen oder zufälligen Zugriff auf Ihre Daten benötigen und in welchem Verhältnis und strukturieren Sie Ihre Datenbank nach dieser Anforderung. Werden Sie zum Beispiel Abfragen auf Ihre Werte nach Typ durchführen? Jede dieser Fragen kann Auswirkungen darauf haben, wie Sie Ihre Datenbank strukturieren.

Eine spezielle Überlegung zu Blobs in Postgresql: Sie werden intern als pg_largetable dargestellt (loid:oid,pageno:int4,data:bytea). Die Größe der Chunks ist durch LOBBLKSIZE definiert, aber typischerweise 2k. Wenn Sie also Byte-Arrays in Ihrer Tabelle anstelle von Blobs verwenden können und die Größe Ihres Wert/Schlüssel-Paares unter der Blockgröße begrenzen, können Sie diese Umleitung durch eine zweite Tabelle vermeiden. Sie könnten auch die Blockgröße erhöhen, wenn Sie Zugang zur Konfiguration der Datenbank haben.

Ich würde vorschlagen, nach Strukturen in den Daten und Mustern im Datenzugriff zu suchen und dann Ihre Frage mit mehr Details erneut zu stellen.

0voto

Brian Agnew Punkte 260470

Was müssen Sie als Wert speichern? Zeichenketten ? Ints ? Objekte (z. B. serialisierte Java-Objekte). Eine einfache Implementierung würde mit einer 3-Spalten-Tabelle funktionieren, die wie folgt aussieht:

NAME(VARCHAR)   TYPE(VARCHAR)   VALUE(VARCHAR)

(vielleicht ist der TYP eine Aufzählung). Die obige Methode würde jedoch nicht für binäre Daten wie serialisierte Objekte funktionieren, und vielleicht brauchen Sie hier ein BLOB.

Alternativ dazu (und wahrscheinlich ein viel bessere Idee), haben Sie gesehen Apache Commons Konfiguration ? Sie können das mit einer Datenbank unterstützen (über JDBC) und Sie können Eigenschaften so speichern, dass Sie sie auf diese Weise abrufen können:

// get a property called 'number'
Double double = config.getDouble("number");
Integer integer = config.getInteger("number");

Das kann Ihnen eine Menge Ärger bei der Umsetzung ersparen. Sie Mai haben ein Problem mit dem Speichern von Binärdaten, da man sie vor dem Einfügen und nach dem Abrufen serialisieren muss. Aber ich habe dies in der Vergangenheit für die Speicherung von Ints, Doubles und serialisierte Java-Objekte über XStream verwendet, so kann ich bestätigen, es funktioniert gut.

0voto

cyberconte Punkte 2293

Es sollte wirklich davon abhängen, was der Schlüssel sein wird. Wenn es sich immer um eine Zeichenkette mit weniger als 255 Zeichen handelt, verwenden Sie einen Varchar als PK und dann einen Blob (unter der Annahme eines großen Wertes) für den Wert. Wenn es sich immer um eine Zahl handelt, verwenden Sie int usw.

Mit anderen Worten, ich brauche mehr Informationen, um Ihnen eine gute Antwort geben zu können :)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X