EAV mat JSONB an PostgreSQL ersetzen

TL; DR: JSONB kann d'Datebankschema Entwécklung immens vereinfachen ouni d'Queryleistung ofzeschafen.

Aféierung

Loosst eis e klassescht Beispill vu mĂ©iglecherweis ee vun den eelste BenotzungsfĂ€ll an der Welt vun enger relationaler Datebank (Datebank): mir hunn eng EntitĂ©it, a mir musse bestĂ«mmte Eegeschafte (Attributer) vun dĂ«ser EntitĂ©it spĂ€icheren. Awer net all Instanzen kĂ«nnen deeselwechte Set vun Eegeschaften hunn, a mĂ©i Eegeschafte kĂ«nnen an Zukunft bĂ€igefĂŒĂŒgt ginn.

Deen einfachste Wee fir dĂ«se Problem ze lĂ©isen ass eng Kolonn an der Datebanktabelle fir all ImmobiliewĂ€ert ze kreĂ©ieren an einfach dĂ©i auszefĂ«llen, dĂ©i fir eng spezifesch EntitĂ©itsinstanz gebraucht ginn. Super! Problem gelĂ©ist ... bis Ären DĂ«sch Millioune Rekorder enthĂ€lt an Dir musst en neie Rekord derbĂ€isetzen.

Bedenkt d'EAV Muster (Entitéit-Attributer-WÀert), geschitt et zimlech dacks. Eng Tabell enthÀlt Entitéiten (Rekord), eng aner Tabell enthÀlt Eegeschafte Nimm (Attributer), an eng drëtt Tabell assoziéiert Entitéite mat hiren Attributer an enthÀlt de WÀert vun dësen Attributer fir déi aktuell Entitéit. Dëst gëtt Iech d'Méiglechkeet verschidde SÀtze vun Eegeschafte fir verschidden Objeten ze hunn, an och Properties op der Flucht ze addéieren ouni d'Datebankstruktur z'Ànneren.

Wéi och ëmmer, ech géif dëse Post net schreiwen wann et net e puer Nodeeler vun der EVA Approche wier. Also, zum Beispill, fir eng oder méi Entitéiten ze kréien, déi all 1 Attribut hunn, sinn 2 BÀitrÀg an der Ufro erfuerderlech: déi éischt ass e BÀitrëtt mat der Attributtabell, déi zweet ass e BÀitrëtt mat der WÀertertabelle. Wann eng Entitéit 2 Attributer huet, da sinn 4 Joint néideg! ZousÀtzlech ginn all Attributer typesch als Saiten gespÀichert, wat zu Typcasting fir d'Resultat an d'WHERE Klausel resultéiert. Wann Dir vill Ufroen schreift, dann ass dëst zimmlech verschwendend wat d'Ressourceverbrauch ugeet.

Trotz dësen offensichtleche MÀngel ass EAV scho laang benotzt fir dës Zorte vu Probleemer ze léisen. Dëst waren inévitabel MÀngel, an et gouf einfach keng besser Alternativ.
Awer dunn ass eng nei "Technologie" am PostgreSQL opgetaucht ...

Ugefaange mat PostgreSQL 9.4, gouf den JSONB Datentyp bĂ€igefĂŒĂŒgt fir JSON binĂ€r Daten ze spĂ€icheren. Och wann d'SpĂ€ichere vun JSON an dĂ«sem Format typesch e bĂ«sse mĂ©i Plaz an ZĂ€it brauch wĂ©i Einfachen Text JSON, ass d'Operatiounen dorop vill mĂ©i sĂ©ier. JSONB Ă«nnerstĂ«tzt och IndexĂ©ierung, wat Ufroen nach mĂ©i sĂ©ier mĂ©cht.

D'JSONB Datentyp erlaabt eis dat ëmstÀndlech EAV Muster ze ersetzen andeems Dir just eng JSONB Kolonn un eis Entity Tabell bÀidréit, wat d'Datebankdesign staark vereinfacht. Awer vill streiden datt dëst vun enger Ofsenkung vun der Produktivitéit begleet soll ginn ... Dofir hunn ech dësen Artikel geschriwwen.

Ariichten vun enger Test Datebank

Fir dëse Verglach hunn ech d'Datebank op enger frëscher Installatioun vu PostgreSQL 9.5 op der $80 Build erstallt DigitalOcean Ubuntu 14.04 Nodeems ech e puer Parameteren an postgresql.conf konfiguréiert hat, hunn ech dat Skript mat psql. Déi folgend Tabelle goufen erstallt fir d'Donnéeën an EAV Form ze presentéieren:

CREATE TABLE entity ( 
  id           SERIAL PRIMARY KEY, 
  name         TEXT, 
  description  TEXT
);
CREATE TABLE entity_attribute (
  id          SERIAL PRIMARY KEY, 
  name        TEXT
);
CREATE TABLE entity_attribute_value (
  id                  SERIAL PRIMARY KEY, 
  entity_id           INT    REFERENCES entity(id), 
  entity_attribute_id INT    REFERENCES entity_attribute(id), 
  value               TEXT
);

Drënner ass eng Tabell wou déiselwecht Daten gespÀichert ginn, awer mat Attributer an enger JSONB Typ Kolonn - Eegeschafte.

CREATE TABLE entity_jsonb (
  id          SERIAL PRIMARY KEY, 
  name        TEXT, 
  description TEXT,
  properties  JSONB
);

GesĂ€it vill mĂ©i einfach aus, oder? Duerno gouf et an d'EntitĂ©itstabellen bĂ€igefĂŒĂŒgt (Entity- & entity_jsonb) 10 Milliounen Opzeechnungen, an deementspriechend war den DĂ«sch mat de selwechten DonnĂ©eĂ«n mat dem EAV Muster an der Approche mat enger JSONB Kolonn gefĂ«llt - entity_jsonb.properties. Also hu mir verschidde verschidden Datentypen Ă«nner der ganzer Set vun Eegeschafte kritt. Beispill Daten:

{
  id:          1
  name:        "Entity1"
  description: "Test entity no. 1"
  properties:  {
    color:        "red"
    lenght:       120
    width:        3.1882420
    hassomething: true
    country:      "Belgium"
  } 
}

Also elo hu mir déiselwecht Daten fir béid Optiounen. Loosst eis ufÀnken Implementatiounen op der Aarbecht ze verglÀichen!

Vereinfacht Ären Design

Et gouf virdru gesot datt den Datebankdesign immens vereinfacht gouf: eng Tabell, andeems Dir eng JSONB Kolonn fir Eegeschafte benotzt, anstatt drÀi Dëscher fir EAV ze benotzen. Awer wéi spigelt sech dat an Ufroen aus? D'Aktualiséierung vun enger Entitéitsimmobilie gesÀit esou aus:

-- EAV
UPDATE entity_attribute_value 
SET value = 'blue' 
WHERE entity_attribute_id = 1 
  AND entity_id = 120;

-- JSONB
UPDATE entity_jsonb 
SET properties = jsonb_set(properties, '{"color"}', '"blue"') 
WHERE id = 120;

Wéi Dir gesitt, gesÀit déi lescht Ufro net méi einfach aus. Fir de WÀert vun enger Immobilie an engem JSONB Objet ze aktualiséieren, musse mir d'Funktioun benotzen jsonb_set(), a soll eisen neie WÀert als JSONB Objet passéieren. Mir brauchen awer keen Identifizéierer am Viraus ze kennen. Wann Dir d'EAV Beispill kuckt, musse mir souwuel d'Entity_id wéi och d'Entity_attribute_id wëssen fir den Update auszeféieren. Wann Dir e Besëtz an enger JSONB Kolonn aktualiséieren wëllt op Basis vum Objektnumm, da gëtt alles an enger einfacher Zeil gemaach.

Loosst eis elo d'Entitéit auswielen, déi mir just op senger neier Faarf aktualiséiert hunn:

-- EAV
SELECT e.name 
FROM entity e 
  INNER JOIN entity_attribute_value eav ON e.id = eav.entity_id
  INNER JOIN entity_attribute ea ON eav.entity_attribute_id = ea.id
WHERE ea.name = 'color' AND eav.value = 'blue';

-- JSONB
SELECT name 
FROM entity_jsonb 
WHERE properties ->> 'color' = 'blue';

Ech mengen, mir kënnen d'accord sinn, datt deen zweete méi kuerz ass (kee join!), an dofir méi liesbar ass. JSONB gewënnt hei! Mir benotzen den JSON ->> Bedreiwer fir d'Faarf als TextwÀert vum JSONB Objet ze kréien. Et gëtt och en zweete Wee fir datselwecht Resultat am JSONB Modell z'erreechen mam @> Bedreiwer:

-- JSONB 
SELECT name 
FROM entity_jsonb 
WHERE properties @> '{"color": "blue"}';

Dëst ass e bësse méi komplizéiert: mir kucken ob den JSON Objet a senger Propertieskolonne en Objet enthÀlt deen riets vum @> Bedreiwer ass. Manner liesbar, méi produktiv (kuckt hei ënnen).

Loosst eis d'Benotzung vun JSONB nach méi einfach maachen wann Dir e puer Eegeschafte glÀichzÀiteg wielt. Dëst ass wou d'JSONB Approche wierklech erakënnt: mir wielt einfach Eegeschafte als zousÀtzlech Kolonnen an eisem Resultatset ouni de Besoin fir Join:

-- JSONB 
SELECT name
  , properties ->> 'color'
  , properties ->> 'country'
FROM entity_jsonb 
WHERE id = 120;

Mat EAV braucht Dir 2 Join fir all Immobilie déi Dir wëllt ufroen. Menger Meenung no weisen déi uewe genannte Ufroen eng grouss Vereinfachung am Datebankdesign. Kuckt méi Beispiller vu wéi een JSONB Ufroen schreift, och an dat posten.
Elo ass et ZÀit iwwer Leeschtung ze schwÀtzen.

Produktivitéit

Ze verglÀichen Leeschtung ech benotzt Analyse erklÀren bei Ufroen, fir d'AusféierungszÀit ze berechnen. All Ufro gouf op d'mannst drÀimol ausgefouert well de Ufroplaner déi éischte Kéier méi laang dauert. Als éischt hunn ech d'Ufroen ouni Index gemaach. Natierlech war dëst e Virdeel vu JSONB, well d'Joins erfuerderlech fir EAV konnten keng Indexen benotzen (auslÀnnesch Schlësselfelder goufen net indexéiert). Duerno hunn ech en Index op den 2 auslÀnnesche Schlësselkolonnen vun der EAV WÀerttabelle erstallt, souwéi en Index gin fir eng JSONB Kolonn.

D'Datenaktualiséierung huet déi folgend Resultater a punkto ZÀit gewisen (a ms). Notéiert datt d'Skala logarithmesch ass:

EAV mat JSONB an PostgreSQL ersetzen

Mir gesinn datt JSONB vill (> 50000-x) mĂ©i sĂ©ier ass wĂ©i EAV wann Dir keng Indexen benotzt, aus dem uewe genannte Grond. Wa mir Spalten mat primĂ€re SchlĂ«sselen indexĂ©ieren, verschwĂ«nnt den Ënnerscheed bal, awer JSONB ass Ă«mmer nach 1,3 Mol mĂ©i sĂ©ier wĂ©i EAV. NotĂ©iert datt den Index op der JSONB Kolonn keen Effekt hei huet well mir d'Eegeschaftskolonn net an den Evaluatiounskriterien benotzen.

Fir d'Auswiel vun Donnéeën baséiert op ImmobiliewÀert, kréie mir déi folgend Resultater (normal Skala):

EAV mat JSONB an PostgreSQL ersetzen

Dir kënnt bemierken datt JSONB erëm méi séier funktionnéiert wéi EAV ouni Indexen, awer wann EAV mat Indizes funktionnéiert et ëmmer méi séier wéi JSONB. Awer dunn hunn ech gesinn datt d'ZÀite fir JSONB Ufroen d'selwecht waren, dëst huet mech zur Tatsaach gefrot datt GIN Indexen net funktionnéieren. Anscheinend wann Dir e GIN Index op enger Kolonn mat populéierten Eegeschafte benotzt, wierkt et nëmme wann Dir den Inkludéierende Bedreiwer @> benotzt. Ech hunn dat an engem neien Test benotzt an et huet e groussen Impakt op d'ZÀit: nëmmen 0,153ms! Dëst ass 15000 Mol méi séier wéi EAV an 25000 Mol méi séier wéi den ->> Bedreiwer.

Ech mengen et war séier genuch!

Datebank Dësch Gréisst

Loosst eis d'Tabellgréissten fir béid Approche verglÀichen. An psql kënne mir d'Gréisst vun all Dëscher an Indexen mat dem Kommando weisen dti+

EAV mat JSONB an PostgreSQL ersetzen

Fir den EAV Approche sinn d'Tabelle Gréissten ongeféier 3068 MB an Indizes bis zu 3427 MB fir insgesamt 6,43 GB. D'JSONB Approche benotzt 1817 MB fir den Dësch an 318 MB fir d'Indexen, dat ass 2,08 GB. Et stellt sech 3 Mol manner eraus! Dës Tatsaach iwwerrascht mech e bëssen well mir Immobiliennimm an all JSONB Objet spÀicheren.

Awer trotzdem schwÀtzen d'Zuelen fir sech selwer: an EAV spÀichere mir 2 ganz Zuel auslÀnnesch Schlësselen pro AttributwÀert, wat zu 8 Bytes vun zousÀtzlech Donnéeën resultéiert. ZousÀtzlech spÀichert EAV all ImmobiliewÀerter als Text, wÀrend JSONB numeresch a boolesch WÀerter intern benotzt wa méiglech, wat zu engem méi klenge Foussofdrock resultéiert.

Resultater

Insgesamt denken ech datt d'EntitĂ©itseigenschaften am JSONB Format spĂ€icheren kann den Design an d'Erhalen vun Ärer Datebank vill mĂ©i einfach maachen. Wann Dir vill Ufroen laaft, da hĂ€lt alles an der selwechter Tabell wĂ©i d'EntitĂ©it tatsĂ€chlech mĂ©i effizient. An d'Tatsaach, datt dĂ«st d'Interaktioun tĂ«scht Daten vereinfacht ass schonn e Plus, awer dĂ©i resultĂ©ierend Datebank ass 3 Mol mĂ©i kleng am Volume.

Och op Basis vun den Tester déi gemaach goufen, kënne mir schléissen datt d'Leeschtungsverloschter ganz onbedeitend sinn. A verschiddene FÀll ass JSONB nach méi séier wéi EAV, wat et nach besser mécht. Wéi och ëmmer, dëse Benchmark deckt natierlech net all Aspekter (z.B. Entitéite mat enger ganz grousser Zuel vun Eegeschaften, eng bedeitend Erhéijung vun der Unzuel vun Eegeschafte vun existéierenden Donnéeën, ...), also wann Dir Suggestioune hutt fir se ze verbesseren , weg fillen gratis an de Kommentaren ze verloossen!

Source: will.com

Kaaft zouverlĂ€sseg Hosting fir Site mat DDoS Schutz, VPS VDS Server đŸ”„ Kaaft zouverlĂ©issegt WebsĂ€ithosting mat DDoS-Schutz, VPS VDS Server | ProHoster