以下是应优化的代码:
def statistics
blogs = Blog.where(id: params[:ids])
results = blogs.map do |blog|
{
id: blog.id,
comment_count: blog.blog_comments.select("DISTINCT user_id").count
}
end
render json: results.to_json
end
每个SQL查询的成本大约为200毫秒。如果我有10篇博文,这个函数需要2s,因为它同步运行。我可以使用GROUP BY
来优化查询,但我先把它放在一边,因为任务可能是第三方请求,我对Ruby如何处理异步感兴趣。
在Javascript中,当我想调度多个异步工作并等待所有这些工作解析时,我可以使用Promise.all()
。我想知道Ruby语言的替代方案是什么来解决这个问题。
这个案例我需要一个帖子吗?在Ruby中这样做是否安全?
答案 0 :(得分:3)
在ruby中有多种方法可以解决这个问题,包括承诺(由宝石启用)。
JavaScript使用事件循环和事件驱动的I / O完成异步执行。有一些事件库可以在ruby中完成同样的事情。其中最受欢迎的是eventmachine
。
正如您所提到的,线程也可以解决此问题。线程安全是一个很大的主题,并且由于不同风格的红宝石(MRI,JRuby等)中的不同线程模型而进一步复杂化。总而言之,我只是说当然可以安全地使用线程......有时很难。但是,当与阻塞I / O(如API或数据库请求)一起使用时,线程可能非常有用并且相当直接。带线程的解决方案可能如下所示:
# run blocking IO requests simultaneously
thread_pool = [
Thread.new { execute_sql_1 },
Thread.new { execute_sql_2 },
Thread.new { execute_sql_3 },
# ...
]
# wait for the slowest one to finish
thread_pool.each(&:join)
您还可以访问其他货币模型,例如演员模型,异步类,承诺以及由concurrent-ruby
等宝石启用的其他货币模型。
最后,ruby并发可以采用通过内置机制(drb,套接字等)或通过分布式消息代理(redis,rabbitmq等)进行通信的多个进程的形式。
答案 1 :(得分:1)
当然只需在一次数据库调用中进行计数:
blogs = Blog
.select('blogs.id, COUNT(DISTINCT blog_comments.user_id) AS comment_count')
.joins('LEFT JOIN blog_comments ON blog_comments.blog_id = blogs.id')
.where(comments: { id: params[:ids] })
.group('blogs.id')
results = blogs.map do |blog|
{ id: blog.id, comment_count: blog.comment_count }
end
render json: results.to_json
您可能需要更改语句,具体取决于您在数据库中命名的表的方式,因为我只是猜到了您的关联名称。
答案 2 :(得分:1)
您有一个数据列表List
,并希望异步操作该数据。假设列表中的所有条目的操作相同,则可以执行以下操作:
data
data = [1, 2, 3, 4] # Example data
operation = -> (data_entry) { data * 2 } # Our operation: multiply by two
results = data.map{ |e| Thread.new(e, &operation) }.map{ |t| t.value }
这可以是从数据库ID到URI的任何内容。这里使用数字来简化。
data = [1, 2, 3, 4]
定义一个lambda,它接受一个参数并对其进行一些计算。这可能是API调用,SQL查询或需要一些时间才能完成的任何其他操作。同样,为简单起见,我只是将数字乘以2。
operation = -> (data_entry) { data * 2 }
此数组将包含所有异步操作的结果。
results =
对于数据集中的每个条目,生成一个运行data.map{ |e| Thread.new(e, &operation) }...
的线程并将该条目作为参数传递。这是lambda中的operation
参数。
data_entry
从每个线程中提取值。这将等待线程首先完成,因此在此行结束时,所有数据都将存在。
如果传入错误数量的参数,Lambdas实际上只是引发错误的美化块。语法...map{ |t| t.value }
只是-> (arguments) {code}
的语法糖。
当方法接受Lambda.new { |arguments| code }
之类的块时,您也可以传递前缀为Thread.new { do_async_stuff_here }
的Lambda或Proc对象,并且它将以相同的方式处理。