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

发布于 2024-09-18 10:49:19 字数 1608 浏览 5 评论 0原文

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

  1. 在记录的列中存储序列化哈希,与在列中存储序列化哈希。
  2. 存储一堆属于主对象的键/值对象。

代码

假设您有如下 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% 的情况下我不需要通过自定义设置进行搜索。只是想知道在性能和未来出现问题的可能性方面有何权衡。自定义设置的数量可能在 10-50 之间。

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

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

If I have several objects that each have basically a Profile, what I'm using to store random attributes, what are the pros and cons of:

  1. Storing a serialized hash in a column for a record, vs.
  2. Storing a bunch of key/value objects that belong_to the main object.

Code

Say you have STI records like these:

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

Each has_one :profile

Option 1. Serialized Hash

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

Option 2. Key/Value Store

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

Which would you choose?

Assume that 99% of the time I won't need to search by the custom settings. Just wondering what the tradeoffs are in terms of performance and the likelihood of future problems. And the number of custom settings will likely be anywhere from 10-50.

I would rather go with the second option, with the settings table, because it follows the ActiveRecord object-oriented conventions. But I'm wondering if in this kind of situation that would come at too high a performance cost.

Note: I am wondering in terms of RDBMS only. This would be a perfect fit for MongoDB/Redis/CouchDB/etc. but I want to know purely the pros and cons in terms of SQL.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

云仙小弟 2024-09-25 10:49:19

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

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

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

2013 年 11 月更新

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

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

I had the same problem, but finally made the decision.

Hash serialization option makes maintenance problem. It is hard to query, extend or refactor such data - any subtle change needs migration which means reading each record deserializing and serializing back, and depending on refactoring serialization exception may happen.
I tried both binary serialization and JSON - the second is easier to extract and fix but still too much hassle.

Separate settings table is what I'm trying to use now - much easier to maintain. I plan to use Preferences gem for that which mostly does all abstraction for easy use. I'm not sure if it works with Rails 3 yet - it is small so I can extend it if needed.

Update Nov 2013

Recently released Rails 4 supports great new features of PostgreSQL 9.1+ such as hstore or json column types for your dynamic data sets. Here is an article covering hstore usage in Rails 4. Both types support indexing and advanced querying capabilities (Json with Pg 9.3). Hstore is also available to Rails 3 users with activerecord-postgres-hstore gem.

I am in the process of migrating some of non critical preference tables in my project to hstores. In migrations I just update table definitions and execute one SQL query per table to move the data.

静谧 2024-09-25 10:49:19

我建议只创建一个模型调用属性,并让每个需要许多的对象都有 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

I would recomend just creating a model call Attribute and have each of your objects that need many of them has_many. Then you don't have to mess around with serialization or anything brittle like that. If you use the :join syntax you don't have any real performance issues with this.

Serializing data into your RDBMS is almost always unwise. It's more than about queries, it's about the ability to describe and migrate your data (and serialization shatters that ability).

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
何处潇湘 2024-09-25 10:49:19

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

我个人在使用序列化哈希时遇到的另一个问题是,您必须小心存储的序列化数据不能大于数据库文本字段可以容纳的数据。如果不小心,很容易导致数据丢失或损坏。例如,使用 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 插件,它看起来很合适。

I was facing the same dilemma you described and ended up going with the key/value table implementation because of the potential maintenance advantages that others mentioned. It's just easier to think through how I could select and update information in separate rows of the database in a future migration as opposed to a single serialized Hash.

Another catch I've personally experienced when using a serialized Hash is that you have to be careful that the serialized data you're storing isn't larger than what the DB text field can hold. You can easily end up with missing or corrupted data if you aren't careful. For example, using the SerializedProfile class & table you described, you could cause this behavior:

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

All that code to say, be aware of your DB limits or your serialized data will be clipped the next time it's retrieved and ActiveRecord won't be able to re-serialize it.

For those of you that do want to use a Serialized Hash, go for it! I think it has potential to work well in some cases. I stumbled across the activerecord-attribute-fakers plugin which seems like a good fit.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文