Подтвердить что ты не робот

Еще один вопрос о динамической модели данных

У меня есть проект, который требует определенных пользователем атрибутов для определенного объекта во время выполнения (скажем, объект-человек в этом примере). В проекте будет много разных пользователей (1000 +), каждый из которых определяет свои собственные уникальные атрибуты для собственных наборов объектов Person.

(Например, пользователь # 1 будет иметь набор определенных атрибутов, которые будут применяться ко всем принадлежащим ему объектам пользователя. Мать это будет на 1000 пользователей, а в нижней строке минимальное количество пользователей приложение будет работать с.) Эти атрибуты будут использоваться для запроса объекта people и возврата результатов.

Я думаю, что это возможные подходы, которые я могу использовать. Я буду использовать С# (и любую версию .NET 3.5 или 4), и у вас есть свободное владение re: что использовать для хранилища данных. (У меня есть mysql и mssql доступны, хотя они имеют свободу использовать любое программное обеспечение, если оно будет соответствовать счету)

Я что-то пропустил или сделал какие-либо неправильные предположения в своей оценке?

Из этих вариантов - какое решение вы бы выбрали?

  • Гибридная модель объекта EAV. (Определите базу данных с использованием обычной реляционной модели и создайте таблицу свойств для таблицы Person).

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

    Я выбил быстрый образец, который имеет интерфейс Subsonic 2.x 'esqe:

    Select().From().Where  ... etc
    

    Что генерирует правильные соединения, затем фильтрует + сворачивает возвращаемые данные в С#, чтобы вернуть данные, настроенные с правильно введенным набором данных.

    Мне еще нужно загрузить это решение. Он основан на совете EA в этом техническом документе Microsoft: Документы RTM для SQL Server 2008 Рекомендации по созданию семантических данных для производительности и масштабируемости

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

    http://bartreyserhove.blogspot.com/2008/02/dynamic-domain-mode-using-nhibernate.html

    Downsides:

    По мере роста системы количество определенных столбцов будет очень большим и может поражать максимальное количество столбцов. Если есть 1000 пользователей, каждый из которых имеет 10 различных атрибутов для своих объектов Person, тогда нам понадобится таблица, содержащая 10k столбцов. Не масштабируется в этом сценарии.

    Я предполагаю, что могу разрешить таблицу атрибутов person для каждого пользователя, но если есть 1000 пользователей для запуска, то 1000 таблиц плюс другие 10 нечетных в приложении.

    Я не уверен, что это будет масштабируемо, но это не похоже. Кто-то, пожалуйста, поправьте меня, если я ошибаюсь!

  • Используйте хранилище данных NoSQL, например CouchDb/MongoDb

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

    http://www.eflorenzano.com/blog/post/why-couchdb-sucks/

  • Использование столбца XML в таблице people для хранения атрибутов

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

  • Сериализация графа объектов в базе данных.

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

  • Связывание С# для berkelyDB

    Из того, что я читаю здесь: http://www.dinosaurtech.com/2009/berkeley-db-c-bindings/

    Berkeley Db определенно оказался полезным, но, как заметил Роберт, нет простого интерфейса. Вся ваша обертка WOO должна быть закодирована вручную, а все ваши индексы поддерживаются вручную. Это намного сложнее, чем SQL/linq-to-sql, но это цена, которую вы платите за нелепую скорость.

    Похоже на большие накладные расходы - однако, если кто-либо может предоставить ссылку на учебник о том, как поддерживать индексы на С#, это может быть зрителем.

  • [EDIT - просто добавил этот] гибрид SQL/RDF. Странно, я не думал об этом раньше. Как и в случае с вариантом 1, но вместо таблицы "property bag", только XREF в хранилище RDF? Запрос будет включать в себя два шага - запрос в хранилище RDF для людей, которые нажимают правильные атрибуты, возвращать объект человека и использовать идентификатор для этого объекта-человека в запросе SQL для возврата реляционных данных. Дополнительные накладные расходы, но могут быть искателями.

Я бы очень признателен за любой вклад здесь!

4b9b3361

Ответ 1

Механизм базы данных ESENT в Windows используется для такого рода полуструктурированных данных. Одним из примеров является Microsoft Exchange, который, как и ваше приложение, имеет тысячи пользователей, каждый из которых может определить свой собственный набор свойств (MAPI named properties). Exchange использует слегка измененную версию ESENT.

ESENT обладает множеством функций, которые позволяют приложениям с большими требованиями к метаданным: каждая таблица ESENT может иметь около ~ 32 тыс. столбцов; таблицы, индексы и столбцы могут быть добавлены во время выполнения; разреженные столбцы не занимают места записи, если не установлены; и таблицы шаблонов могут уменьшить пространство, используемое самими метаданными. Для крупных приложений обычно имеется тысячи таблиц/индексов.

В этом случае вы можете иметь одну таблицу для каждого пользователя и создавать столбцы для каждого пользователя в таблице, создавая индексы для любых столбцов, которые вы хотите запросить. Это будет похоже на то, как некоторые версии Exchange хранят свои данные. Недостатком этого подхода является то, что ESENT не имеет механизма запросов, поэтому вам придется обрабатывать ваши запросы в виде вызовов MakeKey/Seek/MoveNext.

Управляемая оболочка для ESENT находится здесь:

http://managedesent.codeplex.com/

Ответ 2

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

Например, запрос может возвращать лиц с расширенным свойством "Возраст" > 18:

Таблица свойств:

1        Age
2        NickName

Первые результаты:

PersonID Name
1        John
2        Mary

второй результат:

PersonID PropertyID Value
1        1         24
1        2         'Neo'
2        1         32
2        2         'Pocahontas'

Для первого набора результатов вам потребуется внутреннее соединение для расширенного свойства age для запроса основной части объекта объекта Person:

select p.ID, p.Name from Persons p
join PersonExtendedProperties pp
on p.ID = pp.PersonID
where pp.PropertyName = 'Age'
and pp.PropertyValue > 18 -- probably need to convert to integer here

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

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

Ответ 3

Моя рекомендация:

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

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

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

Использовать кварки для идентификаторов полей (но только в главном движке для сохранения ОЗУ и ускорения некоторых операций чтения - не полагайтесь на сравнение указателей на кварков во всех случаях).

Моя мысль о ваших возможностях:

1 возможно. Очевидно, что производительность будет ниже, чем если не будут сохранены столбцы идентификаторов полей.

2 - нет в общем случае, DB не все довольны динамическими изменениями схемы. Но возможно, да, если ваш двигатель БД хорош в этом.

3 Возможно.

4 Да, хотя я бы использовал JSON.

5 Похоже, что 4 менее оптимизированы.

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

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

Изменить: Изменена последняя редакция.

Ответ 4

Предполагая вам место лимита N, о том, сколько пользовательских атрибутов может определить каждый пользователь; просто добавьте N дополнительных столбцов в таблицу Person. Затем укажите отдельную таблицу, в которой вы храните метаданные для каждого пользователя, чтобы описать, как интерпретировать содержимое этих столбцов для каждого пользователя. Подобно # 1, как только вы читаете данные, но нет соединений, необходимых для создания пользовательских атрибутов.

Ответ 5

Для проблемы, аналогичной вашей проблеме, мы использовали подход "XML Column" (четвертый в вашем опросе методов). Но вы должны заметить, что многие базы данных (DBMS) поддерживают индекс для значений xml.

Я рекомендую вам использовать одну таблицу для Person, которая содержит один столбец xml вместе с другими общими столбцами. Другими словами, создайте таблицу Person с столбцами, которые являются общими для всех записей человека, и добавьте один столбец xml для динамических и разных атрибутов.

Мы используем Oracle. он поддерживает индекс для своего xml-типа. Поддерживаются два типа индексов: 1- XMLIndex для индексирования элементов и атрибутов в xml, 2- текстовом индексе Oracle для включения полнотекстового поиска в текстовых полях xml.

Например, в Oracle вы можете создать индекс, например:

CREATE INDEX index1 ON table_name (XMLCast(XMLQuery ('$p/PurchaseOrder/Reference' 
  PASSING XML_Column AS "p" RETURNING CONTENT) AS VARCHAR2(128)));

и xml-запрос поддерживаются в отдельных запросах:

SELECT count(*) FROM purchaseorder
  WHERE XMLCast(XMLQuery('$p/PurchaseOrder/Reference'
  PASSING OBJECT_VALUE AS "p" RETURNING CONTENT)
  AS INTEGER) = 25;

Как я знаю, другие базы данных, такие как PostgreSQL и MS SQL Server (но не mysql), поддерживают такие индексные модели для значения xml.

см. также: http://docs.oracle.com/cd/E11882_01/appdev.112/e23094/xdb_indexing.htm#CHDEADIH