17

如果我有几个对象,每个对象基本上都有一个Profile,我用来存储随机属性的对象是什么,优缺点是什么:

  1. 将序列化的哈希存储在记录的列中,与
  2. 存储一堆作为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

选项 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% 的时间我不需要按 custom 搜索settings。只是想知道在性能和未来问题的可能性方面的权衡是什么。定制的数量settings可能在 10 到 50 之间。

我宁愿选择第二个选项,即设置表,因为它遵循 ActiveRecord 面向对象的约定。但我想知道在这种情况下是否会带来过高的性能成本。

注意:我只是想知道 RDBMS。这将非常适合 MongoDB/Redis/CouchDB/等。但我想纯粹了解 SQL 的优缺点。

4

3 回答 3

13

我有同样的问题,但最终做出了决定。

哈希序列化选项会导致维护问题。很难查询、扩展或重构这些数据 - 任何细微的变化都需要迁移,这意味着读取每条记录反序列化和序列化回来,并且根据重构可能会发生序列化异常。我尝试了二进制序列化和 JSON - 第二种更容易提取和修复,但仍然太麻烦。

单独的设置表是我现在尝试使用的 - 更容易维护。我计划使用Preferences gem 来完成所有抽象以便于使用。我不确定它是否适用于 Rails 3 - 它很小,所以如果需要我可以扩展它。

2013 年 11 月更新

最近发布的 Rails 4 支持 PostgreSQL 9.1+ 的强大新功能,例如动态数据集的hstorejson列类型。这是一篇介绍Rails 4 中 hstore 使用的文章。两种类型都支持索引和高级查询功能(带有 Pg 9.3 的 Json)。使用activerecord-postgres-hstore gem的Rails 3 用户也可以使用Hstore。

我正在将项目中的一些非关键首选项表迁移到 hstores。在迁移中,我只更新表定义和execute每个表的一个 SQL 查询来移动数据。

于 2010-09-13T15:13:27.437 回答
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
于 2010-09-13T03:33:52.830 回答
2

由于其他人提到的潜在维护优势,我面临着您描述的相同困境并最终选择了键/值表实现。与单个序列化哈希相比,在未来的迁移中,更容易思考如何在数据库的不同行中选择和更新信息。

我在使用序列化哈希时亲身经历的另一个问题是,您必须小心,您存储的序列化数据不大于 DB 文本字段可以容纳的数据。如果您不小心,您很容易以丢失或损坏的数据告终。例如,使用您描述的 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 将无法重新序列化它。

对于那些想要使用序列化哈希的人,去吧!我认为它有可能在某些情况下运作良好。我偶然发现了似乎很合适的activerecord-attribute-fakers 插件。

于 2010-12-23T00:27:33.943 回答