如何在Rails中推迟数据库更新?

时间:2009-08-18 03:26:16

标签: ruby-on-rails database performance

我正在构建类似于Google Analytics的东西,目前我正在进行实时数据库更新。这是我的应用程序的工作流程:

  1. 用户发出RESTful API请求
  2. 我在数据库中找到一条记录,将其作为JSON
  3. 返回
  4. 我在数据库中记录用户的请求计数器(即如果我用户2 API调用,我将用户的请求计数器增加'2'。
  5. 1和2在SQL中非常快 - 它们是SELECT。 #3真的很慢,因为它是一个更新。在现实世界中,我的数据库(MySQL)没有扩展。根据New Relic的说法,#3占据了大部分时间 - 高达70%!

    我的想法是我需要停止进行同步数据库操作。在短期内,我正在尝试减少数据库写入,所以我正在考虑一个全局哈希(比如在environment.rb中声明),可以从我的控制器和模型中访问,我可以写入,而不是写入D B。我经常可以让任务编写需要写入数据库的更新。

    问题:

    1. 听起来合理吗?任何陷阱?
    2. 我会遇到任何并发问题吗?
    3. 这与将日志写入文件系统并稍后导入相比如何?
    4. 我应该使用一些消息排队系统,比如Starling吗?有什么建议吗?
    5. PS:这是违规查询 - 所有感兴趣的列都被编入索引:

      更新statistics_api设置count_request = COALESCE(count_request,?)+? WHERE(id =?)

3 个答案:

答案 0 :(得分:1)

你的哈希解决方案听起来有点过于复杂。这组幻灯片是一个富有洞察力的最新资源,可以解决您的问题:

他们说最简单的事情是:

Thread.new do
  MyModel.do_long_thing
end

但Ruby mysql驱动程序是阻塞的,因此该线程中的mysql请求仍然可以阻止您的请求。您可以使用mysqlplus作为驱动程序并获取非阻塞请求,但现在我们正在获得一个非常复杂和专业的解决方案。

如果你真的只是想要这个你的请求周期,但可以为它保留服务器的锁定,你可以这样做:

MyController
  after_filter :do_jobs

  def index
    @job = Proc.new{ MyModel.do_long_thing }
  end

private

  def do_jobs
    return unleses @job
    @job.call
  end

end

我将它更多地抽象到ApplicationController中,但你明白了。 proc将更新推迟到请求之后。

如果您认真对待异步和后台进程,则需要查看各种选项,并根据需要做出决定。 Matt Grande推荐使用DelayedJob-这是一个非常受欢迎的选择,但如果您的整个服务器陷入数据库写入困境,我不会建议它。如果这只是一个特别慢的更新,但你的服务器没有过载,那么这可能是一个很好的解决方案。

我目前在最复杂的项目中使用与Starling配合。工作已经相当可扩展,但Starling有点不太理想。 Workling的优势之一是能够交换后端,因此如果Starling成为一个大问题,我们可以将其移除。

如果您的服务器陷入写入困境,则无论您的异步任务方法如何,都需要查看它的扩展。

祝你好运!听起来你的应用程序正在以令人兴奋的速度增长: - )

答案 1 :(得分:0)

稍后使用DelayedJob执行此操作。

编辑:如果您的数据库受到如此大的打击,以至于一个UPDATE显着减慢了您的请求,那么您可能应该考虑设置主从数据库架构。

答案 2 :(得分:0)

我刚刚在EventMachine邮件列表上提出了类似的问题,我建议我尝试使用phat(http://www.mikeperham.com/2010/04/03/introducing-phat-an-asynchronous-rails-app/)来获取异步数据库。

也许你应该尝试一下。