如果我有几个基本上都有Profile
的对象,我用来存储随机属性的东西,有哪些优点和缺点:
belong_to
主要对象的键/值对象。假设你有这样的STI记录:
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
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
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面向对象的约定。但我想知道在这种情况下是否会产生太高的性能成本。
注意:我只想知道RDBMS。这将非常适合MongoDB / Redis / CouchDB /等。但我想纯粹了解SQL的优点和缺点。
答案 0 :(得分:12)
我遇到了同样的问题,但终于做出了决定。
哈希序列化选项会导致维护问题。很难查询,扩展或重构这些数据 - 任何微妙的变化都需要迁移,这意味着读取每个记录反序列化和序列化,并且取决于重构序列化异常可能发生。 我尝试了二进制序列化和JSON - 第二个更容易提取和修复,但仍然太麻烦。
我正在尝试使用单独的设置表 - 更容易维护。我计划使用Preferences gem来实现所有抽象,以便于使用。我不确定它是否适用于Rails 3 - 它很小,所以我可以根据需要扩展它。
2013年11月更新
最近发布的Rails 4支持PostgreSQL 9.1+的强大新功能,例如动态数据集的 hstore 或 json 列类型。这是一篇涵盖hstore usage in Rails 4的文章。两种类型都支持索引和高级查询功能(Json与Pg 9.3)。使用activerecord-postgres-hstore gem的Rails 3用户也可以使用Hstore。
我正在将项目中的一些非关键偏好表迁移到hstores。在迁移中,我只更新表定义,并在每个表中execute
一个SQL查询来移动数据。
答案 1 :(得分:4)
我建议只创建一个模型调用属性,并让每个需要其中许多对象的对象具有has_many。然后你不必乱搞序列化或类似的任何脆弱。如果您使用:join语法,则不会出现任何实际性能问题。
将数据序列化到RDBMS几乎总是不明智的。它不仅仅是关于查询,而是关于描述和迁移数据的能力(以及序列化破坏了这种能力)。
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 :(得分:2)
由于其他人提到的潜在维护优势,我面临着您所描述的相同困境并最终使用键/值表实现。在未来的迁移中,我可以更容易地思考如何在数据库的不同行中选择和更新信息,而不是单个序列化的哈希。
我在使用序列化哈希时亲身经历的另一个问题是,您必须小心,您存储的序列化数据并不比数据库文本字段所能容纳的数据大。如果您不小心,您可以轻松地获得丢失或损坏的数据。例如,使用SerializedProfile类&amp;你描述的表,你可能会导致这种行为:
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无法重新序列化。
对于那些想要使用序列化哈希的人,请选择它!我认为它有可能在某些情况下运作良好。我偶然发现了activerecord-attribute-fakers plugin这似乎很合适。