Natywne wsparcie JSON w MYSQL 5.7: jakie są wady i zalety typu danych JSON w MYSQL?

114

W MySQL 5.7 dodano nowy typ danych do przechowywania danych JSON w tabelach MySQL . Będzie to oczywiście wielka zmiana w MySQL. Wymienili kilka korzyści

Walidacja dokumentów - w kolumnie JSON można przechowywać tylko prawidłowe dokumenty JSON, dzięki czemu uzyskujesz automatyczną weryfikację danych.

Wydajny dostęp - co ważniejsze, gdy przechowujesz dokument JSON w kolumnie JSON, nie jest on przechowywany jako zwykła wartość tekstowa. Zamiast tego jest przechowywany w zoptymalizowanym formacie binarnym, który umożliwia szybszy dostęp do składowych obiektów i elementów tablicy.

Wydajność - popraw wydajność zapytań, tworząc indeksy wartości w kolumnach JSON. Można to osiągnąć za pomocą „indeksów funkcjonalnych” w wirtualnych kolumnach.

Wygoda - dodatkowa składnia wbudowana w kolumny JSON sprawia, że ​​integracja zapytań dokumentów w SQL jest bardzo naturalna. Na przykład (features.feature to kolumna JSON):SELECT feature->"$.properties.STREET" AS property_street FROM features WHERE id = 121254;

ŁAŁ ! zawierają świetne funkcje. Teraz łatwiej jest manipulować danymi. Teraz możliwe jest przechowywanie bardziej złożonych danych w kolumnie. Więc MySQL jest teraz wzbogacony o NoSQL.

Teraz mogę sobie wyobrazić zapytanie o dane JSON w stylu

SELECT * FROM t1
WHERE JSON_EXTRACT(data,"$.series") IN 
( 
SELECT JSON_EXTRACT(data,"$.inverted") 
FROM t1 | {"series": 3, "inverted": 8} 
WHERE JSON_EXTRACT(data,"$.inverted")<4 );

Czy mogę więc przechowywać ogromne, małe relacje w kilku kolumnach json? Czy to jest dobre? Czy to łamie normalizację. Jeśli jest to możliwe, myślę, że będzie działać jak NoSQL w kolumnie MySQL . Naprawdę chcę dowiedzieć się więcej o tej funkcji. Plusy i minusy typu danych MySQL JSON.

Imran
źródło
och, proszę, nie mów tego, co myślę, że mówisz. Tutaj, przeczytaj to . Twój jest kolejnym wariantem złego pomysłu.
Drew
@Drew You udzielił dużej odpowiedzi. Ale to nie jest moje pytanie. Chcę tylko wiedzieć, że jeśli napiszemy zapytanie o dane json, możemy pominąć reguły sql. ponieważ nie potrzebujemy wielu stolików
Imran
1
powiedziałeś Now it is possible to store more complex data in column. Uważaj
Drew
2
Indeks obsługi typu danych Json i ma inteligentny rozmiar: 64K i 4G. Więc jaki jest problem, jeśli chcę przechowywać 2000 danych i dodać 5 zagnieżdżonych etykiet zamiast 5 tabel z relacją?
Imran
5
„Naprawdę chcę dowiedzieć się więcej o tej funkcji”. oraz „Zalety i wady typu danych MySQL JSON”. nie są pytaniami i jeśli zostaną przeformułowane, ponieważ pytania są zbyt szerokie. „Dlatego nigdy nie myślę o złożonej strukturze schematu i kluczach obcych w MySQL. Przechowuję złożone relacje, używając tylko kilku tabel”. jest wewnętrznie sprzeczne, ponieważ JSON to nie relacje i FK. Wyjaśnienie „czy to dobrze” jest tylko wstępem do modelu relacyjnego, więc znowu jest zbyt szerokie. Przeanalizuj kilka przykładów, stwórz własną listę zalet i wad z odniesieniami i zapytaj, gdzie popełniłeś błąd.
philipxy

Odpowiedzi:

58
SELECT * FROM t1
WHERE JSON_EXTRACT(data,"$.series") IN ...

Użycie kolumny wewnątrz wyrażenia lub funkcji, takiej jak ta, niweczy wszelkie szanse, że zapytanie użyje indeksu, aby zoptymalizować zapytanie. Zapytanie pokazane powyżej jest zmuszone wykonać skanowanie tabeli.

Twierdzenie o „skutecznym dostępie” jest mylące. Oznacza to, że po sprawdzeniu przez zapytanie wiersza z dokumentem JSON może wyodrębnić pole bez konieczności analizowania tekstu składni JSON. Ale wyszukiwanie wierszy nadal wymaga skanowania tabeli. Innymi słowy, zapytanie musi zbadać każdy wiersz.

Analogicznie, jeśli wyszukuję w książce telefonicznej osoby o imieniu „Bill”, nadal muszę czytać każdą stronę w książce telefonicznej, nawet jeśli imiona zostały podświetlone, aby ich odnalezienie było nieco szybsze.

MySQL 5.7 umożliwia zdefiniowanie wirtualnej kolumny w tabeli, a następnie utworzenie indeksu na wirtualnej kolumnie.

ALTER TABLE t1
  ADD COLUMN series AS (JSON_EXTRACT(data, '$.series')),
  ADD INDEX (series);

Następnie, jeśli zapytasz o wirtualną kolumnę, może ona użyć indeksu i uniknąć skanowania tabeli.

SELECT * FROM t1
WHERE series IN ...

To fajne, ale mija się z celem używania JSON. Atrakcyjną częścią korzystania z formatu JSON jest to, że umożliwia on dodawanie nowych atrybutów bez konieczności wykonywania ALTER TABLE. Ale okazuje się, że i tak musisz zdefiniować dodatkową (wirtualną) kolumnę, jeśli chcesz przeszukiwać pola JSON za pomocą indeksu.

Ale nie musisz definiować wirtualnych kolumn i indeksów dla każdego pola w dokumencie JSON - tylko te, które chcesz przeszukiwać lub sortować. Mogą istnieć inne atrybuty w formacie JSON, które wystarczy wyodrębnić z listy wyboru, jak poniżej:

SELECT JSON_EXTRACT(data, '$.series') AS series FROM t1
WHERE <other conditions>

Generalnie powiedziałbym, że jest to najlepszy sposób wykorzystania JSON w MySQL. Tylko na liście wyboru.

Gdy odwołujesz się do kolumn w innych klauzulach (JOIN, WHERE, GROUP BY, HAVING, ORDER BY), bardziej wydajne jest użycie konwencjonalnych kolumn, a nie pól w dokumentach JSON.

Przedstawiłem wykład pt. Jak używać JSON w MySQL Wrong na konferencji Percona Live w kwietniu 2018 r. Zaktualizuję i powtórzę wykład na Oracle Code One jesienią.

Istnieją inne problemy z JSON. Na przykład w moich testach wymagał 2-3 razy więcej miejsca na przechowywanie dokumentów JSON w porównaniu do konwencjonalnych kolumn przechowujących te same dane.

MySQL agresywnie promuje swoje nowe możliwości JSON, głównie w celu zniechęcenia ludzi do migracji do MongoDB. Ale przechowywanie danych zorientowane na dokumenty, takie jak MongoDB, jest zasadniczo nierelacyjnym sposobem organizowania danych. Różni się od relacyjnego. Nie mówię, że jedna jest lepsza od drugiej, to po prostu inna technika, dostosowana do różnych typów zapytań.

Należy zdecydować się na użycie formatu JSON, gdy JSON zwiększa wydajność zapytań.

Nie wybieraj technologii tylko dlatego, że jest nowa lub ze względu na modę.


Edycja: implementacja kolumny wirtualnej w MySQL powinna używać indeksu, jeśli klauzula WHERE używa dokładnie tego samego wyrażenia, co definicja kolumny wirtualnej. Oznacza to, że następujące osoby powinny używać indeksu w kolumnie wirtualnej, ponieważ kolumna wirtualna jest zdefiniowanaAS (JSON_EXTRACT(data,"$.series"))

SELECT * FROM t1
WHERE JSON_EXTRACT(data,"$.series") IN ...

Z wyjątkiem tego, że podczas testowania tej funkcji odkryłem, że NIE działa ona z jakiegoś powodu, jeśli wyrażenie jest funkcją wyodrębniania JSON. Działa z innymi typami wyrażeń, ale nie z funkcjami JSON.

Bill Karwin
źródło
7
Warto kliknąć link do slajdów
Paul Campbell
Dobra uwaga, że ​​obie technologie są dobre na swój sposób, decydujemy, która będzie pasować do naszych potrzeb i co daje nam większą przewagę pod względem bezpieczeństwa i wydajności.
Christopher Pelayo
1
Sedno problemu polega na tym, że funkcja ALTER TABLE jest nadal potrzebna do korzystania z indeksu w wygenerowanej kolumnie dla każdego nowego klucza w JSON. Cieszę się, że to podkreślono.
user1454926
Tylko jeśli potrzebujesz dodać wirtualną kolumnę i / lub indeks. Jeśli traktujesz dane JSON jako „czarną skrzynkę” i nie próbujesz wykonywać żadnych zapytań, które przeszukują lub sortują podpola w formacie JSON, nie musisz tego robić. Dlatego zalecamy, aby uniknąć w przedstawieniu JSON JOIN, WHERElub innych klauzul. Po prostu pobierz kolumnę JSON z listy wyboru.
Bill Karwin
Link do slajdów jest uszkodzony, @BillKarwin.
lakesare
43

Poniższe z MySQL 5.7 przywraca sexy z JSON brzmi dla mnie dobrze:

Używanie typu danych JSON w MySQL ma dwie zalety w porównaniu z przechowywaniem ciągów JSON w polu tekstowym:

Walidacji danych. Dokumenty JSON będą automatycznie sprawdzane, a nieprawidłowe dokumenty spowodują błąd. Ulepszony format pamięci wewnętrznej. Dane JSON są konwertowane do formatu, który umożliwia szybki dostęp do odczytu danych w ustrukturyzowanym formacie. Serwer może wyszukiwać podobiekty lub zagnieżdżone wartości według klucza lub indeksu, co zapewnia dodatkową elastyczność i wydajność.

...

Specjalistyczne odmiany sklepów NoSQL (bazy danych dokumentów, magazyny klucz-wartość i bazy danych wykresów) są prawdopodobnie lepszymi opcjami dla ich konkretnych przypadków użycia, ale dodanie tego typu danych może pozwolić na zmniejszenie złożoności stosu technologii. Cena jest połączona z bazami danych MySQL (lub kompatybilnymi). Ale to nie problem dla wielu użytkowników.

Zwróć uwagę na język dotyczący walidacji dokumentów, ponieważ jest to ważny czynnik. Wydaje mi się, że należy przeprowadzić serię testów, aby porównać oba podejścia. Te dwie rzeczy:

  1. MySQL z typami danych JSON
  2. MySQL bez

W sieci są tylko płytkie udostępnienia slajdów na temat mysql / json / wydajności z tego, co widzę.

Być może Twój post może być dla niego centrum. A może wydajność jest następstwem przemyślenia, nie jestem pewien, i jesteś po prostu podekscytowany, aby nie tworzyć wielu tabel.

Rysował
źródło
7
Jeden con; Typ danych JSON nie jest obsługiwany przez tabele Mysql Memory, takie jak typy danych, TEXT i BLOB. Oznacza to, że jeśli wymagana jest tabela tymczasowa, utworzy ona tabelę dyskową, a nie pamięć. Niektóre przypadki użycia tabeli tymczasowej zostały opisane tutaj: dev.mysql.com/doc/refman/5.7/en/internal-temporary-tables.html
raiz media
1
@raizmedia Czy mógłbyś wyjaśnić, dlaczego tabela oparta na dyskach jest problemem w porównaniu z pamięcią (chyba tabela oparta na pamięci)?
Lapin
@lapin Prawdopodobnie z powodu ograniczeń prędkości.
Mały pomocnik
@LittleHelper możesz tego uniknąć, jeśli używasz gniazda PCI 4x 40 Gb / s M.2 i włożysz obsługiwany dysk 40 Gb / s. Działa to tak szybko, jak pamięć. Możesz także zastosować specjalny format do tego napędu, który jest używany do formatowania pamięci.
Sergey Romanov
@SergeyRomanov, [citation required]czy porównałeś ten dysk z pamięcią RAM?
Bill Karwin
11

Niedawno wpadłem w ten problem i podsumowuję następujące doświadczenia:

1, nie ma sposobu na rozwiązanie wszystkich pytań. 2, powinieneś poprawnie używać JSON.

Jeden przypadek:

Mam tabelę o nazwie:, CustomFieldktóra musi zawierać dwie kolumny: name, fields. namejest zlokalizowanym ciągiem, jego zawartość powinna wyglądać następująco:

{
  "en":"this is English name",
  "zh":"this is Chinese name"
   ...(other languages)
}

I fieldspowinno wyglądać tak:

[
  {
    "filed1":"value",
    "filed2":"value"
    ...
  },
  {
    "filed1":"value",
    "filed2":"value"
    ...
  }
  ...
]

Jak widać, zarówno nameplikfields może być zapisany jako JSON, i to działa!

Jeśli jednak używam namebardzo często do przeszukiwania tej tabeli, co powinienem zrobić? Użyj JSON_CONTAINS, JSON_EXTRACT...? Oczywiście zapisywanie go jako JSON nie jest już dobrym pomysłem, powinniśmy zapisać go w niezależnej tabeli:CustomFieldName .

Z powyższego przypadku myślę, że powinieneś pamiętać o następujących pomysłach:

  1. Dlaczego MYSQL obsługuje JSON?
  2. Dlaczego chcesz używać JSON? Czy Twoja logika biznesowa właśnie tego potrzebowała? Czy jest coś jeszcze?
  3. Nigdy nie bądź leniwy

Dzięki

Bruce
źródło
2
Możesz być zainteresowany użyciem kolumny WIRTUALNEJ. percona.com/blog/2016/03/07/…
Bell
10

Z mojego doświadczenia wynika, że ​​implementacja JSON przynajmniej w MySql 5.7 nie jest zbyt przydatna ze względu na słabą wydajność. Cóż, nie jest tak źle, jeśli chodzi o odczytywanie danych i sprawdzanie poprawności. Jednak modyfikacja JSON jest 10-20 razy wolniejsza w MySql niż w Pythonie czy PHP. Wyobraźmy sobie bardzo prosty JSON:

{ "name": "value" }

Załóżmy, że musimy przekonwertować to na coś takiego:

{ "name": "value", "newName": "value" }

Możesz stworzyć prosty skrypt w Pythonie lub PHP, który wybierze wszystkie wiersze i zaktualizuje je jeden po drugim. Nie jesteś zmuszony do wykonania jednej dużej transakcji, więc inne aplikacje będą mogły korzystać z tabeli równolegle. Oczywiście, jeśli chcesz, możesz też dokonać jednej ogromnej transakcji, dzięki czemu uzyskasz gwarancję, że MySql wykona „wszystko albo nic”, ale inne aplikacje najprawdopodobniej nie będą w stanie korzystać z bazy danych podczas wykonywania transakcji.

Mam tabelę 40 milionów wierszy, a skrypt Pythona aktualizuje ją w ciągu 3-4 godzin.

Teraz mamy MySql JSON, więc nie potrzebujemy już Pythona ani PHP, możemy zrobić coś takiego:

UPDATE `JsonTable` SET `JsonColumn` = JSON_SET(`JsonColumn`, "newName", JSON_EXTRACT(`JsonColumn`, "name"))

Wygląda prosto i doskonale. Jednak jego prędkość jest 10-20 razy wolniejsza niż wersja Pythona i jest to pojedyncza transakcja, więc inne aplikacje nie mogą równolegle modyfikować danych tabeli.

Tak więc, jeśli chcemy po prostu zduplikować klucz JSON w tabeli 40 milionów wierszy, nie musimy w ogóle używać tabeli przez 30-40 godzin. To nie ma sensu.

Jeśli chodzi o czytanie danych, z mojego doświadczenia wynika, że ​​bezpośredni dostęp do pola JSON przez JSON_EXTRACTin WHEREjest również wyjątkowo wolny (znacznie wolniejszy niż w TEXTprzypadkuLIKE nieindeksowanej kolumny). Wirtualne kolumny generowane działają znacznie szybciej, jednak jeśli znamy wcześniej strukturę danych, nie potrzebujemy JSON, możemy zamiast tego użyć tradycyjnych kolumn. Kiedy używamy JSON tam, gdzie jest to naprawdę przydatne, tj. Gdy struktura danych jest nieznana lub często się zmienia (na przykład niestandardowe ustawienia wtyczek), regularne tworzenie wirtualnych kolumn dla ewentualnych nowych kolumn nie wygląda na dobry pomysł.

Python i PHP sprawiają, że walidacja JSON jest jak urok, więc wątpliwe jest, czy w ogóle potrzebujemy walidacji JSON po stronie MySql. Dlaczego by nie sprawdzić poprawności XML, dokumentów Microsoft Office lub sprawdzić pisownię? ;)

Vitalii
źródło