【问题标题】:Rails Grape api: performannce when getting more dataRails Grape api:获取更多数据时的性能
【发布时间】:2019-11-13 01:16:24
【问题描述】:

我在使用葡萄 api 时遇到性能问题。 我有以下型号:

class Profile
   has_many :transitive_user_profiles
end

class TransitiveUserProfile < ApplicationRecord
  belongs_to :profile
  belongs_to :user
  belongs_to :client

结束

class DefaultAddress 
  belongs_to :user
end

我正在使用葡萄通过 rest-api 获取所有用户和各自的个人资料

@all_profiles =  @all_profiles || 
                TransitiveUserProfile.includes(:profile).where(
              "profile_id IN (?)", application.profile_ids)
present @users, with: Identity::V3::UserEntity, all_profiles: @all_profiles #@user = User.all - around 700 users

我已经写了 UserEntity 类

class UserEntity < Grape::Entity
  expose :id, as: :uniqueId
  expose :trimmed_userid, as: :userId
  expose :nachname, as: :lastName
  expose :vorname, as: :firstName
  expose :is_valid, as: :isValid
  expose :is_system_user, as: :isSystemUser
  expose :email
  expose :utc_updated_at, as: :updatedAt
  expose :applications_and_profiles


  def email  
    object.default_address_email
  end

   def applications_and_profiles
    app_profiles = @all_app_profiles.where(user_id: object.id).collect{|t|  {name: t.profile.unique_id, rights: t.profile.profilrechte} }
    [{:appl_id=>"test", :profiles=>app_profiles}]
  end

 end

我遇到了问题,当我尝试获取所有用户和个人资料时,它需要超过 15 秒。在以下代码中面临问题(花时间获取关联对象)。

 def email  
    object.default_address_email
  end

   def applications_and_profiles
    app_profiles = @all_app_profiles.where(user_id: object.id).collect{|t|  {name: t.profile.unique_id, rights: t.profile.profilrechte} }
    [{:appl_id=>"test", :profiles=>app_profiles}]
  end 

我怎样才能以有效的方式解决(通常少于 5 秒)

【问题讨论】:

  • 代码不是很清楚,但看起来你正在做一个N+1 query。所以简短的回答是:不要那样做。 (具体来说,对于User.all 中的每个user,您都在调用TransitiveUserProfile.all.where("profile_id=?", 12).where(user_id: object.id),然后实例化该集合中的所有记录,所以如果我没看错的话,大约有700+1 个查询)
  • 不,我不认为这是 N+1 问题,我获取所有用户并存储并获取所有配置文件存储 @app_profiles(TransitiveUserProfile.all.where("profile_id=?", 12 )) 并获取用户(User.all)。为每个用户使用grape-api,我使用where条件(@all_app_profiles.where(user_id:object.id))过滤@app_profiles中已经存在的记录。是否有任何替代方法来解决此问题
  • 记住这两件事: 1 - 您的Entity 用于呈现@users 中的每条记录,因此对于@users 中的每个user,您正在调用applications_and_profiles 方法; 2 - 调用Model.where 不会实例化记录,因此您的@all_app_profiles 没有内存中的记录,所以当您最终调用@all_app_profiles.where().collect 时,记录会被拉入内存,因此您正在运行该查询每个用户一次,因此它是 700+1 又名 N+1。 (并且您的日志应该会显示这一点)请参阅here 了解更多信息。
  • @Sidduh 我相信 anothermh 是正确的,它是 N+1,您应该能够通过在控制台中检查您的 SQL 日志来确认这一点,它将触发一堆类似的查询。作为快速修复尝试@all_app_profiles.where(user_id: object.id).includes(:profile).collect { |t| ... }
  • 感谢所有 - 终于解决了 - @all_app_profiles.select {|i| i.user_id == object.id}.collect {|t| {名称:t.profile.unique_id,unique_id:t.profile.unique_id,权利:t.profile.profilrechte} }

标签: ruby-on-rails ruby grape-api grape-entity


【解决方案1】:

app_profiles = @all_app_profiles.where(user_id: object.id).collect{|t| {名称:t.profile.unique_id,权利:t.profile.profilrechte} }

上面的sql会产生一个N+1的问题,会导致性能变慢。您可以检查有关此的日志。解决问题的更好方法是使用如下所示的急切加载(如 @maxplener 和 @anothermh 建议的那样)

app_profiles = @all_app_profiles.where(user_id: object.id).includes(:profile ).collect{|t| {名称:t.profile.unique_id,权利:t.profile.profilrechte} }

有关 Eager Loading 的更多信息,您可以点击以下链接:

1) https://medium.com/@codenode/10-tips-for-eager-loading-to-avoid-n-1-queries-in-rails-2bad54456a3f

2) https://blog.heroku.com/solving-n-plus-one-queries

【讨论】:

    【解决方案2】:

    谢谢大家,是的,这是 N+1 问题。最后我能够使用@all_app_profiles.select {|i| i.user_id == object.id}.collect {|t| {name: t.profile.unique_id, unique_id: t.profile.unique_id, rights: t.profile.profilrechte} }而不是@all_app_profiles.where(user_id: object.id).collect{|t| {name: t.profile.unique_id, rights: t.profile.profilrechte} } [{:appl_id=>"test", :profiles=>app_profiles}]来解决

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-23
      • 1970-01-01
      • 2015-10-14
      相关资源
      最近更新 更多