Rails Rails 里面用 Thread 并行查询的可能性

huacnlee · 2015年05月24日 · 最后由 hastings 回复于 2021年03月18日 · 3295 次阅读

今天尝试了一下用 Thread 并行查询,发现有效果的

示例代码

我在 Ruby China 的代码里面找了个无法命中索引的 MongoDB 查询场景(平均每个 105ms 左右),创建 5 个线程来执行。 大家都知道 Ruby 存在 GIL (Python 也是),但 GIL 不会影响到 I/O (数据库查询,文件读写,网络请求) 的动作。

def index
  threads = []
  5.times.each do 
    threads << Thread.new do
      @total_count = Topic.where(:_id.ne => Time.now.to_i).count
    end
  end

  threads.each(&:join)
  render text: "ok"
end

查询日志

Started GET "/topics" for 127.0.0.1 at 2015-05-24 02:39:46 +0800
Processing by TopicsController#index as HTML
  MOPED: command={:count=>"topics", :query=>{"_id"=>{"$ne"=>1432406386}}} runtime: 103.6620ms
  MOPED: command={:count=>"topics", :query=>{"_id"=>{"$ne"=>1432406386}}} runtime: 105.8040ms
  MOPED: command={:count=>"topics", :query=>{"_id"=>{"$ne"=>1432406386}}} runtime: 104.6600ms
  MOPED: command={:count=>"topics", :query=>{"_id"=>{"$ne"=>1432406386}}} runtime: 105.9680ms
  MOPED: command={:count=>"topics", :query=>{"_id"=>{"$ne"=>1432406386}}} runtime: 107.4280ms
  Rendered text template (0.1ms)
Completed 200 OK in 116ms (Views: 0.6ms)

总耗时 126ms, 如果是顺序执行的话,应该是 105 * 5 = 525ms 看起来,如果场景允许并行查询的话,这么做是有意义的。


顺便提一下,这里 Thread 的并发数量和 Puma 的 thread 数量配置无关,我尝试了用 Unicorn 或 Puma -t 0:1 启动,效果依然和上面一样。

延伸阅读

Puma 的 Thread 数量配置应该是是上层请求的线程数量,就是一个线程处理一个请求,这个请求处理上面的 action 所以这个 action 的处理的时间不与配置数量相关

我的理解 puma 应该不能控制 action 里自己并发出多少个线程,action 生产的子线程归自己管理

不过用 pg 的朋友不要尝试使用上面的用法 ,不然主进程会僵死,好像和 pg 的 gem 有关,这个我在生产中遇到过.....除非我打开的姿势不对

数据库压力不大时这种并发查询可以提升效率 但是如果数据库本身压力就比较大的情况下 反倒会减慢

用 HBase 这种数据库来并发查询会更好一些(典型场景是查询大量日志),其他数据库我还是倾向合并为一次查询

azhao 回复

老铁,pg 用 thread 僵死的问题最终解决了吗

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