【问题标题】:Minimizing calls to database in rails在 Rails 中最小化对数据库的调用
【发布时间】:2010-05-22 17:04:07
【问题描述】:

我熟悉 memcached 和渴望加载,但似乎都无法解决我面临的问题。

我的主要性能滞后来自数据库的数百个数据检索调用。棘手的是,在我完成几个计算步骤之前,我不知道我需要检索哪组用户。

我可以重构我的代码,但我想知道你们专家如何处理这种情况?我认为这应该是一个相当普遍的情况

def newsfeed

  - find out which users i need
  - retrieve those users via DB

  - find out which events happened for these users
  - for each of those events
        - retrieve new set of users

  - find out which groups are relevant
  - for each of those groups
        - retrieve new set of users 

  - etc, etc 

end

【问题讨论】:

    标签: ruby-on-rails ruby performance memcached


    【解决方案1】:

    非规范化是您的情况的神奇密码。

    有几种方法可以做到这一点: 例如,将最后 10 个用户的 id 存储在事件和组中。

    或者创建一个新模型NewsFeedItem (belongs_to :parent, :polymorphic => true)。当用户参加活动时,创建一个带有非规范化信息的 NewsFeedItem,例如此用户名、他的个人资料图片等。将您从对 user_events 和用户的第二次查询中拯救出来。

    【讨论】:

    • 我没有更好的答案,而且我以前从未这样做过,但这种技术不会产生相当多的重复数据吗?也许我错过了一些东西,或者收益超过了成本。
    • 非规范化通常(总是?)意味着您不再以关于更新空间和时间的最佳方式存储数据,而是以一种使您的查询更快的方式存储数据。如果查询主导更新,那么这可能是一个净赢。
    • 这很酷,谢谢!你教给我们一个新术语 -> 非规范化。 ;) 问题:为什么这个用例需要 polymorphic=> true?
    • 您需要多态性,因为父级可以是一个组或一个事件或其他任何东西。在视图中,您可以为每种可能的新闻项目类型使用不同的部分。
    【解决方案2】:

    您应该能够通过每个事件/组循环只有一个查询来做到这一点。您要做的是:在您的 for 循环中将用户 id 添加到 Set,然后在 for 循环之后检索具有这些 id 的所有用户记录。冲洗并重复。这是一个例子:

    def newsfeed
    
      user_ids = Set.new
      # find out which users i need
      ...  add ids to user_ids
      # retrieve those users via DB
      users = User.find(user_ids.to_a)
    
      # find out which events happened for these users
      # you might want to add a condition
      # that limits then events returned to only recent ones
      events = Event.find_by_user_id(user_ids.to_a)
    
      user_ids = Set.new
      events.each do |event|
        user_ids << discover_user_ids_for_event(event)
    
      # retrieve new set of users
      users = User.find(user_ids.to_a)
    
      # ... and so on  
    
    end
    

    我不确定您的方法应该返回什么,但您可能会弄清楚如何使用通过使用 ID 集合将finds 组合在一起的想法来最小化数据库查询.

    【讨论】:

    • 谢谢!这就是我试图检查我是否可以阻止这样做的原因,因为这意味着要重写相当多的函数。但我认为我必须同时做这两件事,以及非规范化的第一个回复
    【解决方案3】:

    你想一次显示所有细节吗(我的意思是当页面加载时你真的想加载所有这些信息),如果不是你可以做的是按需加载它们

    如下

    定义新闻源

    • 找出我需要哪些用户
    • 通过 DB 检索这些用户

    • 找出这些用户发生了哪些事件

      一旦你显示事件给他们一个按钮或其他东西来深入了解其他细节(按需)然后使用 AJAX 加载它们(这样页面就不会刷新)

      当用户想要深入细节时重复使用此技术

    通过这样做,您将节省大量处理能力,并且只会获得用户需要的详细信息

    我不知道这是否适用于你的情况

    如果没有,那么您必须找到更优化的加载详细信息的方式

    干杯, 同龄人

    【讨论】:

    • 感谢sameera!当然可以做到——我必须评估这两种方法的性能。感谢您的建议!
    【解决方案4】:

    我了解到您正在尝试根据您的数据执行某种算法来进行某种推荐或类似的事情。

    我有两个建议:

    1) 您根据您实际想要实现的目标重新评估您的算法/设计。例如,如果应用程序的用户可能有很多帖子,并且应用程序希望根据帖子数量执行一些算法,那么每次计算他们的帖子将非常昂贵。为了优化这一点,可以在用户模型上添加一个 post_count 列,并在用户成功发布帖子时增加该计数。同样,如果您可以在您的用户、事件、组等之间建立某种类似的关系,那么请考虑这些方面的内容。

    2) 如果第一个解决方案不可行,那么对于这样的任何事情,您必须避免执行多个查询,然后使用 ruby​​ 来处理数据,这显然会非常昂贵,并且如果您有大型数据集则永远不可取。因此,您需要的是使用 join 进行一次 sql 查询并一次获取所有数据。也只从您需要的数据库中选择那些字段名称。对于大型数据集,它确实很有帮助。例如,如果您需要用户和事件表中的用户 id 和 event_id 而没有别的,那么就这样做

    User.find(:all, 
          :select => 'users.id, users.event_id', 
          :joins => 'join events on users.id = events.user_id',
          :conditions => ['users.id in (your user ids)'])
    

    我希望这将为您指明正确的方向。

    【讨论】:

    • 好点!展望未来,为每个事件存储一个“分数”肯定是一个潜在的强大解决方案。我还没有足够的确认产品规格,但是当我这样做时,这将是一个很好的潜在解决方案
    猜你喜欢
    • 2011-11-07
    • 2011-08-14
    • 2018-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多