新手问题 请教关于 find_each 的优化使用

pathbox · 2016年02月22日 · 最后由 pathbox 回复于 2016年02月23日 · 1869 次阅读

有一个需求,需要把表中200w左右的数据进行一次过滤。比如:过滤的方法是

def check
  xxx
end

每一条记录都需要调用一次过滤方法 check 不想一次把200w条记录取出来,预测对内存来说会是一个灾难。所以打算使用find_each。 由于过滤操作不是后台异步操作,所以在使用find_each时希望能快一些处理完然后在前台将满足条件的记录马上展示出来。

我先用了一个有200w左右数据的表在rails console中进行测试。

Benchmark.bm do |b|
   b.report("show") {TestOrder.find_each(batch_size: 1000).count}
end

#         user         system       total          real      单位是:秒
#  show   28.340000   1.200000    29.540000    ( 33.036628)

然后我增加了batch_size 到5000

#         user         system       total          real      单位是:秒
#  show   26.870000   0.840000    27.710000    ( 29.737033)

两次使用的时间差不多。batch_size 为 10000时也是差不多。 换成打印测试: batch_size 分别为 1000 和 10000

Benchmark.bm do |b|
   b.report("show") do  
       TestOrder.find_each(batch_size: 1000).each do |p|    
          puts p.id 
        end
    end
end

两次结果大约需要 50s。 不能让用户等20s的时间,所以想请教是否有优化的方法,使find_each的效率更高呢?或者有其他更好的方法(除了异步的操作方法外)

共收到 9 条回复

整体策略有问题,应该在数据库层面做过滤,而不是把数据拉到 Ruby 进程做过滤。 把几百万的数据拉回来过滤,涉及到网络传输,分配大量内存建立对象,难以优化。如果在数据库层面做过滤,配合好的索引,速度有数量级的提升。

自己拼sql过滤。

#2楼 @kungs 也是。还是选择在sql语句层过滤,也许也会复杂些

#4楼 @pathbox Ruby 执行会更慢些

#5楼 @davidwei 如果数据量小,一次读到内存处理还好。不过,sql查是效率最好的。暂时还不知道这个过滤的方法能不能友好的用sql实现

如果过滤方式非常复杂,不适合做sql,可以考虑缓存

赞1楼 Vincent 的回答。

楼主的需求是筛选符合条件的订单吗?

  1. 订单表有200万,我猜大部分数据都是 过期订单/未支付订单,可以考虑写一个定时任务把没用的订单迁移到另外一张表里。未来的各种查询速度都会显著的提升。

  2. 筛选符合条件的订单,可以给常用筛选列建索引在sql 层面过滤;也可以引入额外的搜索引擎(比如 Elasticsearch) 来过滤。

#8楼 @xiaoronglv 谢谢!这是处理过期订单的好方法。 不过这里是筛选有效订单,所以 考虑尝试下 第二个方法

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册