В большинстве руководств по Rails, которые мне попадались в руки, в примерах по полиморфным связям есть интересная особенность выбора типа для этих связей, о которой и пойдет речь в этом посте.
В Rails полиморфными считаются связи, устанавливаемые между объектами разных типов. Предполагается, что все эти объекты разделяют некоторые общие характеристики, но имеют различные качественные представления. Полиморфные связи — один из способов реализации отношения супертип-подтип.
Рассмотрим три модели:
Такая схема требует наличие полей resource_type и resource_id для модели Comment, и в руководствах по Rails стандартно определяют resource_type как VARCHAR (255). Долгое время я не обращал на это никакого внимания, однако на днях подумал, а почему бы не сделать это поле типа Enum ('Post','Image') NOT NULL, ведь при известном количестве моделей, участвующих в полиморфной связи это не должно вызывать никаких проблем и работает быстрее.
Чтобы убедиться в выгоде использования ENUM я провел маленький опыт и сделал два приложения на Rails с указанными выше моделями. Для каждого приложения специальным скриптом сформировал два одинаковых набора данных следующим образом: сгенерировал по 1000 постов и картинок, затем 1 000 000 комментариев случайным образом так чтобы каждый создаваемый комментарий был случайно сопоставлен со случайным постом или картинкой. Этим я хотел добиться расположения комментариев вразнобой, хотя и был введен составной индекс (resource_type, resource_id).
После генерации данных провёл контрольные выборки для постов c id 100,200, …,1000 и результаты эксперимента привожу в табличке. Испытания проводились на MySQL версии 5.1.37–1ubuntu5
Небольшое пояснение к таблице:
Т1 — таблица с resource_type VARCHAR (10) NOT NULL.
Т2 — таблица с resource_type ENUM ('Post','Image') NOT NULL.
Колонки 3 и 5 содержат время исполнения запроса вида
SELECT * FROM `comments` WHERE resource_type='Post' AND resource_id=N1;.
Колонки 4 и 6 содержат время исполнения запроса вида
SELECT * FROM `posts` INNER JOIN comments ON `comments`.resource_type='Post' AND `comments`.resource_id=`posts`.id WHERE `posts`.id=N1.
N — номер опыта, N1=N*100.
После этого я считаю, что имеет смысл использовать ENUM поля для полиморфных связей. Но пока что мне не ясно, какие есть подводные камни? Какие есть у вас соображения насчет полиморфных связей и их использования в народном хозяйстве:)?
В Rails полиморфными считаются связи, устанавливаемые между объектами разных типов. Предполагается, что все эти объекты разделяют некоторые общие характеристики, но имеют различные качественные представления. Полиморфные связи — один из способов реализации отношения супертип-подтип.
Рассмотрим три модели:
- class Post < ActiveRecord::Base
- has_many :comments, :as => :resource
- end
- class Image < ActiveRecord::Base
- has_many :comments, :as => :resource
- end
- class Comment < ActiveRecord::Base
- belongs_to :resource, :polymorphic => true
- end
Такая схема требует наличие полей resource_type и resource_id для модели Comment, и в руководствах по Rails стандартно определяют resource_type как VARCHAR (255). Долгое время я не обращал на это никакого внимания, однако на днях подумал, а почему бы не сделать это поле типа Enum ('Post','Image') NOT NULL, ведь при известном количестве моделей, участвующих в полиморфной связи это не должно вызывать никаких проблем и работает быстрее.
Чтобы убедиться в выгоде использования ENUM я провел маленький опыт и сделал два приложения на Rails с указанными выше моделями. Для каждого приложения специальным скриптом сформировал два одинаковых набора данных следующим образом: сгенерировал по 1000 постов и картинок, затем 1 000 000 комментариев случайным образом так чтобы каждый создаваемый комментарий был случайно сопоставлен со случайным постом или картинкой. Этим я хотел добиться расположения комментариев вразнобой, хотя и был введен составной индекс (resource_type, resource_id).
После генерации данных провёл контрольные выборки для постов c id 100,200, …,1000 и результаты эксперимента привожу в табличке. Испытания проводились на MySQL версии 5.1.37–1ubuntu5
Небольшое пояснение к таблице:
Т1 — таблица с resource_type VARCHAR (10) NOT NULL.
Т2 — таблица с resource_type ENUM ('Post','Image') NOT NULL.
Колонки 3 и 5 содержат время исполнения запроса вида
SELECT * FROM `comments` WHERE resource_type='Post' AND resource_id=N1;.
Колонки 4 и 6 содержат время исполнения запроса вида
SELECT * FROM `posts` INNER JOIN comments ON `comments`.resource_type='Post' AND `comments`.resource_id=`posts`.id WHERE `posts`.id=N1.
N — номер опыта, N1=N*100.
После этого я считаю, что имеет смысл использовать ENUM поля для полиморфных связей. Но пока что мне не ясно, какие есть подводные камни? Какие есть у вас соображения насчет полиморфных связей и их использования в народном хозяйстве:)?