20

ActiveRecord を置き換える Ruby ORM を探しています。Sequel と DataMapper を見てきました。それらは非常によく見えますが、必要のないときにすべてをメモリにロードしないという基本的なことを行っているようには見えません。

つまり、多くの行を持つテーブルの ActiveRecord と Sequel で次の (または同等の) ことを試しました。

 posts.each { |p| puts p }

二人とも記憶に夢中です。必要に応じてフェッチするのではなく、すべてをメモリにロードするようです。ActiveRecord でを使用しfind_in_batchesましたが、受け入れられる解決策ではありません。

  1. 問題が多すぎるため、ActiveRecord は受け入れられるソリューションではありません。
  2. コードでページング メカニズムを認識する必要があるのはなぜですか? ページのサイズをどこかに設定できてうれしいですが、それだけです。次のようなことfind_in_batchesをする必要があります。

    post.find_in_batches { |バッチ| batch.each { |p| p を置きます } }

しかし、それは透明であるべきです。

では、フェッチを適切に行う信頼できる Ruby ORM はどこかにありますか?


アップデート:

Sergioが述べたように、Rails 3では、find_eachまさに私が望むものを使用できます。ただし、ActiveRecord はオプションではないため、誰かが本当にそれを使用するよう説得できる場合を除き、質問は次のとおりです。

  1. find_each と同等の機能をサポートする ORM はどれですか?
  2. どうやってするの?
  3. find_eachwhileが必要なのはなぜ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

たぶん、 Redis NoSQL ストアに基づくOhmを考慮することができます。

于 2012-01-13T14:56:41.633 に答える