有没有使用游标或智能提取的Ruby ORM?

时间:2012-01-13 10:47:17

标签: ruby orm datamapper sequel

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

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

 posts.each { |p| puts p }

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

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

    post.find_in_batches {| batch | batch.each {| p |把p}}

  3. 但这应该是透明的。

    那么有一个可靠的Ruby ORM可以正确获取吗?


    更新

    正如塞尔吉奥所说,在Rails 3中你可以使用find_each,这正是我想要的。但是,由于ActiveRecord不是一个选项,除非有人真的可以说服我使用它,问题是:

    1. 哪些ORM支持相当于find_each?
    2. 怎么做?
    3. 为什么我们需要find_each,而find应该这样做,不应该吗?

5 个答案:

答案 0 :(得分:43)

Sequel的Dataset#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

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

答案 1 :(得分:4)

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

答案 2 :(得分:3)

ActiveRecord实际上有一个几乎透明的batch mode

User.find_each do |user|
  NewsLetter.weekly_deliver(user)
end

答案 3 :(得分: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

答案 4 :(得分:-2)

也许您可以考虑Ohm,这是基于Redis NoSQL商店。