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

215

Я реализую следующую модель для хранения пользовательских данных в моей таблице - у меня есть 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)?

ShuklaSannidhya
источник
40
отличный вопрос! но почему ты не принял ответ? это помогло бы другим пользователям (таким как я)
Sahar Ch.

Ответы:

200

Обновлено 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
@ истина Тем не менее, на момент написания этого ответа, который не был действительно доступен. Также этот вопрос ссылается на MySQL, в котором есть возможности нет.
Колин М
3
@ColinM, да, я понимаю, что мой комментарий на 3 года моложе вашего поста. Причина, по которой я ушел, заключается в том, что это может быть полезным и изменение решения для других. Что касается ссылки на MySQL: может быть правдой, но "For relational databases"в вашем ответе = P
ted
69

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

Другие люди довольно хорошо ответили, что такое технический компромисс.

Не многие люди обсуждали, что ваше приложение и функции развиваются со временем и как это решение для хранения данных влияет на вашу команду.

Поскольку одно из соблазнов использования JSON состоит в том, чтобы избежать миграции схемы, и поэтому, если команда не дисциплинирована, очень легко вставить еще одну пару ключ / значение в поле JSON. Там нет миграции для этого, никто не помнит, для чего он. Там нет проверки на это.

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

С течением времени добавление новых функций и данных в JSON приводило к более сложным запросам, чем то, что могло бы быть добавлено, если бы мы придерживались традиционных столбцов. Итак, мы начали вылавливать определенные ключевые значения обратно в столбцы, чтобы мы могли объединяться и сравнивать значения. Плохая идея. Теперь у нас было дублирование. Новый разработчик придет на борт и будет сбит с толку? Какое значение я должен сохранить обратно? JSON один или столбец?

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

Оглядываясь назад, JSON позволил нам очень быстро выполнить итерацию и получить что-то за дверью. Это было здорово. Однако после того, как мы достигли определенного размера команды, ее гибкость также позволила нам повесить длинную веревку технического долга, что затем замедлило последующий прогресс в развитии функций. Используйте с осторожностью.

Задумайтесь над тем, какова природа ваших данных. Это основа вашего приложения. Как данные будут использоваться с течением времени. И как это может измениться?

Homan
источник
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
.................

РЕДАКТИРОВАТЬ

Для хранения истории / нескольких ключей

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. Я тоже это заметил! Но это дает вам огромную таблицу (с точки зрения строк). Также вы не можете хранить несколько значений, скажем, если пользователь меняет свое имя, но я хочу сохранить старое имя, в таком случае мне понадобится модель данных типа JSON.
ShuklaSannidhya
@Sann, если вы хотите сохранить старое значение в JSON, вам также необходимо переименовать ключ: вы можете сделать это с помощью EAV (как в этом примере) или JSON. Это не особенно отличается.
Бруно
Это дает вам огромную таблицу, но что касается дублированных значений, вы сталкиваетесь с той же проблемой с JSON - вы не можете иметь дублирующиеся ключи на одном уровне (например, два ключа «name») и ожидать предсказуемого поведения.
Адам
Конечно, вы не можете иметь дубликаты ключей, но можете иметь массив, связанный с этим ключом. Проверьте emailidключ в примере, который я привел в своем вопросе.
ShuklaSannidhya
13

Недостаток подхода - именно то, что вы упомянули:

это делает поиск объектов ОЧЕНЬ медленным, так как каждый раз, когда вам нужно выполнить текстовый поиск по нему.

вместо этого значение на столбец соответствует всей строке.

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

Изменить: просто чтобы уточнить, вышеизложенное относится к классическим реляционным базам данных. NoSQL использует JSON для внутреннего использования и, вероятно, является лучшим вариантом, если это желаемое поведение.

Ник Андриопулос
источник
1
Так ты имеешь в виду, я должен использовать оба. Ключ-столбец для данных, которые мне нужно искать, и JSON для других, верно?
ShuklaSannidhya
4
да. таким образом вы получаете требуемую производительность от поиска в полях данных на столбец и захватываете большой двоичный объект JSON для использования в коде, когда это необходимо.
Ник Андриопулос
9

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

Вторая модель - это популярная структура реляционных баз данных.

Если вы хотите использовать реляционную базу данных, такую ​​как MySql, я бы предложил вам использовать только вторую модель. Нет смысла использовать MySql и хранить данные, как в первой модели .

Чтобы ответить на ваш второй вопрос, нет способа запросить имя, например «foo», если вы используете первую модель .

Гириш
источник
Разумно ли использовать обе модели? Ключ для столбца для данных, которые мне нужно искать, и JSON для других (в той же базе данных)?
ShuklaSannidhya
@ Санн - хаха. Это дублирование данных. Вы должны убедиться, что обе части данных всегда одинаковы. Даже если одна из данных в любой момент времени отличается, ваши данные не являются чистыми и могут привести к серьезной проблеме. Итак, мой ответ НЕТ
Гириш
Но избыточность не требует больших затрат, когда избыточных данных мало, скажем, есть только два поля, по которым мне нужно выполнить поиск, поэтому я создаю для них два новых столбца, [возможно] удаляю их из моих данных JSON [/ может быть] , Это не будет дорогостоящим дублированием, верно?
ShuklaSannidhya
Если вы смотрите на производительность, то MongoDB и CouchDB обеспечивают более быстрые операции чтения и записи, чем MySql, потому что они не предлагают много функций в реляционных базах данных, которые не требуются в большинстве случаев использования.
Гириш
Разве не выгодно хранить объекты JSON / обратные вызовы из API? Например, вместо вызова API youtube для URL, большого пальца и т. Д., Вы можете просто запросить у вашей локальной БД (mysql, lite и т. Д.) Объект JSON? Я не знаю, имеет для меня смысл, особенно если вы пытаетесь кэшировать или заставить приложение работать быстрее. Но я не профессионал: /
Маркбратанов
4

Кажется, вы в основном сомневаетесь, использовать ли реляционную модель или нет.

В своем нынешнем виде ваш пример вполне соответствовал бы реляционной модели, но проблема может возникнуть, конечно, когда вам нужно будет развить эту модель.

Если у вас есть только один (или несколько предварительно определенных) уровней атрибутов для вашего основного объекта (пользователя), вы все равно можете использовать модель значения атрибута объекта (EAV) в реляционной базе данных. (Это также имеет свои плюсы и минусы.)

Если вы ожидаете, что вы получите менее структурированные значения, которые вы хотите искать в своем приложении, MySQL может оказаться не лучшим выбором.

Если бы вы использовали PostgreSQL, вы могли бы получить лучшее из обоих миров. (Это действительно зависит от фактической структуры данных здесь ... MySQL также не обязательно является неправильным выбором, и параметры NoSQL могут представлять интерес, я просто предлагаю альтернативы.)

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

РЕДАКТИРОВАТЬ:

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

Смешивание двух моделей не обязательно является неправильным (при условии, что дополнительное пространство незначительно), но это может вызвать проблемы, если вы не убедитесь, что два набора данных синхронизированы: ваше приложение никогда не должно менять одну, не обновляя также другую. ,

Хороший способ для достижения этой цели - запуск автоматического запуска триггера путем запуска хранимой процедуры на сервере базы данных при каждом обновлении или вставке. Насколько я знаю, языку хранимых процедур MySQL, вероятно, не хватает поддержки для какой-либо обработки JSON. И снова PostgreSQL с поддержкой PLV8 (и, возможно, другие СУБД с более гибкими языками хранимых процедур) должны быть более полезными (автоматическое обновление реляционного столбца с использованием триггера очень похоже на обновление индекса аналогичным образом).

Bruno
источник
В дополнение к тому, что я сказал выше, стоит обратить внимание на операторы для типа данных JSONB в PostgreSQL 9.4 и выше.
Бруно
1

через некоторое время присоединения на столе будут накладными расходами. скажем для OLAP. Если у меня есть две таблицы, одна таблица ORDERS, а другая ORDER_DETAILS. Чтобы получить все детали заказа, нам нужно объединить две таблицы, это сделает запрос медленнее, когда ни одна из строк в таблицах не увеличится, скажем, в миллионах или около того ... левое / правое соединение слишком медленнее, чем внутреннее соединение. Я думаю, что если мы добавим строку / объект JSON в соответствующую запись ORDERS, JOIN будет исключен. добавить генерацию отчетов будет быстрее ...

Ravindra
источник
1

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

Ахмедфрайе А.А.
источник
0

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

db.mycollection.find(
    {
      name: 'sann'
    }
)
Крис Л
источник
2
Из любопытства, что заставило вас предположить, что его модель нереляционная. Информация, которую он выложил выше, кажется мне очень актуальной.
Колин М
0

Как отмечали другие, запросы будут медленнее. Я бы предложил добавить хотя бы столбец «_ID» для запроса по этому вопросу.

Штаны
источник