使用activemodel序列化器在rails 4中缓慢序列化permormance

时间:2017-11-10 08:08:12

标签: ruby-on-rails activerecord serialization active-model-serializers

删除了N + 1个查询,但它对我没有帮助。只有40个对象,需要15秒。

我认为有很多Stock.with_translations(I18n.locale)Distributor.with_translations(I18n.locale) db调用,序列化的工作速度很慢。我怎么能重构那个db调用?

 class ShopsSerializer < ActiveModel::Serializer
    include ActionView::Helpers::SanitizeHelper

    attributes :id, :title, :description, :audio_sizes, :stocks_count, :image_sizes, :audio_count, :country
    has_many :images, serializer: ShopImageSerializer
    has_many :products, serializer: ProductSerializer

    def image_sizes
      total = 0.0

      stocks = Stock.with_translations(I18n.locale).includes(:images).where(city_id: object.id)
      stocks.each do |stock|
        sum = stock.images.inject(0){|sum, item| sum + item.image_size if item.image.present?} || 0
        total += sum
      end

      total.round(2)
    end

    def audio_sizes
      size = 0.0
      Stock.with_translations(I18n.locale).where(city_id: object.id).map{|s| size += s.audio.size if s.audio.present?}
      Distributor.with_translations(I18n.locale).where(city_id: object.id).map{|d| size += d.audio.size if d.audio.present?}
      size
    end

    def stocks_count
      Stock.with_translations(I18n.locale).where(city_id: object.id).count + Distributor.with_translations(I18n.locale).where(city_id: object.id).count
    end

    def audio_count
      count = 0
      Stock.with_translations(I18n.locale).where(city_id: object.id).map do |s|
        if s.audio.present?
          count += 1
        end
      end

      Distributor.with_translations(I18n.locale).where(city_id: object.id).map do |d|
        if d.audio.present?
          count += 1
        end
      end
      count
    end
  end

5 个答案:

答案 0 :(得分:1)

理想情况下,您应该在数据库级别移动计算,但我没有时间为您编写此代码。否则你仍然有N + 1问题,因为对于要序列化的每个对象,你要查询东西。

无论如何,在你的情况下获胜至少会进行一次查询,记住它们:

 class ShopsSerializer < ActiveModel::Serializer
    include ActionView::Helpers::SanitizeHelper

    attributes :id, :title, :description, :audio_sizes, :stocks_count, :image_sizes, :audio_count, :country
    has_many :images, serializer: ShopImageSerializer
    has_many :products, serializer: ProductSerializer

    def image_sizes
      total = 0.0

      stocks.each do |stock|
        sum = stock.images.inject(0){|sum, item| sum + item.image_size if item.image.present?} || 0
        total += sum
      end

      total.round(2)
    end

    def audio_sizes
      size = 0.0
      stocks.map{|s| size += s.audio.size if s.audio.present?}
      distributors.map{|d| size += d.audio.size if d.audio.present?}
      size
    end

    def stocks_count
      stocks.count + distributors.count
    end

    def audio_count
      count = 0
      stocks.map do |s|
        if s.audio.present?
          count += 1
        end
      end

      distributors.map do |d|
        if d.audio.present?
          count += 1
        end
      end
      count
    end

    private

    def stocks
      @stocks ||= Stock.with_translations(I18n.locale).includes(:images).where(city_id: object.id)
    end

    def distributors
      @distributors||= Distributor.with_translations(I18n.locale).where(city_id: object.id)
    end
  end

答案 1 :(得分:1)

使用rails缓存而不是activemodel序列化程序缓存解决了我的问题。 link

答案 2 :(得分:1)

您没有说明您使用的是什么版本的AMS,如何使用序列化程序,或者您的ar模型或关联序列化程序。

你也没有说你尝试了什么,或者你读过什么文档,所以很难知道你自己解决这个问题的投资与要求互联网为你做的工作。如果这听起来很苛刻,抱歉,这只是来自处理开源问题的经验。

那就是说,AMS本身不会为你做任何db操作。如果你想加载任何东西,那么你需要在你的应用程序中做一些事情,这意味着阅读有关关联和查询的rails文档

在询问技术问题时,提供足够的信息是一个常见问题。我建议您查看https://www.chiark.greenend.org.uk/~sgtatham/bugs.htmlhttps://github.com/rails-api/active_model_serializers/blob/f5ec8ed9d4624afa6ede9b39d51d145b53b1f344/CONTRIBUTING.md#filing-an-issuehttps://github.com/norman/yourbugreportneedsmore.info/blob/master/index.html

引用最后一个:

你好!

    

      您已被定向到此网站,因为您提交了错误报告       一个开源项目,但是你提供的信息太少了       开发人员能够帮助您。这看起来很熟悉吗?     

    
               嗨,当我使用&lt; program&gt;时,我收到了一个奇怪的错误,你知道吗?         什么可能是错的?            
    

      即使你面前有代码,调试软件也很难。       现在想象一下,尝试在其他人的计算机上调试软件,而不是       任何对代码的访问,而不知道操作系统是什么       计算机,甚至是正在使用的软件版本。你唯一的       提示是“有一个奇怪的错误”,你有50行的1行       堆栈跟踪工作。声音不可能?那是因为它!     

    

你想要帮忙吗?

    

      如果你想真正解决你的问题,你可以这样做       提交开发人员实际响应的错误报告:     

    
          
  •         有堆栈跟踪?发送整件事 - 或者更好的是,发送一个链接         它粘贴在Gist或Pastie上。       
  •       
  •         提供上下文,例如Ruby或Python或COBOL的版本         无论你使用什么,以及导致问题的代码。         再一次,Gist和Pastie是你的朋友。       
  •       
  •         更好的是,创建一个可以重现问题的小程序         它在Github上,或压缩并发送         一封电邮。       
  •       
  •         更好的是,如果你能够,添加一个失败的测试用例         演示您遇到的问题,并将其作为拉取请求发送         或补丁。       
  •     
    

太多信息?

    

      如果你只记得一件事,请记住:再现性       关键。如果我无法重现您的问题,我无法修复它。     

    

信息不足?

    

      有关正确错误报告的更长指南,请检查       Simon Tatham's excellent article。     

答案 3 :(得分:1)

你还记得DRY吗?我会重构 Stock.with_translations(I18n.locale).where(city_id: object.id) Stock模型并将其作为范围或范围。它也可能对未来有用。

可能Rails会更好地缓存它。

加快查询速度的一点是向stocks.city_id添加索引,除非您已经拥有索引。

您还可以按joining tables

检查效果

joins(:images)代替includes(:images)

答案 4 :(得分:1)

对于您要查询Stock.with_translations(I18n.locale).includes(:images)一次,Stock.with_translations(I18n.locale)两次和Distributor.with_translations(I18n.locale)一次的40个项目中的每一项。

这导致至少40 * 2 + 40 * 2 + 40个查询。

您显然需要一种方法来创建ShopStock之间的关联,Distributor,可能与has_many :through。但是,当您尝试从城市StockDistributor访问项目时,您可以查询它们并将它们与序列化程序中的选项一起传递,或者您可以按照@建议的那样记忆并运行这些查询。 apneadiving。