Przechowywanie JSON w bazie danych a nowa kolumna dla każdego klucza

213

Wdrażam następujący model do przechowywania danych związanych z użytkownikiem w mojej tabeli - mam 2 kolumny - uid(klucz podstawowy) i metakolumnę, która przechowuje inne dane o użytkowniku w formacie JSON.

 uid   | meta
--------------------------------------------------
 1     | {name:['foo'], 
       |  emailid:['[email protected]','[email protected]']}
--------------------------------------------------
 2     | {name:['sann'], 
       |  emailid:['[email protected]','[email protected]']}
--------------------------------------------------

Jest to lepszy sposób (wydajność mądry, design-wise) niż model jedna kolumna-per-mienia, gdzie tabela ma wiele kolumn, takich jak uid, name, emailid.

W pierwszym modelu podoba mi się to, że możesz dodać tyle pól, ile to możliwe, nie ma ograniczeń.

Zastanawiałem się też, kiedy wdrożyłem pierwszy model. Jak wykonać na nim zapytanie, na przykład chcę pobrać wszystkich użytkowników o nazwach takich jak „foo”?

Pytanie - Jaki jest lepszy sposób przechowywania danych związanych z użytkownikiem (pamiętając, że liczba pól nie jest ustalona) w bazie danych przy użyciu JSON lub kolumna na pole? Ponadto, jeśli wdrożony jest pierwszy model, jak wykonać zapytanie do bazy danych, jak opisano powyżej? Czy powinienem używać obu modeli, przechowując wszystkie dane, które mogą być wyszukiwane przez zapytanie w oddzielnym wierszu, a inne dane w JSON (to inny wiersz)?


Aktualizacja

Ponieważ nie będzie zbyt wielu kolumn, w których muszę przeprowadzić wyszukiwanie, czy mądrze jest używać obu modeli? Klucz po kolumnie dla danych, które muszę wyszukać, i JSON dla innych (w tej samej bazie danych MySQL)?

ShuklaSannidhya
źródło
40
świetne pytanie! ale dlaczego nie przyjąłeś odpowiedzi? które pomogłyby innym użytkownikom (takim jak ja)
Sahar Ch.

Odpowiedzi:

198

Zaktualizowano 4 czerwca 2017 r

Biorąc pod uwagę, że to pytanie / odpowiedź zyskała popularność, uznałem, że warto było zaktualizować.

Kiedy to pytanie zostało pierwotnie opublikowane, MySQL nie obsługiwał typów danych JSON, a obsługa PostgreSQL była w powijakach. Od wersji 5.7 MySQL obsługuje teraz typ danych JSON (w binarnym formacie pamięci), a PostgreSQL JSONB znacznie dojrzał. Oba produkty zapewniają wydajne typy JSON, które mogą przechowywać dowolne dokumenty, w tym obsługę indeksowania określonych kluczy obiektu JSON.

Nadal jednak podtrzymuję moje oryginalne stwierdzenie, że domyślną preferencją podczas korzystania z relacyjnej bazy danych powinna być nadal kolumna według wartości. Relacyjne bazy danych są nadal budowane przy założeniu, że dane w nich zawarte będą dość dobrze znormalizowane. Planer zapytań ma lepsze informacje optymalizacyjne podczas przeglądania kolumn niż podczas przeglądania kluczy w dokumencie JSON. Klucze obce można tworzyć między kolumnami (ale nie między kluczami w dokumentach JSON). Co ważne: jeśli większość schematu jest wystarczająco niestabilna, aby uzasadnić użycie JSON, możesz przynajmniej rozważyć, czy relacyjna baza danych jest właściwym wyborem.

To powiedziawszy, niewiele aplikacji jest doskonale relacyjnych lub zorientowanych na dokumenty. Większość aplikacji ma kombinację obu. Oto kilka przykładów, w których osobiście uważam JSON za użyteczny w relacyjnej bazie danych:

  • Podczas przechowywania adresów e-mail i numerów telefonów dla kontaktu, gdzie przechowywanie ich jako wartości w tablicy JSON jest znacznie łatwiejsze do zarządzania niż wiele osobnych tabel

  • Zapisywanie preferencji użytkownika klucza / wartości (gdzie wartość może być logiczna, tekstowa lub numeryczna, a nie chcesz mieć oddzielnych kolumn dla różnych typów danych)

  • Przechowywanie danych konfiguracyjnych, które nie mają zdefiniowanego schematu (jeśli budujesz Zapier lub IFTTT i potrzebujesz przechowywać dane konfiguracyjne dla każdej integracji)

Jestem pewien, że są też inne, ale to tylko kilka szybkich przykładów.

Oryginalna odpowiedź

Jeśli naprawdę chcesz być w stanie dodać tyle pól, ile chcesz bez żadnych ograniczeń (innych niż arbitralny limit rozmiaru dokumentu), rozważ rozwiązanie NoSQL, takie jak MongoDB.

W przypadku relacyjnych baz danych: użyj jednej kolumny na wartość. Umieszczenie obiektu blob JSON w kolumnie sprawia, że ​​zapytanie jest praktycznie niemożliwe (i boleśnie spowalnia, gdy faktycznie znajdziesz zapytanie, które działa).

Relacyjne bazy danych wykorzystują typy danych podczas indeksowania i są przeznaczone do implementacji ze znormalizowaną strukturą.

Na marginesie: nie oznacza to, że nigdy nie powinieneś przechowywać JSON w relacyjnej bazie danych. Jeśli dodajesz prawdziwe metadane lub jeśli Twój JSON opisuje informacje, które nie wymagają odpytywania i są używane tylko do wyświetlania, może być przesada, aby utworzyć osobną kolumnę dla wszystkich punktów danych.

Colin M.
źródło
1
Ponieważ nie będzie zbyt wielu kolumn, w których muszę przeprowadzić wyszukiwanie, czy mądrze jest używać obu modeli? Klucz po kolumnie dla danych, które muszę wyszukać, i JSON dla innych (w tej samej bazie danych MySQL)?
ShuklaSannidhya,
3
@Sann Należy użyć kolumny według wartości dla danych, które chcesz często odczytywać lub wyszukiwać. Umieszczanie czyichś nazwisk w JSON nie ma sensu, ponieważ nawet jeśli nie możesz na ich podstawie zapytać, prawdopodobnie będziesz go potrzebować bardzo często. To dużo marnotrawstwa dekodowania po stronie aplikacji. Jeśli naprawdę nie uważasz, że Twoje dane są lepiej reprezentowane jako JSON (i zaufaj mi, prawdopodobnie tak nie jest), nie powinieneś uciekać się do tego.
Colin M
5
virtually impossible to query” - dzisiaj psql pozwala na wyszukiwanie i indeksowanie jej jsonb
ted
1
@ted true. Jednak w momencie pisania tej odpowiedzi tak naprawdę nie była dostępna. To pytanie dotyczy również MySQL, w którym nie ma możliwości.
Colin M
3
@ColinM, tak, zdaję sobie sprawę, że mój komentarz jest o 3 lata młodszy od twojego postu. Powodem, dla którego go opuściłem, jest to, że może być pomocne i może zmienić decyzję dla innych. Jeśli chodzi o odniesienie do MySQL: może być prawdą, ale mieć "For relational databases"w swojej odpowiedzi = P
ted
69

Jak większość rzeczy „to zależy”. Przechowywanie danych w kolumnach lub JSON nie jest dobre ani złe / dobre ani złe samo w sobie. To zależy od tego, co musisz z tym zrobić później. Jaki jest twój przewidywany sposób dostępu do tych danych? Czy będziesz musiał odwoływać się do innych danych?

Inne osoby dość dobrze odpowiedziały na kompromis techniczny.

Niewiele osób mówiło o tym, że Twoja aplikacja i funkcje ewoluują w czasie i jak ta decyzja o przechowywaniu danych wpływa na Twój zespół.

Ponieważ jedną z pokus używania JSON jest unikanie migracji schematu, więc jeśli zespół nie jest zdyscyplinowany, bardzo łatwo jest włożyć jeszcze jedną parę klucz / wartość w pole JSON. Nie ma dla niego migracji, nikt nie pamięta, po co. Nie ma na to żadnego potwierdzenia.

Mój zespół zastosował JSON obok tradycyjnych kolumn w postgresie i na początku była to najlepsza rzecz od czasu krojonego chleba. JSON był atrakcyjny i potężny, aż pewnego dnia zdaliśmy sobie sprawę, że elastyczność jest kosztowna i nagle staje się to prawdziwym problemem. Czasami ten punkt bardzo szybko się rozwija, a potem trudno go zmienić, ponieważ na podstawie tej decyzji projektowej zbudowaliśmy wiele innych rzeczy.

Nadgodziny, dodawanie nowych funkcji, posiadanie danych w JSON prowadziło do bardziej skomplikowanych zapytań niż to, co mogłoby zostać dodane, gdybyśmy trzymali się tradycyjnych kolumn. Więc zaczęliśmy łowić niektóre kluczowe wartości z powrotem do kolumn, abyśmy mogli tworzyć sprzężenia i porównywać wartości. Kiepski pomysł. Teraz mieliśmy duplikację. Nowy programista wszedłby na pokład i byłby zdezorientowany? Jaką wartość powinienem oszczędzać? JSON jeden czy kolumna?

Pola JSON stały się szufladami śmieciowymi dla małych kawałków tego i tamtego. Brak sprawdzania poprawności danych na poziomie bazy danych, brak spójności lub integralności dokumentów. To zepchnęło całą tę odpowiedzialność do aplikacji zamiast sprawdzania trudnych typów i ograniczeń z tradycyjnych kolumn.

Patrząc wstecz, JSON pozwolił nam bardzo szybko iterować i wyciągnąć coś za drzwi. To było wspaniałe. Jednak po osiągnięciu pewnego rozmiaru zespołu jego elastyczność pozwoliła nam również powiesić się na długiej linie długu technicznego, co następnie spowolniło postęp ewolucji funkcji. Używaj ostrożnie.

Zastanów się długo, jaki jest charakter Twoich danych. To podstawa Twojej aplikacji. Jak dane będą wykorzystywane w miarę upływu czasu. A jak to się może ZMIENIĆ?

Homan
źródło
7
„jego elastyczność pozwoliła nam również powiesić się na długiej linie długu technicznego” bardzo ładna metafora!
Antoine Gallix,
Po wielu latach rozwoju i pracy z różnymi ludźmi, jeśli powinienem pisać na ten temat, napiszę to samo. Jest teraz tak wielu programistów, których wielu nawet z wieloletnim doświadczeniem tak naprawdę nie awansuje. Musimy utrzymać wszystko w prostocie i dla mnie dwie rzeczy, które zawsze musimy brać pod uwagę, które mogą „ustrukturyzować” sukces, to skalowalność i łatwość konserwacji kodu.
JohnnyJaxs
27

Po prostu go tam wyrzucam, ale WordPress ma strukturę dla tego rodzaju rzeczy (przynajmniej WordPress był pierwszym miejscem, w którym go zauważyłem, prawdopodobnie pochodzi to gdzie indziej).

Pozwala na nieograniczoną liczbę kluczy i jest szybszy do wyszukiwania niż przy użyciu obiektu blob JSON, ale nie tak szybki jak niektóre rozwiązania NoSQL.

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

EDYTOWAĆ

Do przechowywania historii / wielu kluczy

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
.................

i zapytaj za pomocą czegoś takiego:

select meta_val from `table` where meta_key = 'name' and uid = 1 order by meta_id desc
Adam
źródło
1
Byłbym ciekawy, czy rozwiązanie NoSQL naprawdę działa lepiej niż zapytanie relacyjne dotyczące poprawnie klucza indeksu. Podejrzewam, że powinno być mniej więcej tak samo na takim 1-poziomowym przykładzie.
Bruno,
+1. Też to zauważyłem! Ale daje ci ogromny stół (pod względem rzędów). Również można nie przechowywać wiele wartości, powiedzmy, jeśli użytkownik zmieni jego / jej imienia, ale chcę zachować starą nazwę Również w tym przypadku będę potrzebował JSON model danych typu.
ShuklaSannidhya
@Sann, jeśli chcesz zachować starą wartość w JSON, musisz także zmienić nazwę klucza: możesz to zrobić za pomocą EAV (co jest tym przykładem) lub JSON. Nie jest specjalnie inny.
Bruno,
Daje to ogromną tabelę, ale jeśli chodzi o zduplikowane wartości, napotykasz ten sam problem z JSON - nie możesz mieć zduplikowanych kluczy na tym samym poziomie (np. Dwa klucze „nazwa”) i oczekiwać przewidywalnego zachowania.
Adam
Oczywiście nie możesz mieć zduplikowanych kluczy, ale możesz mieć tablicę powiązaną z tym kluczem. Sprawdź emailidklucz w przykładzie podanym w moim pytaniu.
ShuklaSannidhya,
13

wadą tego podejścia jest dokładnie to, o czym wspomniałeś:

sprawia, że ​​wyszukiwanie rzeczy jest BARDZO powolne, ponieważ za każdym razem musisz przeprowadzić na nim wyszukiwanie tekstu.

zamiast tego wartość na kolumnę pasuje do całego łańcucha.

Twoje podejście (dane oparte na JSON) jest w porządku w przypadku danych, których nie musisz wyszukiwać, i po prostu musisz je wyświetlić wraz z normalnymi danymi.

Edycja: Dla wyjaśnienia powyższe dotyczy klasycznych relacyjnych baz danych. NoSQL używa JSON wewnętrznie i jest prawdopodobnie lepszą opcją, jeśli jest to pożądane zachowanie.

Nick Andriopoulos
źródło
1
Masz na myśli, że powinienem użyć obu. Klucz po kolumnie dla danych, które muszę wyszukać, i JSON dla innych, prawda?
ShuklaSannidhya,
4
tak. w ten sposób uzyskujesz wymaganą wydajność dzięki przeszukiwaniu pól danych w kolumnie i pobiera obiekt blob JSON do użycia w kodzie w razie potrzeby.
Nick Andriopoulos,
9

Zasadniczo pierwszy używany model nazywany jest magazynem opartym na dokumentach. Powinieneś rzucić okiem na popularną bazę danych opartą na dokumentach NoSQL, taką jak MongoDB i CouchDB . Zasadniczo w bazach danych opartych na dokumentach przechowujesz dane w plikach json, a następnie możesz wyszukiwać te pliki json.

Drugi model to popularna struktura relacyjnych baz danych.

Jeśli chcesz korzystać z relacyjnej bazy danych, takiej jak MySql, sugeruję użycie tylko drugiego modelu. Nie ma sensu używanie MySql i przechowywanie danych jak w pierwszym modelu .

Aby odpowiedzieć na drugie pytanie, nie ma możliwości zapytania nazwy takiej jak „foo”, jeśli używasz pierwszego modelu .

Girish
źródło
Czy mądrze jest używać obu modeli? Klucz po kolumnie dla danych, które muszę wyszukać, i JSON dla innych (w tej samej bazie danych)?
ShuklaSannidhya,
@Sann - haha. To duplikacja danych. Musisz upewnić się, że obie części danych są zawsze takie same. Nawet jeśli jeden z danych jest inny w dowolnym momencie, dane nie są czyste i mogą prowadzić do poważnego problemu. Tak więc moja odpowiedź brzmi NIE
Girish,
Ale nadmiarowość nie jest kosztowna, gdy nadmiarowe dane są małe, powiedzmy, są tylko dwa pola, na których muszę przeprowadzić wyszukiwanie, więc tworzę dla nich dwie nowe kolumny, [może] usuwam je z moich danych JSON [/ może] . To nie będzie kosztowne powielanie, prawda?
ShuklaSannidhya,
Jeśli patrzysz na wydajność, wtedy MongoDB i CouchDB zapewniają szybsze operacje odczytu i zapisu niż MySql, ponieważ nie oferują wielu funkcji w relacyjnych bazach danych, które nie są wymagane w większości przypadków użycia.
Girish,
Czy korzyścią może być przechowywanie obiektów JSON / wywołań zwrotnych z interfejsu API? Na przykład, zamiast wywoływać API youtube dla URL, thumb itp., Możesz po prostu zapytać lokalnego DB (mysql, lite itp.) O obiekt JSON? Nie wiem, ma to dla mnie sens, szczególnie jeśli próbujesz buforować lub przyspieszyć działanie aplikacji. Ale nie jestem profesjonalistą: /
markbratanov
4

Wygląda na to, że głównie się wahasz, czy użyć modelu relacyjnego, czy nie.

Na obecnym etapie twój przykład byłby dość dobrze dopasowany do modelu relacyjnego, ale problem może oczywiście pojawić się, gdy trzeba go rozwinąć.

Jeśli masz tylko jeden (lub kilka wcześniej określonych) poziomów atrybutów dla głównej jednostki (użytkownika), nadal możesz użyć modelu wartości atrybutu jednostki (EAV) w relacyjnej bazie danych. (Ma to również swoje zalety i wady.)

Jeśli spodziewasz się, że otrzymasz mniej uporządkowanych wartości, które będziesz chciał przeszukiwać za pomocą aplikacji, MySQL może nie być najlepszym wyborem tutaj.

Jeśli korzystasz z PostgreSQL, potencjalnie możesz uzyskać to, co najlepsze z obu światów. (To naprawdę zależy od faktycznej struktury danych tutaj ... MySQL niekoniecznie jest złym wyborem, a opcje NoSQL mogą być interesujące, po prostu sugeruję alternatywy).

Rzeczywiście, PostgreSQL może budować indeks na (niezmiennych) funkcjach (których MySQL nie potrafi, o ile wiem), aw najnowszych wersjach można było używać PLV8 na danych JSON bezpośrednio do budowania indeksów na określonych elementach JSON, które mogłyby się poprawić szybkość twoich zapytań podczas wyszukiwania tych danych.

EDYTOWAĆ:

Ponieważ nie będzie zbyt wielu kolumn, w których muszę przeprowadzić wyszukiwanie, czy mądrze jest używać obu modeli? Klucz po kolumnie dla danych, które muszę wyszukać, i JSON dla innych (w tej samej bazie danych MySQL)?

Mieszanie dwóch modeli niekoniecznie jest złe (zakładając, że dodatkowa przestrzeń jest nieistotna), ale może powodować problemy, jeśli nie upewnisz się, że oba zestawy danych są zsynchronizowane: aplikacja nigdy nie może zmienić jednego bez aktualizacji drugiego .

Dobrym sposobem na osiągnięcie tego byłoby uruchomienie automatycznej aktualizacji przez wyzwalacz, uruchamiając procedurę przechowywaną na serwerze bazy danych za każdym razem, gdy wykonywana jest aktualizacja lub wstawianie. O ile mi wiadomo, język procedur przechowywanych MySQL prawdopodobnie nie obsługuje żadnego przetwarzania JSON. Ponownie PostgreSQL ze wsparciem PLV8 (i ewentualnie inne RDBMS z bardziej elastycznymi językami procedur składowanych) powinny być bardziej przydatne (automatyczne aktualizowanie kolumny relacyjnej za pomocą wyzwalacza jest dość podobne do aktualizowania indeksu w ten sam sposób).

Bruno
źródło
Oprócz tego, co powiedziałem powyżej, warto przyjrzeć się operatorom typu danych JSONB w PostgreSQL 9.4 i nowszych.
Bruno,
1

przyłączenie się do stołu na jakiś czas będzie narzutem. powiedzmy dla OLAP. jeśli mam dwie tabele, jedna to tabela ZAMÓWIENIA, a druga to ZAMÓWIENIA. Aby uzyskać wszystkie szczegóły zamówienia, musimy połączyć dwie tabele. Spowoduje to spowolnienie zapytania, gdy liczba wierszy w tabelach wzrośnie, powiedzmy w milionach lub mniej więcej. Myślę, że jeśli dodamy ciąg JSON / Obiekt do odpowiedniego wpisu ZAMÓWIENIA, unikniemy JOIN. dodaj generowanie raportu będzie szybsze ...

Ravindra
źródło
1

krótka odpowiedź, którą musisz wymieszać, użyć json dla danych, których nie zamierzasz nawiązać z nimi relacji, takich jak dane kontaktowe, adres, różne produkty

Ahmedfraije Aa
źródło
0

Próbujesz dopasować nierelacyjny model do relacyjnej bazy danych, myślę, że lepiej byś był obsługiwany przy użyciu bazy danych NoSQL, takiej jak MongoDB . Nie ma predefiniowanego schematu, który pasowałby do twojego wymagania, aby nie mieć ograniczenia liczby pól (patrz typowy przykład kolekcji MongoDB). Sprawdź dokumentację MongoDB, aby dowiedzieć się, w jaki sposób sprawdzasz swoje dokumenty, np

db.mycollection.find(
    {
      name: 'sann'
    }
)
Chris L.
źródło
2
Z ciekawości sprawiło, że założyłeś, że jego model jest nierelacyjny. Informacje, które przedstawił powyżej, wydają mi się bardzo relacyjne.
Colin M
0

Jak zauważyli inni, zapytania będą wolniejsze. Zamiast tego sugerowałbym dodanie do zapytania co najmniej kolumny „_ID”.

Spodnie
źródło