find_each批量获取数据

作者:周星 发布:2017-10-07

       我们经常需要先读取大量的数据然后进行一些操作,比如对取出的每个元素做某些操作,或者导出数据等。通常情况下一般人会这么做:直接使用一个 each 循环整个数据库表

# This is very inefficient when the users table has thousands of rows.
User.all.each do |user|
  NewsLetter.weekly_deliver(user)
end

       在项目初期这种做法可能没什么问题,但是随着我们数据库数据的增大,这个方法会显得很不切实际,因为 User.all.each 会一次取出整个表,并为每一条记录创建一个对象,然后把这个对象数组保存在内存中,事实上,如果记录数过大,这种处理很有可能会使内存溢出。

       Rails提供了两种对内存友好的方法来处理这个问题,第一种方法是:find_each,它会取出一批数据然后对每个数据执行block,每个数据都会被视为一个独立的model;第二种方法是:find_in_batches,它会取出一批数据,把这一批数据看做一个对象数组,然后对整个数组执行 block。

       友情提示: find_each 和 find_in_batches 方法只适用于批处理那些数据量大到有可能会读取一次会超出内存的数据,如果你仅仅想循环上千条数据,那么普通的方法可能是更好的选择。

The find_each and find_in_batches methods are intended for use in the batch processing of a large number of records that wouldn’t fit in memory all at once. If you just need to loop over a thousand records the regular find methods are the preferred option.

1. find_each

       下面的例子中,find_each 会每次取出1000条数据(find_each 和 find_in_batch 方法默认值),然后对每一个数据进行操作,这个过程会一直持续,直到所有的数据都被处理了。

User.find_each do |user|
  puts user.id
end

find_each方法接受大多数适用于普通 find 方法的参数,除了 order 和 limit,因为他们作为保留字被 find_each方法内部使用了。两个新增的参数, :batch_size 和 :start

:batch_size 允许你指定每次取出多少条数据,比如每次取出5000条:

User.find_each(:batch_size => 5000) do |user|
  puts user.id
end

:start 参数允许你指定从哪个ID开始,这个参数非常有用,比如你想恢复一个挂掉的批处理进程。

User.find_each(:start => 2000, :batch_size => 5000) do |user|
  puts user.id
end

       还有一个可能的例子是:你想多进程处理一个队列,你可以通过设置适当的 :start 参数来达到这一目的,比如让每个进程处理10000条数据。

小提示:你可以使用 :include 参数来解决 N+1

2. find_in_batches

       find_in_batches 方法和 find_each 很相似,都是批量读取数据,不同之处是 find_in_batches 把取出的数据作为一个数组,下面的例子 find_in_batches 取出1000条数据为一个数组,然后对这个数组执行 count 方法

# Give add_invoices an array of 1000 invoices at a time
Invoice.find_in_batches(:include => :invoice_lines) do |invoices|
  puts invoices.count
end

3. 结合distinct

       如果你需要结合 distinct 和 find_each,必须要注意一点:做 distinct 查询的时候要加上主键,否则会报错,查找了一下资料,应该是因为排序问题,如果你知道具体原因,请联系我。

4. 总结

       如果你需要读取大量数据,就应该考虑这两个方法了,不要忘了那三个常用的参数,:batch_size, :start 和 :include

支付宝扫码赞助博主


评论(0)