Зберігання JSON в базі даних порівняно з новим стовпцем для кожного ключа


213

Я реалізую наступну модель для зберігання пов’язаних з користувачами даних у своїй таблиці - у мене є 2 стовпчики - uid(первинний ключ) та metaстовпець, який зберігає інші дані про користувача у форматі JSON.

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

Це кращий спосіб (продуктивність мудрий, дизайн-навхрест) , ніж модель однієї колонки-в-власності, де таблиця буде мати багато стовпців , як uid, name, emailid.

Що мені подобається в першій моделі, це те, що ви можете додати якомога більше полів, обмежень немає.

Також мені було цікаво, тепер, коли я реалізував першу модель. Як я виконую запит на ньому, як, наприклад, я хочу назбирати всіх користувачів, які мають ім’я типу "foo"?

Питання - Який кращий спосіб зберігати пов’язані з користувачем дані (маючи на увазі, що кількість полів не визначено) у базі даних, використовуючи - JSON або стовпець на поле? Крім того, якщо перша модель реалізована, як запитувати базу даних, як описано вище? Чи слід використовувати обидві моделі, зберігаючи всі дані, які можуть шукатись запитом в окремому рядку, а інші дані в JSON (це інший рядок)?


Оновлення

Оскільки не буде занадто багато стовпців, на яких мені потрібно здійснити пошук, чи розумно використовувати обидві моделі? Ключ на стовпчик даних, які мені потрібно шукати, та JSON для інших (у тій самій базі даних MySQL)?


40
чудове запитання! але чому ви не прийняли відповідь? це допоможе іншим користувачам (як я)
Sahar Ch.

Відповіді:


198

Оновлено 4 червня 2017 року

З огляду на те, що це питання / відповідь набули деякої популярності, я подумав, що варто оновити.

Коли це питання було спочатку розміщено, MySQL не підтримував типи даних JSON, а підтримка в PostgreSQL була в зародковому стані. Починаючи з 5.7, MySQL тепер підтримує тип даних JSON (у форматі бінарного зберігання), а PostgreSQL JSONB значно дозрів. Обидва продукти надають виконавські типи JSON, які можуть зберігати довільні документи, включаючи підтримку індексації конкретних ключів об’єкта JSON.

Однак я все ще зберігаю своє первісне твердження, що ваш параметр за замовчуванням при використанні реляційної бази даних все-таки повинен бути стовпцем на значення. Реляційні бази даних все ще будуються на припущенні, що дані всередині них будуть досить добре нормалізовані. Планувальник запитів має кращу інформацію про оптимізацію при перегляді стовпців, ніж при перегляді ключів у документі JSON. Іноземні ключі можна створювати між стовпцями (але не між ключами в документах JSON). Важливо: якщо більшість ваших схем є досить мінливими, щоб виправдати використання JSON, ви можете хоча б розглянути, чи є реляційна база даних правильним вибором.

Однак, деякі програми є ідеально реляційними або орієнтованими на документи. Більшість програм мають певну сумішшю обох. Ось кілька прикладів, коли я особисто вважав JSON корисним у реляційній базі даних:

  • Під час зберігання адрес електронної пошти та номерів телефонів для контакту, де зберігання їх як значень у масиві JSON управляється набагато простіше, ніж декілька окремих таблиць

  • Збереження довільних параметрів користувача ключ / значення (де значення може бути булевим, текстовим чи числовим, і ви не хочете мати окремі стовпці для різних типів даних)

  • Зберігання даних конфігурації, що не мають визначеної схеми (якщо ви будуєте Zapier або IFTTT і потрібно зберігати дані конфігурації для кожної інтеграції)

Я впевнений, що є й інші, але це лише кілька швидких прикладів.

Оригінальний відповідь

Якщо ви дійсно хочете мати можливість додавати стільки полів, скільки ви хочете, без обмежень (крім довільного обмеження розміру документа), розгляньте рішення NoSQL, наприклад MongoDB.

Для реляційних баз даних: використовуйте один стовпець на значення. Якщо розмістити JSON крапку в стовпчик, це практично неможливо зробити запит (і болісно повільно, коли ви дійсно знайдете запит, який працює).

Реляційні бази даних використовують переваги типів даних при індексації та мають бути реалізовані з нормалізованою структурою.

Як зауваження: це не означає, що ви ніколи не повинні зберігати JSON у реляційній базі даних. Якщо ви додаєте справжні метадані або якщо ваш JSON описує інформацію, яку не потрібно запитувати і використовується лише для відображення, можливо, буде надмірно створити окремий стовпець для всіх точок даних.


1
Оскільки не буде занадто багато стовпців, на яких мені потрібно здійснити пошук, чи розумно використовувати обидві моделі? Ключ на стовпчик для даних, які мені потрібно шукати, та JSON для інших (у тій самій базі даних MySQL)?
ShuklaSannidhya

3
@Sann Ви повинні використовувати стовпець на значення для даних, які ви хочете часто читати чи запитувати. Поклавши чиє - то ім'я в форматі JSON не має сенсу , тому що, незважаючи на те, що ви , ймовірно, не запит на його основі, ви , ймовірно, потрібно дуже часто. Це багато марного декодування на вашій програмі. Якщо ви справді не відчуваєте, що ваші дані краще представлені як JSON (і, повірте, це, мабуть, немає), ви не повинні вдаватися до цього.
Колін М

5
" virtually impossible to query" - сьогодні psql дозволяє шукати та індексувати його jsonb
тед

1
@ted true. Однак на момент написання цієї відповіді насправді не було. Крім того, в цьому питанні посилання на MySQL, в якому є можливості, немає.
Колін М

3
@ColinM, так, я розумію, що мій коментар на 3 роки молодший за ваш пост. Причина, яку я покинув, полягає в тому, що це може бути корисним та змінити рішення для інших. Щодо посилання на MySQL: може бути правдою, але "For relational databases"у своїй відповіді = P
тед

69

Як і більшість речей "це залежить". Не правильно чи неправильно / добре чи погано саме по собі зберігати дані у стовпцях чи JSON. Це залежить від того, що вам потрібно зробити з цим пізніше. Який ваш прогнозований спосіб доступу до цих даних? Вам потрібно буде перехрестити інші дані?

Інші люди досить добре відповіли, що таке технічний компроміс.

Не багато людей обговорювали, що ваш додаток та функції розвиваються з часом і як це рішення щодо зберігання даних впливає на вашу команду.

Оскільки однією із спокус використання JSON є уникнення міграції схеми, і тому, якщо команда не є дисциплінованою, дуже просто вставити ще одну пару ключів / значень у поле JSON. Міграції для цього немає, ніхто не пам’ятає, для чого це потрібно. Немає перевірки на цьому.

Моя команда використовувала JSON уздовж традиційних стовпців у постгресах, і спочатку це було найкраще, оскільки нарізаний хліб. JSON був привабливим і потужним, поки одного разу ми не зрозуміли, що гнучкість приходить дорожче, і раптом це справжня больова точка. Іноді ця точка виповзає дуже швидко, і тоді стає важко змінити, тому що ми створили так багато інших речей на основі цього дизайнерського рішення.

Понаднормовий додавання нових функцій, наявність даних у JSON призвели до складніших пошукових запитів, ніж те, що можна було б додати, якби ми дотримувались традиційних стовпців. Тоді ми почали виловлювати деякі ключові значення назад у стовпці, щоб ми могли з'єднатись і порівняти значення. Погана ідея. Зараз у нас було дублювання. Новий розробник прийшов на борт і розгубився? Яке значення я повинен економити назад? JSON один чи стовпець?

Поля JSON стали ящиками для сміття для маленьких шматочків цього і того. Немає перевірки даних на рівні бази даних, немає узгодженості та цілісності між документами. Це підштовхнуло всю цю відповідальність до програми, а не перевіряти жорсткі типи та обмеження у традиційних стовпцях.

Озирнувшись назад, JSON дозволив нам дуже швидко перебрати і щось вийти з дверей. Це було чудово. Однак після того, як ми досягли певного розміру команди, гнучкість також дозволила нам повісити себе з довгою мотузкою технічного боргу, яка потім сповільнила подальший прогрес еволюції функції. Використовуйте обережно.

Подумайте довго і наполегливо над тим, який характер ваших даних. Це основа вашого додатка. Як будуть використовуватися дані з часом. І як це можливо ЗМІНИТИ?


7
"ця гнучкість також дозволила нам повіситись довгою мотузкою технічного боргу" дуже приємна метафора!
Антуан Галлікс

Після багатьох років розвитку та роботи з різними людьми, якщо мені слід писати на цю тему, я напишу те саме. Зараз існує так багато розробників, де багато з них, навіть не маючи багаторічного досвіду, насправді нівелюють. Ми повинні тримати все просто, і для мене два речі, які ми завжди маємо враховувати, що можуть "підбити" успіх, - це масштабованість та ремонтопридатність коду.
JohnnyJaxs

27

Просто кидаючи його там, але WordPress має структуру для подібних матеріалів (принаймні, WordPress було першим місцем, де я його спостерігав, він, мабуть, виник у іншому місці).

Це дозволяє безмежні клавіші і швидше шукати, ніж використовувати крапку JSON, але не так швидко, як деякі рішення NoSQL.

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

EDIT

Для зберігання історії / декількох ключів

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

і запитуйте через щось подібне:

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

1
Мені буде цікаво дізнатись, чи дійсно рішення NoSQL працює краще, ніж реляційний запит на належному індексному ключі. Я б підозрював, що він повинен бути більш-менш однаковим на прикладі 1-го рівня.
Бруно

+1. Я теж це помітив! Але це дає вам величезну таблицю (за рядками). Крім того, ви не можете зберігати декілька значень, скажімо, якщо користувач змінює своє ім'я, але я також хочу зберегти старе ім'я, і ​​в цьому випадку мені знадобиться модель даних типу JSON.
ShuklaSannidhya

@Sann, якщо ви хочете зберегти старе значення в JSON, вам також доведеться перейменувати ключ: ви можете зробити це за допомогою EAV (що таке приклад) або JSON. Це не особливо відрізняється.
Бруно

Це дає вам величезну таблицю, але що стосується дублюючих значень, ви стикаєтесь з тією ж проблемою, що і з JSON - у вас не може бути повторюваних ключів на одному рівні (наприклад, два клавіші "ім'я") і очікуєте передбачуваної поведінки.
Адам,

Звичайно, у вас немає дублікатів ключів, але може бути масив, пов'язаний з цим ключем. Перевірте emailidключ у прикладі, який я подав у своєму запитанні.
ShuklaSannidhya

13

недолік підходу - це саме те, що ви згадали:

це робить ДУЖЕ повільним пошук речей, оскільки щоразу потрібно виконувати пошук тексту.

значення на стовпець натомість відповідає цілому рядку.

Ваш підхід (дані на основі JSON) чудово підходить для даних, за якими вам не потрібно шукати, а їх просто потрібно відображати разом із звичайними даними.

Редагувати: Просто для уточнення, вищесказане стосується класичних реляційних баз даних. NoSQL використовує JSON внутрішньо, і, ймовірно, кращий варіант, якщо це бажана поведінка.


1
Отже, ви маєте на увазі, я повинен використовувати обоє. Ключ на стовпчик для даних, які мені потрібні для пошуку, та JSON для інших, правда?
ShuklaSannidhya

4
так. таким чином, ви отримуєте необхідну ефективність від пошуку полів даних на стовпець і захоплюєте крапку JSON для використання в коді, коли це необхідно.
Нік Андріопулос

9

По суті, перша модель, яку ви використовуєте, називається як зберігання на основі документів. Ви повинні ознайомитись з популярною базою даних NoSQL, такою як MongoDB та CouchDB . В основному в документах, заснованих на документах, ви зберігаєте дані у файлах json, а потім можете здійснювати запит на ці файли json.

Друга модель - популярна структура реляційних баз даних.

Якщо ви хочете використовувати реляційну базу даних, як MySql, я б запропонував вам використовувати лише другу модель. Немає сенсу використовувати MySql та зберігати дані, як у першій моделі .

Щоб відповісти на ваше друге запитання, немає можливості запитувати ім’я типу "foo", якщо ви використовуєте першу модель .


Чи розумно використовувати обидві моделі? Ключ на стовпчик для даних, які мені потрібно шукати, та JSON для інших (у тій самій базі даних)?
ShuklaSannidhya

@Sann - ха-ха. Це дублювання даних. Вам доведеться переконатися, що обидва фрагменти даних завжди однакові. Навіть якщо одна з даних у будь-який момент часу відрізняється, то ваші дані не є чистими і можуть призвести до серйозних проблем. Отже, моя відповідь " НІ"
Гіріш

Але надмірність не є дорогою, коли надлишкових даних мало, скажімо, є лише два поля, в яких мені потрібно здійснити пошук, тому я створюю два нові стовпці для них, [можливо] видаляю їх з моїх даних JSON [/ можливо] . Це не буде дорогим дублюванням, правда?
ShuklaSannidhya

Якщо ви дивитесь на продуктивність, то MongoDB і CouchDB забезпечують швидші операції читання і запису, ніж MySql, оскільки вони не пропонують багато можливостей у реляційних базах даних, які не потрібні у більшості випадків використання.
Гіріш

Не могла б користь зберігати об’єкти / зворотні дзвінки JSON з API? Наприклад, замість виклику API youtube за URL-адресою, великим пальцем тощо, ви можете просто запитати локальну БД (mysql, lite тощо) для об'єкта JSON? Я не знаю, для мене це має сенс, особливо якщо ви намагаєтеся кешувати або змусити програму працювати швидше. Але я не професіонал: /
markbratanov

4

Здається, ви головним чином вагаєтесь, чи використовувати реляційну модель чи ні.

Як видно, ваш приклад досить добре підійде до реляційної моделі, але проблема може виникнути, звичайно, коли вам потрібно змусити цю модель розвиватися.

Якщо у вас є лише один (або кілька заздалегідь визначених) рівнів атрибутів для вашого основного об'єкта (користувача), ви все одно можете використовувати модель значення атрибутів суб'єкта (EAV) у реляційній базі даних. (У цього також є свої плюси і мінуси.)

Якщо ви передбачаєте, що ви отримаєте менш структуровані значення, які ви хочете шукати за допомогою своєї програми, MySQL може бути не найкращим вибором тут.

Якщо ви використовували PostgreSQL, ви могли б отримати найкраще з обох світів. (Це дійсно залежить від фактичної структури даних тут ... MySQL не обов'язково є неправильним вибором, і параметри NoSQL можуть представляти інтерес, я просто пропоную альтернативи.)

Дійсно, PostgreSQL може будувати індекс на (незмінних) функціях (яких MySQL не може, наскільки я знаю), а в останніх версіях ви можете використовувати PLV8 на даних JSON безпосередньо для побудови індексів на конкретних цікавих елементах JSON, що покращило б швидкість ваших запитів при пошуку цих даних.

Редагувати:

Оскільки не буде занадто багато стовпців, на яких мені потрібно здійснити пошук, чи розумно використовувати обидві моделі? Ключ на стовпчик даних, які мені потрібно шукати, та JSON для інших (у тій самій базі даних MySQL)?

Змішування двох моделей не обов'язково помиляється (якщо припустити, що додатковий простір є незначним), але це може спричинити проблеми, якщо ви не переконайтеся, що два набори даних синхронізовано: ваша програма ніколи не повинна змінювати одну, не оновлюючи іншу. .

Хорошим способом досягти цього було б, щоб тригер здійснив автоматичне оновлення, запустивши збережену процедуру на сервері бази даних, коли робиться оновлення або вставка. Наскільки мені відомо, мова збережених процедур MySQL, ймовірно, не підтримує будь-яку обробку JSON. Знову PostgreSQL з підтримкою PLV8 (і, можливо, інші RDBMS з більш гнучкими мовами збережених процедур) має бути кориснішим (оновлення реляційного стовпчика автоматично за допомогою тригера досить схоже на оновлення індексу таким же чином).


Окрім сказаного вище, варто поглянути на операторів для типу даних JSONB у PostgreSQL 9.4 та вище.
Бруно

1

деякий час приєднання до столу буде накладними. давайте скажемо для OLAP. якщо у мене дві таблиці, одна - таблиця ЗАМОВЛЕННЯ, а інша - ORDER_DETAILS. Щоб отримати всі деталі замовлення, ми повинні з'єднати дві таблиці, це зробить запит повільніше, коли жоден рядок у таблицях не збільшується, можна сказати в мільйонах і так. З'єднання вліво / вправо надто повільніше, ніж внутрішнє з'єднання. Я думаю, якщо ми додамо рядок JSON / Об'єкт у відповідний запис ЗАМОВЛЕННЯ, то уникнемо. додати генерація звіту буде швидше ...


1

коротка відповідь, яку ви повинні змішати між собою, використовувати json для даних, які ви не збираєтеся з ними зв’язувати, як контактні дані, адреса, змінні продукту


0

Ви намагаєтесь вписати нереляційну модель у реляційну базу даних, я думаю, вам краще послужити, використовуючи базу даних NoSQL, наприклад MongoDB . Немає заздалегідь визначеної схеми, яка б відповідала вашим вимогам не обмежувати кількість полів (див. Типовий приклад колекції MongoDB). Перегляньте документацію MongoDB, щоб отримати уявлення про те, як ви запитували документи, наприклад

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

2
З цікавості ви змусили вас припустити, що його модель нереляційна. Інформація, яку він виклав вище, здається мені дуже відносною.
Колін М

0

Як зазначають інші, запити будуть повільнішими. Я б запропонував замість цього додати принаймні стовпчик "_ID".

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.