Плюсы / минусы хранения сериализованного хэша и объекта базы данных ключ / значение в ActiveRecord? - PullRequest
16 голосов
/ 13 сентября 2010

Если у меня есть несколько объектов, каждый из которых имеет в основном Profile, что я использую для хранения случайных атрибутов, каковы плюсы и минусы:

  1. Хранение сериализованного хэша в столбце для записи против
  2. Хранение группы объектов ключа / значения, которые belong_to являются основными объектами.

код

Скажем, у вас есть записи об ИППП, подобные этим:

class Building < ActiveRecord::Base
  has_one :profile, :as => :profilable
end
class OfficeBuilding < Building; end
class Home < Building; end
class Restaurant < Building; end

Каждый has_one :profile

Вариант 1. Сериализованный хэш

class SerializedProfile < ActiveRecord::Base
  serialize :settings
end

create_table :profiles, :force => true do |t|
  t.string   :name
  t.string   :website
  t.string   :email
  t.string   :phone
  t.string   :type
  t.text     :settings
  t.integer  :profilable_id
  t.string   :profilable_type
  t.timestamp
end

Вариант 2. Ключ / хранилище значений

class KeyValueProfile < ActiveRecord::Base
  has_many :settings
end

create_table :profiles, :force => true do |t|
  t.string   :name
  t.string   :website
  t.string   :email
  t.string   :phone
  t.string   :type
  t.integer  :profilable_id
  t.string   :profilable_type
  t.timestamp
end

create_table :settings, :force => true do |t|
  t.string   :key
  t.text     :value
  t.integer  :profile_id
  t.string   :profile_type
  t.timestamp
end

Что бы вы выбрали?

Предположим, что в 99% случаев мне не нужно искать по обычаю settings. Просто интересно, каковы компромиссы с точки зрения производительности и вероятности будущих проблем. И число пользовательских settings, вероятно, будет где-то от 10-50.

Я бы предпочел перейти ко второму варианту, с таблицей настроек, поскольку она соответствует объектно-ориентированным соглашениям ActiveRecord. Но мне интересно, если бы в такой ситуации это стоило бы слишком дорого.

Примечание: меня интересует только СУБД. Это было бы идеально подходит для MongoDB / Redis / CouchDB / и т. Д. но я хочу знать только плюсы и минусы с точки зрения SQL.

Ответы [ 3 ]

12 голосов
/ 13 сентября 2010

У меня была та же проблема, но в конце концов я принял решение.

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

Отдельная таблица настроек - это то, что я пытаюсь использовать сейчас - гораздо проще в обслуживании.Я планирую использовать Preferences gem для того, что в основном делает все абстракции для простоты использования.Я еще не уверен, работает ли он с Rails 3 - он небольшой, поэтому я могу расширить его при необходимости.

Обновление ноябрь 2013

Недавно выпущенный Rails 4 поддерживаетзамечательные новые возможности PostgreSQL 9.1+, такие как hstore или json типы столбцов для ваших динамических наборов данных.Вот статья, рассказывающая об использовании hstore в Rails 4 .Оба типа поддерживают индексирование и расширенные возможности запросов (Json с Pg 9.3).Hstore также доступен для пользователей Rails 3 с activerecord-postgres-hstore gem.

Я в процессе переноса некоторых из некритических таблиц предпочтений в моем проекте в hstores.В миграциях я просто обновляю определения таблиц и execute один SQL-запрос на таблицу для перемещения данных.

4 голосов
/ 13 сентября 2010

Я бы порекомендовал просто создать вызов модели Attribute, и у каждого из ваших объектов, которые нуждаются во многих из них, есть has_many.Тогда вам не нужно возиться с сериализацией или чем-то таким хрупким.Если вы используете синтаксис: join, у вас нет реальных проблем с производительностью.

Сериализация данных в вашей СУБД почти всегда неразумна.Это больше, чем просто запросы, речь идет о способности описывать и переносить ваши данные (и сериализация разрушает эту способность).

class Building < ActiveRecord::Base
  has_many :attributes
end

class Attribute < ActiveRecord::Base
   belongs_to :building
end

create_table :attributes, :force => true do |t|
  t.integer :building_id
  t.string :att_name
  t.string :data
  t.timestamp
end
2 голосов
/ 23 декабря 2010

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

Еще один улов, с которым я лично столкнулся при использовании сериализованного хэша, эточто вы должны быть осторожны, чтобы данные, которые вы храните, не превышали то, что может содержать текстовое поле БД.Вы можете легко получить недостающие или поврежденные данные, если не будете осторожны.Например, используя класс и таблицу SerializedProfile, которые вы описали, вы можете вызвать такое поведение:

profile = SerializedProfile.create(:settings=>{})
100.times{ |i| profile.settings[i] = "A value" }
profile.save!
profile.reload
profile.settings.class #=> Hash
profile.settings.size #=> 100

5000.times{ |i| profile.settings[i] = "A value" }
profile.save!
profile.reload
profile.settings.class #=> String
profile.settings.size #=> 65535

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

Для тех из вас, кто хочет использовать Serialized Hash, сделайте это!Я думаю, что в некоторых случаях он может хорошо работать.Я наткнулся на плагин activerecord-attribute-fakers , который кажется мне подходящим.

...