2017-02-15 4 views
4

Ich habe gerade JSONB für PostgreSQL entdeckt und fragte mich, was könnte schief gehen, wenn ich es für alle Spalten meiner Tabellen verwendet?Verwenden Sie JSONB für Postgres-Spalten außer Primär- und Fremdschlüssel

Das heißt, alle meine Tabellen würden primäre und fremde Schlüssel als Spalten und eine field Spalte des Typs JSONB für andere Daten haben.

Abgesehen davon, dass ich wegen JSONBs Overhead zusätzlichen Platz einbüße und auf "Spalten" tippe, was würde ich vermissen?

Antwort

3

Es stellt sich heraus, Sie sind hier auf etwas.

Die wichtigsten Punkte der Verwendung einer relationalen Datenbank.

  • Gut definierte Beziehungen.
  • Ein gut definiertes und detailliertes Schema.
  • Hohe Leistung für große Datenmengen.

Sie erhalten die Beziehungen zu halten. Aber Sie verlieren das Schema und viel Leistung. Das Schema ist mehr als nur eine Datenvalidierung. Dies bedeutet, dass Sie keine Trigger oder Einschränkungen für einzelne Felder verwenden können.

In Bezug auf die Leistung ... werden Sie feststellen, dass die meisten Tests der JSONB-Leistung gegen andere ähnliche Datentypen sind. Sie sind nie gegen normale SQL-Tabellen. JSONB ist zwar erstaunlich effizient, aber nicht so effizient wie normales SQL. Lass es uns testen, es stellt sich heraus, dass du hier etwas hast.

Verwenden den Datensatz aus this JSONB performance presentation ich ein richtigen SQL-Schema erstellt ...

create table customers (
    id text primary key 
); 

create table products (
    id text primary key, 
    title text, 
    sales_rank integer, 
    "group" text, 
    category text, 
    subcategory text, 
    similar_ids text[] 
); 

create table reviews (
    customer_id text references customers(id), 
    product_id text references products(id), 
    "date" timestamp, 
    rating integer, 
    votes integer, 
    helpful_votes integer 
); 

Und eine, die für die Daten SQL Beziehungen aber JSONB verwendet ...

create table customers (
    id text primary key 
); 

create table products_jb (
    id text primary key, 
    fields jsonb 
); 

create table reviews_jb (
    customer_id text references customers(id), 
    product_id text references products_jb(id), 
    fields jsonb 
); 

Und ein einziger JSONB Tisch .

create table reviews_jsonb (
    review jsonb 
); 

Dann ich imported the same data into both sets of tables using a little script. 589859 Bewertungen, 93319 Produkte, 98761 Kunden.

Lassen Sie uns die gleiche Abfrage wie im JSONB-Performance-Artikel verwenden, um die durchschnittliche Bewertung für eine Produktkategorie zu erhalten. Erstens, ohne Indizes.

Traditionelle SQL: 138 ms

test=> select round(avg(r.rating), 2) 
from reviews r 
join products p on p.id = r.product_id 
where p.category = 'Home & Garden'; 
round 
------- 
    4.59 
(1 row) 

Time: 138.631 ms 

Voll JSONB: 380 ms

test=> select round(avg((review#>>'{review,rating}')::numeric),2) 
test-> from reviews_jsonb 
test-> where review #>>'{product,category}' = 'Home & Garden'; 
round 
------- 
    4.59 
(1 row) 

Time: 380.697 ms 

Hybrid JSONB: 190 ms

test=> select round(avg((r.fields#>>'{rating}')::numeric),2) 
from reviews_jb r 
join products_jb p on p.id = r.product_id 
where p.fields#>>'{category}' = 'Home & Garden'; 
round 
------- 
    4.59 
(1 row) 

Time: 192.333 ms 

die ehrlich besser ging, als es dachte. Der Hybrid-Ansatz ist doppelt so schnell wie voller JSONB, aber 50% langsamer als normaler SQL. Wie wäre es mit Indizes?

Traditionelle SQL: 130 ms (500 ms für den Index)

test=> create index products_category on products(category); 
CREATE INDEX 
Time: 491.969 ms 

test=> select round(avg(r.rating), 2) 
from reviews r 
join products p on p.id = r.product_id 
where p.category = 'Home & Garden'; 
round 
------- 
    4.59 
(1 row) 

Time: 128.212 ms 

Voll JSONB: 360 ms (+ 25000 ms für den Index)

test=> create index on reviews_jsonb using gin(review); 
CREATE INDEX 
Time: 25253.348 ms 
test=> select round(avg((review#>>'{review,rating}')::numeric),2) 
from reviews_jsonb 
where review #>>'{product,category}' = 'Home & Garden'; 
round 
------- 
    4.59 
(1 row) 

Time: 363.222 ms 

Hybrid JSONB: 185 ms (6900 ms für die Indizes)

test=> create index on products_jb using gin(fields); 
CREATE INDEX 
Time: 3654.894 ms 
test=> create index on reviews_jb using gin(fields); 
CREATE INDEX 
Time: 3237.534 ms 
test=> select round(avg((r.fields#>>'{rating}')::numeric),2) 
from reviews_jb r 
join products_jb p on p.id = r.product_id 
where p.fields#>>'{category}' = 'Home & Garden'; 
round 
------- 
    4.59 
(1 row) 

Time: 183.679 ms 

Es stellt sich heraus, das ist eine Abfrage Indizierung wird nicht viel Hilfe sein.

Das ist, was ich mit den Daten ein wenig zu spielen, Hybrid JSONB ist immer langsamer als Full SQL, aber schneller als Full JSONB. Es scheint ein guter Kompromiss zu sein. Sie können traditionelle Fremdschlüssel und Joins verwenden, haben aber die Flexibilität, beliebige Felder hinzuzufügen.

Ich empfehle den hybriden Ansatz einen Schritt weiter zu gehen: Verwenden Sie SQL-Spalten für die Felder, von denen Sie wissen, dass sie da sein werden, und eine JSONB-Spalte, um zusätzliche Felder für Flexibilität aufzunehmen.

Ich ermutige Sie, hier mit den Testdaten herumzuspielen und zu sehen, wie die Leistung ist.

Verwandte Themen