【问题标题】:Can I save memory by writing SQL instead of ActiveRecord?我可以通过编写 SQL 而不是 ActiveRecord 来节省内存吗?
【发布时间】:2017-02-14 10:05:24
【问题描述】:

除了简洁易读的语法之外,ActiveRecord 还有其他好处吗? ActiveRecord 是否真的比纯 SQL 占用更多的内存? (我正在使用 PostgreSQL。)

我在 Alexander Dymo 的 blog post on Rails performance 中读到 ActiveRecord 比纯 SQL 占用更多内存:

使用 ActiveRecord 很容易操作数据。但是 ActiveRecord 本质上是数据之上的包装器。如果你有1G的数据 在表中,它的 ActiveRecord 表示将占用 2G,并且在 有些情况,更多。是的,在 90% 的情况下,开销是合理的 您获得的额外便利。但有时你不需要它。

我还在the documentation 中读到 ActiveRecord “更好”:

如果您习惯于使用原始 SQL 来查找数据库记录,那么您 通常会发现有更好的方法来执行相同的操作 Rails 中的操作。 Active Record 使您无需使用 大多数情况下都是 SQL。

我在 Heroku 上遇到过Error R14 (Memory quota exceeded),所以为了解决这个问题,我了解了膨胀和内存泄漏。我已经确定了一些改进方法,包括急切地加载 ActiveRecord 数据,如List.joins(:quantities).find(@list_id),以及减少对数据库的整体调用。但我仍然渴望记忆。

如果 ActiveRecord 的好处只是更容易编写,那么我将尽可能编写纯 SQL。但我希望 SO 社区可以评论权衡并填补我的理解中的任何空白,特别是如果我在离开 AR 之前应该三思而后行。

完全摆脱 AR 会不会很糟糕?

【问题讨论】:

    标签: sql ruby-on-rails postgresql heroku rails-activerecord


    【解决方案1】:

    您需要查看该博客文章中的下一段以了解上下文:

    可以避免 ActiveRecord 开销的一个示例是批量更新。下面的代码既不会实例化任何模型,也不会运行验证和回调。

    Book.where('title LIKE ?', '%Rails%').update_all(author: 'David')
    

    作者并不是说您应该使用原始 SQL 将数据(作为一堆哈希)拉入 Rails,而不是使用 ActiveRecord 将数据拉入 Rails(作为一堆模型),他们是说您不应该从数据中提取一堆数据,在 Rails 中对其进行操作,然后在您可以将所有工作直接推入数据库时​​将其放回。

    考虑以下之间的区别:

    Book.where('title LIKE ?', '%Rails%').update_all(author: 'David')
    

    Book.where('title LIKE ?', '%Rails%').each do |b|
      b.update(author: 'David')
    end
    

    第一个只是简单地向数据库发送一点 SQL:

    update books
    set author = 'David'
    where title like '%Rails%'
    

    并让数据库完成所有工作。几乎没有任何数据在数据库和 Rails 应用程序之间移动,您的应用程序几乎不会为此使用任何内存,并且您的应用程序几乎不会为此做任何工作/CPU。

    第二个从数据库中取出一堆行,创建一堆 ActiveRecord 模型,运行一堆 Ruby 代码(创建模型,设置值,运行验证,...),然后发送一堆单行 SQL 更新返回到数据库。此版本通过网络传输更多数据,在 Rails 中使用更多内存,并使用更多 CPU 来运行更多 Ruby 代码。

    消息不是“不要使用 ActiveRecord”,而是要将您的数据操作逻辑放在正确的位置,而不是教条地在 Rails 中做所有事情,因为您在某处听说过“数据库中没有逻辑”。


    ActiveRecord 的内存问题通常来自于实例化太多模型。如果你说Model.all,你可能做错了。如果您试图在 Rails 中同时操作多个模型,那么您可能做错了。 ORM 非常适合处理单个模型,但它们不适合批量操作。

    如果您下拉至该博文的第 2.2 节:

    有时手头的任务最好用其他工具来完成。最常见的是数据库。为什么?因为 Ruby 不擅长处理大型数据集。就像,非常非常糟糕。请记住,Ruby 占用大量内存。因此,例如,要处理 1G 的数据,您可能需要 3G 和更多的内存。垃圾收集 3G 需要几十秒。好的数据库可以在一秒钟内处理数据。让我举几个例子。

    你会看到博客作者说同样的话。

    【讨论】:

    • 谢谢@mu,这很有帮助!我了解上述关于批量更新的内容,这可能会导致严重的内存问题。如果可以的话,有几个后续行动。 AR 使用更多内存来生成相同的 SQL 代码是真的吗?我可以通过运行Model.where("id=10").first 而不是Model.find(10) 来节省内存吗?或Model.where("age=50") 而不是Model.where(age:50)。另外,为了确保我理解,当你说我不应该运行 Model.all 时,你的意思是创建一个包含许多 AR 对象的 Ruby 变量是不好的做法;对吗?
    • (1) 当然,AR 将使用更多的内存来生成select * from t where ...,而不是仅仅编写 SQL 字符串本身,但差异并不值得担心。 AR 在这里将更容易且更不容易出错,并且运行时成本将非常小。 (2) where.firstfind 将向数据库发送几乎相同的 SQL 并实例化相同数量的模型,因此没有真正的区别,请使用 findwhere(string)where(hash) 应该几乎相同,尽可能使用 where(hash)。 (3) 对,Model.all 创建了太多模型实例,这会占用内存。
    • 谢谢。跟进,如果你是游戏。我需要评估几千个模型并选择一个。选择取决于它的属性如何影响一小部分其他模型的累积属性。目前,我从一组有资格添加的模型开始(不好!实践)。我用几个查询缩小了数组。我没有尝试将“缩小”归结为一个查询,尽管我可以。问题:从一组模型 ID 开始并缩小 那个 会更好吗?喜欢这个Food.select("id").where("age=50")Food.where(age:50).pluck(:id)
    • 如果这需要一个新问题,或者您现在已经完成了帮助,请告诉我。 @mu,忘记在上面的评论中标记你(并且字符用完了)。
    • 一个新问题可以更容易地提供一些细节。如果您只需要 id,那么 1k id 将比 1k 模型占用更少的内存。通常最好将批量/聚合数据操作推送到数据库中,数据库擅长处理大量数据。
    猜你喜欢
    • 1970-01-01
    • 2014-03-19
    • 2012-01-18
    • 1970-01-01
    • 2012-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-22
    相关资源
    最近更新 更多