【问题标题】:How to make ActiveRecord query faster on production environment running multiple servers?如何在运行多台服务器的生产环境中使 ActiveRecord 查询更快?
【发布时间】:2019-08-15 07:15:15
【问题描述】:

如何重写 ActiveRecord 查询来解决这个问题?我是一名 python 程序员并使用 sqlalchemy,所以如果可以从 python 的角度解释这将是很棒的。我真的只需要知道如何解决这个问题。

我知道这个问题可能与在每台服务器上打开的连接有关,因此与单服务器暂存环境相比,在生产环境中运行时运行速度较慢。但我不知道如何解决这个问题。我习惯在 python 中编程,我使用 sqlalchemy,它已经针对这种分布式系统进行了优化。

class Board < ActiveRecord::Base
    def messages
        message_ids = Message.where("board_id = ?", self.id).map { |c| c.id }
        message_ids.map { |message_id| Message.find_by_id(message_id) }
    end
end

ActiveRecord 查询经过优化,可在多服务器生产环境和单服务器暂存环境中运行。

【问题讨论】:

    标签: ruby-on-rails performance activerecord distributed-system multi-tier


    【解决方案1】:

    app/models/board.rb

    class Board < ActiveRecord::Base
      has_many :messages
    end
    

    以上将允许您执行以下操作:

    board = Board.first
    board.messages
    # => returns a `ActiveRecord::Associations::CollectionProxy` object
    #    for beginners, simply treat `ActiveRecord::Associations::CollectionProxy` as
    #    if that's an Array object. So you can still do something like
    
    board.messages.first
    # => #<Message id: 123, ...>
    
    • 现在您还需要在下面添加(但不需要回答您的问题)

    app/models/message.rb

    class Message < ActiveRecord::Base
      belongs_to :board
    end
    

    ...这将允许您这样做:

    message = Message.first
    message.board
    # => #<Board id: 456, ...>
    

    Rails association docs here

    特别要注意“隐含约定”,例如隐含的 foreign_keyclass_name

    【讨论】:

    • 只是对您的“我知道问题可能与在每台服务器上打开的连接有关”的快速评论。不,它不会打开 [并关闭] 每个 SQL 语句的连接。 Rails 默认使用ConnectionPool(除非您想要自定义行为或只是好奇,否则您不会真正关心它)。请参阅this SO 了解更多信息。
    • 如果您故意不想定义关联,而只是故意只想定义messages 方法,请告诉我,我会更新我的答案。虽然我建议为此使用关联,因为 Rails 会自动缓存对象,并且您可以执行更强大和更复杂的关联查询,就像 JOINS 一样,无需编写大量代码的复杂性。
    • 那么为什么原始代码在分布式系统上的运行速度会比在具有更高 CPU/RAM 的单个服务器上运行得更慢,如果它没有在多个服务器上打开连接?我想我想知道的是优化后的代码与原始代码的运行方式有何不同,以及它在多服务器环境中的表现如何。
    • 我不知道你在分布式系统和单服务器之间运行它的个人体验之间的变量,所以很遗憾,我无法回答这个问题。我只是想直接回答你的问题,尽管 Rails 有一个连接池,默认情况下它不会关闭连接; Rails 只是在尚未使用时将它们放回池中。但是,如果我不得不猜测,是的,如果一次只有一个请求,分布式系统也可能比生产系统慢,尽管分布式系统可能比单服务器系统具有可扩展性
    • 回答您的问题“我想我想知道的是优化后的代码与原始代码的运行方式有何不同”,您的原始代码正在执行 N+1 多个 SQL 查询,因为您先执行一个"SELECT * FROM messages where board_id = ?",然后你有n次做一个SELECT * FROM messages WHERE id = ? LIMIT 1。我的代码将改为执行一个 JOIN SQL 语句:SELECT * FROM messages INNER JOIN boards ON messages.board_id = boards.id
    【解决方案2】:

    如果我没记错的话,您要做的是为特定的board 选择all messages

    如果正确,您可以这样做:

    def messages
      Message.where(board_id: self.id)
    end
    
    

    这将检索特定板的所有消息。

    现在,您可以为板子调用此方法,例如:

    board = Board.first
    board.messages
    

    有关更多信息,请查看此参考:https://guides.rubyonrails.org/active_record_querying.html

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 2019-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-28
      • 2023-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多