Memorizzazione di JSON nel database anziché avere una nuova colonna per ogni chiave


215

Sto implementando il seguente modello per l'archiviazione dei dati relativi all'utente nella mia tabella - Ho 2 colonne - uid(chiave primaria) e una metacolonna che memorizza altri dati sull'utente in formato JSON.

 uid   | meta
--------------------------------------------------
 1     | {name:['foo'], 
       |  emailid:['foo@bar.com','bar@foo.com']}
--------------------------------------------------
 2     | {name:['sann'], 
       |  emailid:['sann@bar.com','sann@foo.com']}
--------------------------------------------------

È questo un modo migliore (performance, disegno-saggio) rispetto al modello una colonna-per-proprietà, dove il tavolo avrà molte colonne come uid, name, emailid.

Quello che mi piace del primo modello è che puoi aggiungere il maggior numero di campi possibile senza limiti.

Inoltre, mi chiedevo, ora che ho implementato il primo modello. Come posso eseguire una query su di esso, ad esempio, voglio recuperare tutti gli utenti che hanno un nome come "pippo"?

Domanda : qual è il modo migliore per archiviare i dati relativi all'utente (tenendo presente che il numero di campi non è fisso) nel database usando - JSON o colonna per campo? Inoltre, se il primo modello è implementato, come interrogare il database come descritto sopra? Dovrei usare entrambi i modelli, memorizzando tutti i dati che possono essere cercati da una query in una riga separata e gli altri dati in JSON (è una riga diversa)?


Aggiornare

Dal momento che non ci saranno troppe colonne su cui ho bisogno di eseguire la ricerca, è saggio usare entrambi i modelli? Chiave per colonna per i dati che devo cercare e JSON per altri (nello stesso database MySQL)?


40
ottima domanda! ma perché non hai accettato una risposta? che aiuterebbe altri utenti (come me)
Sahar Ch.

Risposte:


200

Aggiornato il 4 giugno 2017

Dato che questa domanda / risposta ha guadagnato una certa popolarità, ho pensato che valesse la pena aggiornarlo.

Quando questa domanda era stata originariamente pubblicata, MySQL non aveva supporto per i tipi di dati JSON e il supporto in PostgreSQL era agli inizi. Da 5.7, MySQL ora supporta un tipo di dati JSON (in un formato di archiviazione binario) e PostgreSQL JSONB è maturato in modo significativo. Entrambi i prodotti forniscono tipi JSON performanti che possono archiviare documenti arbitrari, incluso il supporto per l'indicizzazione di chiavi specifiche dell'oggetto JSON.

Tuttavia, rimango comunque fedele alla mia affermazione originale secondo cui la preferenza predefinita, quando si utilizza un database relazionale, dovrebbe essere comunque colonna per valore. I database relazionali sono ancora basati sul presupposto che i dati al loro interno saranno abbastanza ben normalizzati. Il pianificatore di query offre informazioni di ottimizzazione migliori quando si esaminano le colonne rispetto alle chiavi in ​​un documento JSON. Le chiavi esterne possono essere create tra le colonne (ma non tra le chiavi nei documenti JSON). Cosa importante: se la maggior parte del tuo schema è abbastanza volatile da giustificare l'utilizzo di JSON, potresti voler considerare almeno se un database relazionale è la scelta giusta.

Detto questo, poche applicazioni sono perfettamente relazionali o orientate ai documenti. La maggior parte delle applicazioni ha un mix di entrambi. Ecco alcuni esempi in cui ho trovato personalmente JSON utile in un database relazionale:

  • Quando si memorizzano indirizzi e-mail e numeri di telefono per un contatto, dove archiviarli come valori in un array JSON è molto più facile da gestire rispetto a più tabelle separate

  • Salvataggio di preferenze utente chiave / valore arbitrarie (in cui il valore può essere booleano, testuale o numerico e non si desidera avere colonne separate per diversi tipi di dati)

  • Memorizzazione di dati di configurazione che non hanno uno schema definito (se si sta creando Zapier o IFTTT e è necessario archiviare i dati di configurazione per ogni integrazione)

Sono sicuro che ce ne sono anche altri, ma questi sono solo alcuni brevi esempi.

Risposta originale

Se vuoi davvero essere in grado di aggiungere tutti i campi che vuoi senza alcuna limitazione (a parte un limite di dimensione del documento arbitrario), considera una soluzione NoSQL come MongoDB.

Per database relazionali: utilizzare una colonna per valore. Mettere un BLOB JSON in una colonna rende praticamente impossibile eseguire una query (e dolorosamente lento quando si trova effettivamente una query che funziona).

I database relazionali sfruttano i tipi di dati durante l'indicizzazione e devono essere implementati con una struttura normalizzata .

Come nota a margine: questo non vuol dire che non si dovrebbe mai archiviare JSON in un database relazionale. Se stai aggiungendo veri metadati o se il tuo JSON sta descrivendo informazioni che non richiedono query e che vengono utilizzate solo per la visualizzazione, potrebbe essere eccessivo creare una colonna separata per tutti i punti dati.


1
Dal momento che non ci saranno troppe colonne su cui ho bisogno di eseguire la ricerca, è saggio usare entrambi i modelli? Chiave per colonna per i dati che devo cercare e JSON per altri (nello stesso database MySQL)?
ShuklaSannidhya

3
@Sann Dovresti usare una colonna per valore per i dati che vuoi leggere o interrogare spesso. Inserire il nome di qualcuno in JSON non ha senso perché, anche se non è probabile che si esegua una query in base a esso, è probabile che sia necessario molto spesso. Si tratta di una decodifica dispendiosa sul lato applicazione. A meno che tu non senta davvero che i tuoi dati siano meglio rappresentati come JSON (e fidati di me, probabilmente non lo è), non dovresti ricorrere a quello.
Colin M,

5
" virtually impossible to query" - oggi psql ti permette di cercare e indicizzare il suo jsonb
ted

1
@ted true. Tuttavia, al momento della stesura di questa risposta che non era realmente disponibile. Inoltre, questa domanda fa riferimento a MySQL in cui la capacità non è presente.
Colin M,

3
@ColinM, sì, mi rendo conto che il mio commento è di 3 anni più giovane del tuo post. Il motivo per cui l'ho lasciato è perché può essere utile e cambiare le decisioni per gli altri. Per quanto riguarda il riferimento a MySQL: potrebbe essere vero, ma hai "For relational databases"nella tua risposta = P
ted

69

Come la maggior parte delle cose "dipende". Non è giusto o sbagliato / buono o cattivo in sé e per sé archiviare i dati in colonne o JSON. Dipende da cosa devi fare in seguito. Qual è il tuo modo previsto di accedere a questi dati? Dovrai fare riferimenti incrociati ad altri dati?

Altre persone hanno risposto abbastanza bene quali sono i compromessi tecnici.

Non molte persone hanno discusso del fatto che l'app e le funzionalità si evolvono nel tempo e in che modo questa decisione sull'archiviazione dei dati influisce sul team.

Poiché una delle tentazioni dell'utilizzo di JSON è evitare la migrazione dello schema e quindi se il team non è disciplinato, è molto semplice inserire un'altra coppia chiave / valore in un campo JSON. Non c'è migrazione per questo, nessuno ricorda a cosa serve. Non esiste alcuna convalida al riguardo.

Il mio team ha usato JSON lungo le colonne tradizionali laterali in postgres e all'inizio è stata la cosa migliore da quando è stato tagliato il pane. JSON era attraente e potente, fino a quando un giorno ci siamo resi conto che la flessibilità aveva un costo ed è improvvisamente un vero punto dolente. A volte quel punto si insinua molto rapidamente e poi diventa difficile cambiare perché abbiamo costruito molte altre cose in cima a questa decisione di progettazione.

Gli straordinari, l'aggiunta di nuove funzionalità, la presenza dei dati in JSON ha portato a query dall'aspetto più complicato rispetto a ciò che sarebbe stato aggiunto se fossimo rimasti alle colonne tradizionali. Quindi abbiamo iniziato a pescare alcuni valori chiave in colonne in modo da poter creare join e fare confronti tra valori. Cattiva idea. Ora abbiamo avuto la duplicazione. Un nuovo sviluppatore verrebbe a bordo e sarebbe confuso? Qual è il valore che dovrei salvare di nuovo? Quello JSON o la colonna?

I campi JSON sono diventati cassetti spazzatura per piccoli pezzi di questo e quello. Nessuna convalida dei dati a livello di database, nessuna coerenza o integrità tra i documenti. Ciò ha spinto tutta questa responsabilità nell'app invece di ottenere il controllo di tipo e vincolo da colonne tradizionali.

Guardando indietro, JSON ci ha permesso di iterare molto rapidamente e ottenere qualcosa fuori dalla porta. È stato fantastico Tuttavia, dopo aver raggiunto una determinata dimensione del team, la flessibilità ci ha permesso di impiccarci con una lunga serie di debiti tecnici che hanno quindi rallentato i successivi progressi nell'evoluzione delle caratteristiche. Usare con cautela.

Pensa a lungo e intensamente alla natura dei tuoi dati. È il fondamento della tua app. Come verranno utilizzati i dati nel tempo. E come è possibile CAMBIARE?


7
"la sua flessibilità ci ha anche permesso di impiccarci con una lunga corda di debito tecnico" metafora molto bella!
Antoine Gallix,

Dopo molti anni di sviluppo e di lavoro con persone diverse, se dovessi scrivere su questo argomento, scriverò la stessa cosa. Ci sono così tanti sviluppatori ora, in cui molti di loro, anche se con anni di esperienza, non aumentano di livello. Dobbiamo mantenere tutto semplice e per me le 2 cose che dobbiamo sempre considerare che possono "strutturare" il successo è la scalabilità e la manutenibilità del codice.
JohnnyJaxs,

27

Basta lanciarlo là fuori, ma WordPress ha una struttura per questo tipo di cose (almeno WordPress è stato il primo posto in cui l'ho osservato, probabilmente è nato altrove).

Consente chiavi illimitate ed è più veloce da cercare rispetto all'utilizzo di un BLOB JSON, ma non così veloce come alcune soluzioni NoSQL.

uid   |   meta_key    |   meta_val
----------------------------------
1         name            Frank
1         age             12
2         name            Jeremiah
3         fav_food        pizza
.................

MODIFICARE

Per memorizzare la cronologia / più chiavi

uid   | meta_id    |   meta_key    |   meta_val
----------------------------------------------------
1        1             name            Frank
1        2             name            John
1        3             age             12
2        4             name            Jeremiah
3        5             fav_food        pizza
.................

e interroga tramite qualcosa del genere:

select meta_val from `table` where meta_key = 'name' and uid = 1 order by meta_id desc

1
Sarei curioso di vedere se una soluzione NoSQL funziona davvero meglio di una query relazionale su una chiave di indice correttamente. Sospetto che dovrebbe essere più o meno lo stesso su un esempio di 1 livello come questo.
Bruno,

+1. L'ho notato anche io! Ma ti dà una tabella enorme (in termini di righe). Inoltre è possibile non memorizzare più valori, ad esempio, se l'utente cambia la sua / il suo nome, ma voglio conservare il vecchio nome di troppo, in quel caso sarò bisogno di modello di dati JSON tipo.
ShuklaSannidhya,

@Sann, se volessi mantenere il vecchio valore in JSON, dovresti anche rinominare la chiave: puoi farlo con un EAV (che è quello che è questo esempio) o JSON. Non è particolarmente diverso.
Bruno,

Ti dà una tabella enorme, ma per quanto riguarda i valori duplicati, ti imbatti nello stesso problema con JSON: non puoi avere chiavi duplicate allo stesso livello (ad esempio due chiavi "name") e ti aspetti un comportamento prevedibile.
Adam

Sicuramente non puoi avere chiavi duplicate, ma puoi avere un array associato a quella chiave. Dai un'occhiata alla emailidchiave nell'esempio che ho fornito nella mia domanda.
ShuklaSannidhya,

13

lo svantaggio dell'approccio è esattamente quello che hai menzionato:

è MOLTO lento a trovare le cose, poiché ogni volta è necessario eseguire una ricerca di testo su di esso.

il valore per colonna corrisponde invece all'intera stringa.

Il tuo approccio (dati basati su JSON) va bene per i dati per i quali non hai bisogno di cercare, e devi solo mostrarli insieme ai tuoi dati normali.

Modifica: solo per chiarire, quanto sopra vale per i database relazionali classici. NoSQL usa JSON internamente e probabilmente è un'opzione migliore se questo è il comportamento desiderato.


1
Quindi vuoi dire, dovrei usare entrambi. Chiave per colonna per i dati che devo cercare e JSON per gli altri, giusto?
ShuklaSannidhya,

4
sì. in questo modo, si ottengono le prestazioni richieste dalla ricerca nei campi di dati per colonna e si acquisisce il BLOB JSON da utilizzare nel codice quando necessario.
Nick Andriopoulos,

9

Fondamentalmente, il primo modello che si sta utilizzando si chiama archiviazione basata su documenti. Dovresti dare un'occhiata al famoso database NoSQL basato su documenti come MongoDB e CouchDB . Fondamentalmente, nei db basati su documenti, i dati vengono archiviati in file json e quindi è possibile eseguire query su questi file json.

Il secondo modello è la popolare struttura del database relazionale.

Se vuoi usare un database relazionale come MySql, ti suggerirei di usare solo il secondo modello. Non ha senso usare MySql e archiviare i dati come nel primo modello .

Per rispondere alla tua seconda domanda, non è possibile eseguire query sul nome come "pippo" se si utilizza il primo modello .


È saggio usare entrambi i modelli? Chiave per colonna per i dati che devo cercare e JSON per altri (nello stesso database)?
ShuklaSannidhya,

@Sann - haha. Questa è la duplicazione dei dati. Dovrai assicurarti che entrambi i dati siano sempre gli stessi. Anche se uno dei dati è diverso in qualsiasi momento, i dati non sono puliti e potrebbero causare seri problemi. Quindi, la mia risposta è NO
Girish

Ma la ridondanza non è costosa quando i dati ridondanti sono piccoli, diciamo, ci sono solo due campi su cui ho bisogno di eseguire la ricerca, quindi creo due nuove colonne per loro, [forse] rimuoverli dai miei dati JSON [/ forse] . Non sarà una duplicazione costosa, giusto?
ShuklaSannidhya,

Se stai osservando le prestazioni, MongoDB e CouchDB forniscono operazioni di lettura e scrittura più veloci di MySql perché non offrono molte funzionalità nei database relazionali che non sono richieste nella maggior parte dei casi d'uso.
Girish,

Il vantaggio non potrebbe essere la memorizzazione di oggetti / callback JSON da un'API? Ad esempio, invece di chiamare l'API di YouTube per URL, pollice, ecc., Potresti semplicemente interrogare il tuo DB locale (mysql, lite, ecc.) Per l'oggetto JSON? Non lo so, ha senso per me, soprattutto se stai cercando di memorizzare nella cache o far funzionare un'app più velocemente. Ma non sono un professionista: /
markbratanov il

4

Sembra che tu esiti principalmente a usare o meno un modello relazionale.

Allo stato attuale, il tuo esempio si adatterebbe ragionevolmente bene a un modello relazionale, ma il problema potrebbe sorgere naturalmente quando devi far evolvere questo modello.

Se hai solo uno (o pochi livelli predeterminati) di attributi per la tua entità principale (utente), potresti comunque usare un modello EAV (Entity Attribute Value) in un database relazionale. (Questo ha anche i suoi pro e contro.)

Se prevedi che otterrai valori meno strutturati che vorrai cercare utilizzando l'applicazione, MySQL potrebbe non essere la scelta migliore qui.

Se stavi usando PostgreSQL, potresti potenzialmente ottenere il meglio da entrambi i mondi. (Questo dipende in realtà dalla struttura effettiva dei dati qui ... MySQL non è necessariamente la scelta sbagliata, e le opzioni NoSQL possono essere di interesse, sto solo suggerendo alternative.)

Infatti, PostgreSQL può costruire indici su (immutabili) funzioni (cosa che MySQL non può sapere per quanto ne so) e nelle versioni recenti, è possibile utilizzare PLV8 sui dati JSON direttamente per costruire indici su specifici elementi JSON di interesse, il che migliorerebbe la velocità delle tue query durante la ricerca di tali dati.

MODIFICARE:

Dal momento che non ci saranno troppe colonne su cui ho bisogno di eseguire la ricerca, è saggio usare entrambi i modelli? Chiave per colonna per i dati che devo cercare e JSON per altri (nello stesso database MySQL)?

La miscelazione dei due modelli non è necessariamente errata (presupponendo che lo spazio extra sia trascurabile), ma può causare problemi se non si assicura che i due set di dati siano sincronizzati: l'applicazione non deve mai cambiarne uno senza aggiornare anche l'altro .

Un buon modo per raggiungere questo obiettivo sarebbe fare in modo che un trigger esegua l'aggiornamento automatico, eseguendo una procedura memorizzata nel server di database ogni volta che viene effettuato un aggiornamento o un inserimento. Per quanto ne so, il linguaggio delle procedure memorizzate MySQL probabilmente non supporta alcun tipo di elaborazione JSON. Anche in questo caso PostgreSQL con supporto PLV8 (e possibilmente altri RDBMS con linguaggi di stored procedure più flessibili) dovrebbe essere più utile (l'aggiornamento automatico della colonna relazionale utilizzando un trigger è abbastanza simile all'aggiornamento di un indice allo stesso modo).


Oltre a quello che ho detto sopra, può valere la pena guardare gli operatori per il tipo di dati JSONB in ​​PostgreSQL 9.4 e versioni successive.
Bruno,

1

qualche volta i join sul tavolo saranno un sovraccarico. diciamo per OLAP. se ho due tabelle, una è ORDERS e l'altra è ORDER_DETAILS. Per ottenere tutti i dettagli dell'ordine dobbiamo unire due tabelle, questo renderà la query più lenta quando nessuna delle righe nelle tabelle aumenta, diciamo in milioni o giù di lì. Il join sinistro / destro è troppo più lento del join interno. Penso che se si aggiunge la stringa / oggetto JSON nella rispettiva voce ORDINI, JOIN verrà evitato. aggiungere la generazione di report sarà più veloce ...


1

risposta breve devi mescolare tra loro, usare json per i dati che non hai intenzione di stabilire relazioni con loro come dati di contatto, indirizzo, variazioni dei prodotti


0

Stai cercando di adattare un modello non relazionale in un database relazionale, penso che ti verrebbe servito meglio usando un database NoSQL come MongoDB . Non esiste uno schema predefinito che si adatta al requisito di non avere limiti al numero di campi (vedere l'esempio di raccolta MongoDB tipico). Consulta la documentazione di MongoDB per avere un'idea di come interrogheresti i tuoi documenti, ad es

db.mycollection.find(
    {
      name: 'sann'
    }
)

2
Per curiosità, cosa ti ha fatto supporre che il suo modello non sia relazionale. Le informazioni che ha messo sopra mi sembrano molto relazionali.
Colin M,

0

Come altri hanno sottolineato, le query saranno più lente. Suggerirei invece di aggiungere almeno una colonna "_ID" per eseguire una query.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.