在ActiveRecord中存储序列化哈希与键/值数据库对象的优点/缺点?

时间:2010-09-13 03:05:23

标签: sql ruby-on-rails ruby key-value-store activerecord

如果我有几个基本上都有Profile的对象,我用来存储随机属性的东西,有哪些优点和缺点:

  1. 在列的记录中存储序列化哈希,vs。
  2. 存储一组belong_to主要对象的键/值对象。
  3. 代码

    假设你有这样的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%的时间我不需要按自定义settings进行搜索。只是想知道在性能和未来问题的可能性方面的权衡。自定义settings的数量可能会在10到50之间。

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

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

3 个答案:

答案 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这似乎很合适。