Проектирование базы данных с динамическими полями: одна таблица против многих таблиц - много индексов - PullRequest
3 голосов
/ 10 января 2012

Мне нужно выбрать структуру базы данных, в которой будут храниться типы контента (например, статьи блога, страницы, документы, счета-фактуры, оценки и т. Д.) С динамическими полями: например, тип контента Estimate должен иметь поля title, date и total price.

Однако со временем эти поля могут быть добавлены или удалены, поэтому через 1 год тип константы Estimate может иметь поле notes.

Это обычная задача, предоставляемая известной CMS (например, drupal), но мне интересно, как лучше всего добиться максимальной производительности и гибкости: например, Drupal использует одну таблицу с полями basic (например, title), а все дополнительные поля хранятся в вложенных таблицах, создаваемых на лету и связанных с основным с помощью внешних ключей:

table node
| id | title         | ...
|  1 | First example |
table fields_node_total_price
| id | node_id | value  |
|  1 | 1       | 123.45 |
table fields_node_date
| id | node_id | value    |
|  1 | 1       | 12345677 |

и т.д ..

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

Кстати, многостоловый подход - наиболее часто используемый подход, поэтому у него должно быть много минусов.

Я думаю, какие недостатки будут иметь использование одной таблицы:

| id | title | total_price | date | ec...

Я провел несколько тестов с 5 и 50 дополнительными полями; производительность между подходом с одной таблицей и подходом с несколькими таблицами огромна: одна таблица примерно в 50 раз быстрее.

Каждый раз, когда добавляется поле, в таблицу добавляется столбец. Какие проблемы возникнут при таком подходе?

EDIT

Позвольте мне предоставить несколько деталей:

  1. Приложение все еще находится на этапе разработки, это полная переработка старого приложения, где номера полей были статическими
  2. Мы провели несколько тестов, имитирующих объект для хранения, как с использованием единой таблицы, так и с использованием нескольких таблиц (с использованием 50 полей), результаты:

Время в секундах:

Test                                                            1°          2°          3°          4°          5°          avg
1000 insert single_table                                        8,5687      8,6832      8,7143      8,7977      8,6906      8,69090137389466
1000 select single table LIKE '%key%' on char(250) field        1,5539      1,5540      1,5591      1,5602      1,5564      1,556705142
1000 select single table LIKE '%key%' on char(25) field         0,8848      0,8923      0,8894      0,8919      0,8888      0,889427996
1000 select single table id = $n                                0,2645      0,2620      0,2645      0,2632      0,2636      0,263564462
1000 select single table integer field < $j                     0,8627      0,8759      0,8673      0,8713      0,8767      0,870787334
1000 insert multi_table                                         446,3830    445,2843    440,8151    436,6051    446,0302    443,023531816
1000 select multi table LIKE '%key%' on char(250) field         1,7048      1,6822      1,6817      1,7041      1,6840      1,691367196
1000 select multi table LIKE '%key%' on char(25) field          0,9391      0,9365      0,9382      0,9431      0,9408      0,939536426
1000 select multi table id = $n                                 0,9336      0,9287      0,9349      0,9331      0,9428      0,93460784
1000 select multi table integer field < $j                      2,3366      2,3260      2,3134      2,3342      2,3228      2,326600456

Ответы [ 4 ]

5 голосов
/ 19 января 2012

Возможно, стоит изучить, что возможно с базами данных NoSQL.Я сам их мало использовал, но, учитывая, что вы говорите, что вам нужно «... хранить типы контента (например, статьи блога, страницы, документы, счета-фактуры, оценки и т. Д.) С динамическими полями», кажется, что этоможет быть разумным подходом.

Из статьи Википедии ;

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

и

Часто базы данных NoSQL классифицируются в соответствии с тем, как они хранят данные, и подпадают под такие категории, как хранилища Key-Value,Реализации BigTable, Document-Store базы данных и Graph Database.

Я не говорю, что это ответ на все ваши проблемы, но я бы, конечно, сказал, что стоит посмотреть.

Что касается других подходов, я использовал Entity-Attribute-Value (EAV) в прошлом, и хотя производительность, вероятно, отстает от наличия фиксированной схемы, я чувствую, что это компромисс, что пришлось сделать , чтобы обеспечить гибкость схемы.

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

Несмотря на то, что нам требовалась динамическая структура из-за разнообразия данных, с которыми мы имеем дело, былотакже некоторая фиксированная структура.Поэтому для каждой таблицы, требующей динамической структуры, мы создали «основную» таблицу и таблицу «атрибутов».

Пример этого (специфического для SQL Server) можно увидеть ниже;

CREATE TABLE [dbo].[ParentTbl](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [KnownCol1] [real] NOT NULL,
        -- Lots of other columns ommitted
    [KnownColn] [real] NULL
)        

CREATE TABLE [dbo].[MainTbl](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [ParentId] [int] NOT NULL, -- FK to ParentTbl.Id
    [KnownCol1] [real] NOT NULL,
        -- Lots of other columns ommitted
    [KnownColn] [real] NULL
) 

CREATE TABLE [dbo].[MainTblAttr](
    [Id] [bigint] IDENTITY(1,1) NOT NULL, -- Note big int to cater for LOTS of records
    [MainId] [int] NOT NULL, --FK to MainTbl.Id
    [AttributeColumn] [nvarchar](255) NOT NULL,
    [AttributeValue] [nvarchar](max) NOT NULL
)

Затем вы можете выполнить запрос PIVOT, чтобы помочь вывести ваши данные.Учитывая, что у вас будут разные атрибуты, вам нужно определить, какие столбцы включить в сводку.Я нашел этот пример неоценимым , когда я разрабатывал свое решение.Однако на SO есть множество примеров.Просто выполните поиск динамических столбцов.

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

Удачи.

3 голосов
/ 19 января 2012

Нет единого «правильного» ответа на этот вопрос.Как вы уже упоминали, это сводится к компромиссу между гибкостью и скоростью.

Это зависит от того, какое узкое место в вашем приложении.Вы выполнили профилирование своего приложения?Соответствует ли время запросов к базе данных типичным временам пинга для конечного пользователя, скорости передачи и т. Д.?На самом деле нет смысла беспокоиться об оптимизации производительности, пока вы не уверены, что у вас действительно есть проблема с производительностью, и не знаете, где находится узкое место!

Мне нравится использовать Firebug в Firefox, чтобы определить, сколько времени занимает моя страница.отобразить с конечным пользователем и сравнить его с результатом таймера секундомера, запущенного до запроса и остановленного после запроса.Для простоты использования я печатаю его внизу каждой страницы во время профилирования.

Рассматривали ли вы представления, чтобы компенсировать недостатки многостолового подхода?

Что касается сложной задачи запроса:с помощью «виртуальных» представлений вы можете избежать использования сложных объединений в своих ежедневных запросах.Вы помещаете объединения в определение представления, и при изменении динамических полей вам нужно только настроить представление.(Примечание: с виртуальными представлениями ваш «простой» запрос динамически переписывается с использованием объединений в определении представления.)

Что касается проблемы скорости: вы можете использовать «материализованные» определения представления с многостоловым подходом, чтобы получитьпроизводительность одного стола.С материализованными представлениями СУБД создает физическую таблицу, используя определение представления, используя соединения в определении представления.В результате вы действительно запрашиваете «одну таблицу», которая, тем не менее, автоматически синхронизируется с определением нескольких таблиц.Вы получаете лучшее из обоих миров за счет места для хранения БД.

В зависимости от вашей СУБД, вы также можете обновлять представления напрямую (вместо нескольких таблиц).Я считаю, что это имеет место с MySQL.С Postgres вам нужно использовать триггеры, чтобы сообщить системе, как изменить базовую мульти-таблицу.

В заключение:

  1. Лично, если бы я хотелсоздать систему на длительный срок, я бы пошел с многостоловым подходом с виртуализированными представлениями.Я бы тогда «материализовал» только те взгляды, по которым я чувствую, что производительности не хватает.Это больше усилий, чтобы оторваться от земли на скорости одного стола, но она останется невероятно гибкой.
  2. Если бы я хотел что-то быстрое и грязное, но быстрое, я бы пошел с одним столом.- Но иногда это может быть болезненно, но включать некоторые изменения.Я не вижу проблемы, связанной с большим количеством столбцов.Любая реляционная СУБД должна быть в порядке.
  3. Если бы я хотел что-то быстрое и грязное, но гибкое, я бы использовал несколько таблиц и не беспокоился об определении представлений и триггеров, а определял только некоторые индексы для ускорения операций объединения.

Последний пункт: Вы действительно должны попытаться выполнить как можно больше обработки данных в СУБД.(т.е. с запросом). Вы уже поняли, что «самому коду приходится многократно повторяться для построения запроса», это не так (см. представление и т. д.).Тем не менее, это говорит о том, что у вас есть склонность к чрезмерной обработке данных в вашем приложении.SQL невероятно выразителен, и ваша БД, скорее всего, будет использовать гораздо более эффективные алгоритмы для оценки вашей обработки данных, чем все, что вы, вероятно, реализуете сами.Примечание: SQL-запрос, который выглядит невероятно сложным, на самом деле может выполняться очень быстро!

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

0 голосов
/ 19 января 2012

В больших системах (50+ столбцов, использующих репликацию с 5+ хостами) нагрузка, связанная с обновлением одной строки в таблице, увеличивается при добавлении дополнительных столбцов (bc. Должна быть реплицирована вся строка). Этот эффект можно уменьшить, разбив большую таблицу на несколько частей. При использовании правильных индексов это практически бесплатно для аналитических рабочих нагрузок. Хотя это ухудшает производительность вставок.

0 голосов
/ 11 января 2012

Первое решение - это база данных «атрибутов значений»: База данных значений атрибутов сущностей и строгая реляционная модель электронной коммерции

Я бы выбрал более позднее решение: базы данных созданы для храненияданные не структура!У нас есть серьезная проблема, потому что у нас есть база данных атрибутов значений сущностей, в которую мы можем вставить любой тип данных, но невозможно запросить их или указать конкретные данные без магических строк.

Или вы можете сделать другоерешение: сохраните ваше дополнительное поле в сериализованной версии вашего объекта AdditionnalFields.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...