Comments 36
я тоже сначала подумал о наследовании.
в коде наверное наследование будет неплохим вариантом, однако в структуре базы данных это будет одна таблица рейтинга — на каждую сущность.
а автор предлагает использовать одну таблицу для всех сущностей, но указывать в этой таблице тип сущности для которой делается рейтинг.
однако на месте автора я бы привел цифры по нагрузочному тестированию такого подхода к рейтингу.
возможно быстрее будет выбирать данные из разных мелких таблиц чем из одной большой.
а автор предлагает использовать одну таблицу для всех сущностей, но указывать в этой таблице тип сущности для которой делается рейтинг.
однако на месте автора я бы привел цифры по нагрузочному тестированию такого подхода к рейтингу.
возможно быстрее будет выбирать данные из разных мелких таблиц чем из одной большой.
Всем понятно что мой вариант будет работать медленнее, но кэширование может исправить сютуацию.
Неужели, полиморфные модели?
труд можно СИИИИЛЬНО облегчить не плодя лишних сущностей
Кстати таблицу Post и Comment волне можно и соединить (в одном из внутрикорпоротивных движков блого-форума я так и сделал).
единая таблица:
Post:
id
parent_id
user_id
…
если parent_id — 0/null — то это пост/начало форума, инача коммент, ответ.
Я не говорю что это правильно и так нужно делать, просто ради прикола в одном небольшом проекте я такое забацал.
единая таблица:
Post:
id
parent_id
user_id
…
если parent_id — 0/null — то это пост/начало форума, инача коммент, ответ.
Я не говорю что это правильно и так нужно делать, просто ради прикола в одном небольшом проекте я такое забацал.
Информацию о рейтинге я бы однозначно закешировал в соответствующем поле у сущности. А делать выборку из таблицы Rating следовало бы только при голосовании (чтобы исключить повторные)
как в рельсах =)
Походит на ContentTypes-фреймворк в джанге. Такие связи там называются generic relations. Только там все это зашло далеко вперед, с достаточно полной поддержкой со стороны ORM. Очень широко применяется при написании подключаемых приложений.
Gibbzy пишет: «Как известно в работе вёб программиста часто нужно делать что то достаточно быстро, для этого придумали множество всяческих инструментов ООП, фреймворки и много всего, но как не порождать монстров, а делать всё быстро и качественно?»
Просто знать и использовать правильные фреймворки. Хорошо разобраться в фреймворке сложнее, чем написать велосипед. Но, как мне кажется, полезнее.
И в джанге, и в рельсах (как пишет LightAlloy) это все давно реализовано и широко применяется (на рельсах не писал, но, думаю, что там тоже), с разными удобными плюшками и много-раз-наступленными граблями.
P.S. Этот конкретный велосипед считаю полезным, ход мыслей — верным.
Просто знать и использовать правильные фреймворки. Хорошо разобраться в фреймворке сложнее, чем написать велосипед. Но, как мне кажется, полезнее.
И в джанге, и в рельсах (как пишет LightAlloy) это все давно реализовано и широко применяется (на рельсах не писал, но, думаю, что там тоже), с разными удобными плюшками и много-раз-наступленными граблями.
P.S. Этот конкретный велосипед считаю полезным, ход мыслей — верным.
в целом решение имеет место быть, но:
1) когда сервис с высокой активностью, общая таблица будет излишне переполнена.
2) раз уж зашел разговор про Zend Framework… Не место этим методам в этом классе — мы не рейтингу рейтинг выставляем и иже с ним.
Следовательно нужены:
— абстрактный класс от которого будут наследоваться классы конкретных таблиц
— каждый класс наследующий VotingTable должен содержать определение зависимостей через $_dependentTables и реализовывать специфичный select (определение метода getTableType) в которым бы выборка производилась с учетом типа таблицы.
— Класс Rating должен содержать правила взаимосвязей с таблицами по которым ведется голосование ($_referenceMap)
— класс Voting extends Zend_Db_Table_Row_Abstract, содержащий методы работы с рейтингом с немного иной сигнатурой, которые в свою очередь будут проксировать вызов магических методов ($row->findTableClassViaIntersectionTableClassByRule1) родного механизма выборки многие ко многим [http://framework.zend.com/manual/en/zend.db.table.relationships.html#zend.db.table.relationships.fetching.many-to-many] и и создавать новые инстанции Rating (методом setRating).
Как-то так…
1) когда сервис с высокой активностью, общая таблица будет излишне переполнена.
2) раз уж зашел разговор про Zend Framework… Не место этим методам в этом классе — мы не рейтингу рейтинг выставляем и иже с ним.
Следовательно нужены:
— абстрактный класс от которого будут наследоваться классы конкретных таблиц
abstract class VotingTable extends Zend_Db_Table_Abstract { protected $_rowClass = 'Voting'; /** * @return Zend_Db_Select */ abstract function getTableType(); }
— каждый класс наследующий VotingTable должен содержать определение зависимостей через $_dependentTables и реализовывать специфичный select (определение метода getTableType) в которым бы выборка производилась с учетом типа таблицы.
— Класс Rating должен содержать правила взаимосвязей с таблицами по которым ведется голосование ($_referenceMap)
— класс Voting extends Zend_Db_Table_Row_Abstract, содержащий методы работы с рейтингом с немного иной сигнатурой, которые в свою очередь будут проксировать вызов магических методов ($row->findTableClassViaIntersectionTableClassByRule1) родного механизма выборки многие ко многим [http://framework.zend.com/manual/en/zend.db.table.relationships.html#zend.db.table.relationships.fetching.many-to-many] и и создавать новые инстанции Rating (методом setRating).
class Voting extends Zend_Db_Table_Row_Abstract { protected $_tableType; public function init() { $this->_tableType = $this->getTable()->getTableType(); } public function setRating($user, $amount) { /* ... */ } public function getRating() { /* ... */ } public function isVoted($user_id) { /* ... */ } }
Как-то так…
«1) когда сервис с высокой активностью, общая таблица будет излишне переполнена.»
в чём кардинальная разница, 3 таблицы с N записями суммарно или 1 таблица с N записями?
в чём кардинальная разница, 3 таблицы с N записями суммарно или 1 таблица с N записями?
а вы сможете эту разницу измерить? вы уверены, что вы сейчас не экономите на списках?
озвучьте, пожалуйста, абсолютную разницу в секундах между этими 2 вариантами?
пусть, к примеру, в каждой из таблиц будет миллионов по 50 записей.
заполните и выложите результаты. потом сделайте вывод о целесообразности.
озвучьте, пожалуйста, абсолютную разницу в секундах между этими 2 вариантами?
пусть, к примеру, в каждой из таблиц будет миллионов по 50 записей.
заполните и выложите результаты. потом сделайте вывод о целесообразности.
Ну и ещё дополнительное условие поиска WHERE field_type=? тоже немного замедляет…
Прикаждом изменении таблицы придется также индексы пересчитывать — в маленькой таблице быстрее.
При большом объеме вставок будет лочиться вся таблица и больше запросов будут висеть в оидании разлока — несколько таблиц выгоднее.
любой join с бОльшей таблице будет медленнее чем с маленькой.
В общем — с точки зрения удобства программирования — здорово, с точки зрения производительности — нет, imo.
При большом объеме вставок будет лочиться вся таблица и больше запросов будут висеть в оидании разлока — несколько таблиц выгоднее.
любой join с бОльшей таблице будет медленнее чем с маленькой.
В общем — с точки зрения удобства программирования — здорово, с точки зрения производительности — нет, imo.
пока я пребывал в царстве морфея без меня уже все рассказали :) но дело не только в скорости, а и в объеме. простой пример: одно поле int в таблице с 50000000. а плюс к этому по нему еще *обязательно* нужно будет создавать индекс. Диски и память хоть вещь и резиновая, но все нужно расходовать с умом :)
если у вас проблемы с тем, что индекс не помещается в память — то не нужно этим бравировать вынужденную денормализацию в схеме и предметной области. просто возьмите FEDERATED и пусть этой работой занимается mysql.
у меня вообще нет проблем :)
а если по тексту:
1) slovari.yandex.ru/dict/mikhelson/article/mi11/mi1-0617.htm — как следствие «вынужденной денормализацией»
2) ru.wikipedia.org/wiki/%D0%9D%D0%BE%D1%80%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F_%D1%84%D0%BE%D1%80%D0%BC%D0%B0. А теперь вопрос: какая именно форма нарушена разделением одной огромной таблицы на несколько помельче?
3) К вопросу об помериться скоростью выборки — предложение сделать кубического коня в вакууме, прошу прощения, и не имеет с реальной действительностью практически ничего общего. в реальности одновременно с выборкой конкретно в данную таблицу будет вестись вставка (хоть и с несколько меньшей интенсивностью), кешировать данные этой таблицы можно, но осторожно и не на долго. В контексте ORM к этой таблице будет десяток последовательных обращений. И при этом это будет делаться ну хотя бы раз 200-300 одновременно например в течении часа. наблюдения и опыт показывают, что разница может быть в пару порядков. да и почему сразу разговор про MySQL? Но если упираться в него, то есть еще как минимум шардинг.
4) ну и под конец читаем *внимательно* последнее предложение ;)
а если по тексту:
1) slovari.yandex.ru/dict/mikhelson/article/mi11/mi1-0617.htm — как следствие «вынужденной денормализацией»
2) ru.wikipedia.org/wiki/%D0%9D%D0%BE%D1%80%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F_%D1%84%D0%BE%D1%80%D0%BC%D0%B0. А теперь вопрос: какая именно форма нарушена разделением одной огромной таблицы на несколько помельче?
3) К вопросу об помериться скоростью выборки — предложение сделать кубического коня в вакууме, прошу прощения, и не имеет с реальной действительностью практически ничего общего. в реальности одновременно с выборкой конкретно в данную таблицу будет вестись вставка (хоть и с несколько меньшей интенсивностью), кешировать данные этой таблицы можно, но осторожно и не на долго. В контексте ORM к этой таблице будет десяток последовательных обращений. И при этом это будет делаться ну хотя бы раз 200-300 одновременно например в течении часа. наблюдения и опыт показывают, что разница может быть в пару порядков. да и почему сразу разговор про MySQL? Но если упираться в него, то есть еще как минимум шардинг.
4) ну и под конец читаем *внимательно* последнее предложение ;)
В нескольких проектах поступал так же, при записи\удалении в таблицу рейтинга, триггер обновлял поле рейтинг для конкретной сущности (что бы НЕ суммировать рейтинг при каждой выборке сущности). Пока полет нормальный!
Есть достаточно наивный вариант, чтобы не хранить информацию о типе записи в каждой таблице.
Создадим таблицу Entity (сущности), в которой будем хранить информацию о всех идентификаторах и типах. Дополнительно создадим таблицу EntityType для хранения данных о самом типе (например, имени типа 'name'). Структуру табиц буду записывать в формате: имя_таблицы: (список полей), PK(поле первичного ключа), FK (внешний ключ)
Поле EntityType.id не генерирует значения идентификатора, а ссылается на Entity.id. Т.е. тип это тоже какая-то сущность.
Аналогично — пользователи это тоже сущности.
Пользователем, который запостил статью, может быть не любая абстрактная сущность, а лишь некто из таблицы User:
Продолжим:
В таблице Comment поле item ссылается на абстрактный Entity.id. Поэтому комментировать можно любые сущности: статьи, пользователей, типы,…, какие-то другие сущности, которые появятся в будущем. Аналогично для Vote.
В приципе, на одну и ту же сущность могут ссылаться id из разных таблиц. Для хранения этих данных создадим таблицу Table, где в поле name будем хранить имя табицы. А для связи таблицы таблиц с таблицей сущностей используем отношение многие-к-многим через таблицу Entity2Table:
Таким образом для записи инфомации о новой сущности (допустим User) придется делать следующие вставки: в таблицу Entity, в таблицу User и в таблицу Entity2Table для связи вновь созданного юзера и таблицы User.
Такая структура отображается на соответствующие понятия ООП: Entity => Object, Table => Class, EntityType => DataType, Entity2Table => extends (classes).
зы: сори за сумбурность. это скорее мемори дамп.
Создадим таблицу Entity (сущности), в которой будем хранить информацию о всех идентификаторах и типах. Дополнительно создадим таблицу EntityType для хранения данных о самом типе (например, имени типа 'name'). Структуру табиц буду записывать в формате: имя_таблицы: (список полей), PK(поле первичного ключа), FK (внешний ключ)
Entity: (id:autoincrement, type:int) , PK(id), FK(type -> EntityType.id)
EntityType: (id:int, name:string), PK(id), FK(id -> Entity.id)
Поле EntityType.id не генерирует значения идентификатора, а ссылается на Entity.id. Т.е. тип это тоже какая-то сущность.
Аналогично — пользователи это тоже сущности.
User: (id:int, firstname:string)
, PK(id), FK(id -> Entity.id)
Пользователем, который запостил статью, может быть не любая абстрактная сущность, а лишь некто из таблицы User:
Article: (id:int, user:int)
, PK(id), FK(id -> Entity.id), FK(user -> User.id)
Продолжим:
Comment: (id:int, user:int, item:int)
, PK(id), FK(id -> Entity.id), FK(user -> User.id), FK(item -> Entity.id)
Vote: (id:int, user:int, item:int)
, PK(id), FK(id -> Entity.id), FK(user -> User.id), FK(item -> Entity.id)
В таблице Comment поле item ссылается на абстрактный Entity.id. Поэтому комментировать можно любые сущности: статьи, пользователей, типы,…, какие-то другие сущности, которые появятся в будущем. Аналогично для Vote.
В приципе, на одну и ту же сущность могут ссылаться id из разных таблиц. Для хранения этих данных создадим таблицу Table, где в поле name будем хранить имя табицы. А для связи таблицы таблиц с таблицей сущностей используем отношение многие-к-многим через таблицу Entity2Table:
Entity2Table: (entity:int, class:int)
, PK(entity, class), FK(entity -> Entity.id, ), FK(class -> Table.id)
Table: (int:int, name:string)
, PK(id), FK(id -> Entity.id)
Таким образом для записи инфомации о новой сущности (допустим User) придется делать следующие вставки: в таблицу Entity, в таблицу User и в таблицу Entity2Table для связи вновь созданного юзера и таблицы User.
Такая структура отображается на соответствующие понятия ООП: Entity => Object, Table => Class, EntityType => DataType, Entity2Table => extends (classes).
зы: сори за сумбурность. это скорее мемори дамп.
ну вот и напутал. конечно же связывать нужно не сущности с таблицами, а их типы с таблицами. т.е. Entity2Table это бред. читать:
а extends из ООП extends это EntityType2Table.
В этом случае, для в ставки юзера потребуется лишь два запроса: в Entity и в User.
EntityType2Table: (entity_type:int, table:int)
, PK(entity_type, table), FK(entity_type -> EntityType.id, ), FK(table -> Table.id)
Table: (int:int, name:string)
, PK(id), FK(id -> Entity.id)
а extends из ООП extends это EntityType2Table.
В этом случае, для в ставки юзера потребуется лишь два запроса: в Entity и в User.
funca, есть реляционные модели, а то что вы предложили более походит на объектную модель
а, автору да, пять, несомненное открытие америки
а, автору да, пять, несомненное открытие америки
И не лень же было рисовать схемки в графическом редакторе (явно не предназначенном для составления блок-схем судя по линиям и прочим деталям)
Sign up to leave a comment.
Сущностей много — код один