20

我正在寻找一个 Ruby ORM 来替换 ActiveRecord。我一直在看 Sequel 和 DataMapper。它们看起来相当不错,但它们似乎都没有做基本的事情:当你不需要时,不要将所有内容加载到内存中。

我的意思是我已经在 ActiveRecord 和 Sequel 上尝试了以下(或等效),并且有很多行:

 posts.each { |p| puts p }

他们俩都在记忆中发疯。他们似乎将所有内容加载到内存中,而不是在需要时获取内容。我find_in_batches在 ActiveRecord 中使用了,但这不是一个可接受的解决方案:

  1. ActiveRecord 不是一个可接受的解决方案,因为我们遇到了太多问题。
  2. 为什么我的代码应该知道分页机制?我很高兴在某处配置页面大小,仅此而已。您find_in_batches需要执行以下操作:

    post.find_in_batches { |批次| 批处理每个 { |p| 放 p } }

但这应该是透明的。

那么是否有可靠的 Ruby ORM 可以正确提取?


更新:

正如 Sergio 所提到的,在 Rails 3 中,您可以使用find_each我想要的。然而,由于 ActiveRecord 不是一个选项,除非有人真的能说服我使用它,问题是:

  1. 哪些 ORM 支持 find_each 的等价物?
  2. 怎么做?
  3. 为什么我们需要一个find_each,而find应该这样做,不应该吗?
4

5 回答 5

44

SequelDataset#each确实一次产生单独的行,但大多数数据库驱动程序会首先将整个结果加载到内存中。

如果你使用的是 Sequel 的 Postgres 适配器,你可以选择使用真正的游标:

posts.use_cursor.each{|p| puts p}

默认情况下一次获取 1000 行,但您可以使用一个选项来指定每次游标获取要获取的行数:

posts.use_cursor(:rows_per_fetch=>100).each{|p| puts p}

如果你没有使用 Sequel 的 Postgres 适配器,你可以使用 Sequel 的分页扩展:

Sequel.extension :pagination
posts.order(:id).each_page(1000){|ds| ds.each{|p| puts p}}

但是,与 ActiveRecord 的find_in_batches/一样find_each,它会执行单独的查询,因此如果您正在检索的数据集有并发修改,则需要小心。

这不是 Sequel 中的默认值的原因可能与它不是 ActiveRecord 中的默认值的原因相同,即在一般情况下它不是一个好的默认值。只有具有大结果集的查询才真正需要担心它,并且大多数查询不会返回大结果集。

至少在 Postgres 适配器光标支持的情况下,将其设置为模型的默认值相当容易:

Post.dataset = Post.dataset.use_cursor

对于分页扩展,你不能真正做到这一点,但你可以将它包装在一个使其大部分透明的方法中。

于 2012-01-13T16:52:55.870 回答
5
Sequel.extension :pagination
posts.order(:id).each_page(1000) do |ds|
  ds.each { |p| puts p }
end

在大桌子上非常慢!

很明显,查看了方法主体: http ://sequel.rubyforge.org/rdoc-plugins/classes/Sequel/Dataset.html#method-i-paginate

# File lib/sequel/extensions/pagination.rb, line 11

def paginate(page_no, page_size, record_count=nil)
  raise(Error, "You cannot paginate a dataset that already has a limit") if @opts[:limit]
  paginated = limit(page_size, (page_no - 1) * page_size)
  paginated.extend(Pagination)
  paginated.set_pagination_info(page_no, page_size, record_count || count)
end
于 2012-02-13T07:32:18.940 回答
3

ActiveRecord 实际上有一个几乎透明的批处理模式

User.find_each do |user|
  NewsLetter.weekly_deliver(user)
end
于 2012-01-13T10:49:33.383 回答
2

此代码比 ActiveRecord 中的 find_in_batches 运行得更快

id_max = table.get(:max[:id])
id_min = table.get(:min[:id])
n=1000
(0..(id_max-id_min)/n).map.each do |i|
    table.filter(:id >= id_min+n*i, :id < id_min+n*(i+1)).each {|row|}
end
于 2012-02-13T07:38:18.760 回答
-2

也许您可以考虑Ohm,它基于Redis NoSQL 存储。

于 2012-01-13T14:56:41.633 回答