改善低效率的查询

时间:2019-03-07 02:25:17

标签: ruby-on-rails ruby database postgresql activerecord

我遇到了我的应用超时的问题,因为查询花费了太长时间。我使用的是Heroku的免费层,因此我没有完全优先考虑速度,因此我遇到了一些本地无法看到的超时问题。我希望有人可以看到我的查询问题,从而使我可以加快速度。

我的查询如下:

  def index
    # file = File.read('app\controllers\recipe.csv')
    clear_database
    file = get_file
    recipe_array = file.split("\n")
    dbUser = User.find_by(id: 999999999)
    recipe_array.each do |recipe|
      # I'm saving here becuase I need the ID later
      dbRecipe = dbUser.recipes.create

      recipe = recipe.split(",")
      url_index = recipe.length - 1
      img_url_index = recipe.length - 2
      recipe.each_with_index do |item, index|
        if index == 0
          dbRecipe.name = item.strip.downcase
          dbRecipe.save
        elsif index == url_index
          dbRecipe.url = item
          dbRecipe.save
        elsif index == img_url_index
          dbRecipe.img_url = item
          dbRecipe.save
        elsif index.odd?
          count = item
          food = recipe[index + 1]
          dbIngredient = Ingredient.new
          dbFood = Food.find_by_name(food)
          if dbFood.nil?
            dbFood = Food.new
            dbFood.name = food.strip.downcase
            dbFood.save
          end

          # populate ingredient
          dbIngredient.unit_type = item.split(" ").last
          dbIngredient.quantity = item.split(" ").first
          # I'm saving so much above because I need the id's
          dbIngredient.recipe_id = dbRecipe.id
          dbIngredient.food_id = dbFood.id
          dbIngredient.save
        end
      end
    end
  end

我的数据由存储在CSV文件中的配方组成。大约有300行,如下所示:

"Sirloin Steak with Blue Cheese Compound Butter,0.6 oz, Butter,2 count, Garlic Cloves,2 count, Green Onions,12 oz, Fingerling Potatoes,8 oz, Green Beans,12 oz, Sirloin Steaks,1 oz, Blue Cheese,https://homechef.imgix.net/https%3A%2F%2Fasset.homechef.com%2Fuploads%2Fmeal%2Fplated%2F2543%2F2543SirloinSteakwithBlueCheeseCompoundButterReshoot2__1_of_1_-b04048840f58000cef80b38fc3f77856-b04048840f58000cef80b38fc3f77856.jpg?ixlib=rails-1.1.0&w=425&auto=format&s=eeba60ce35bcee4938a11286cbea0203,https://www.homechef.com/meals/sirloin-steak-with-blue-cheese-compound-butter
Teriyaki Ginger-Glazed Salmon,1 Tbsp, Chopped Ginger,2 count, Garlic Cloves,2 count, Green Onions,8 oz, Carrot,2 count, Heads of Baby Bok Choy,1 count, Red Fresno Chile,2 oz, Teriyaki Glaze,12 oz, Salmon Fillets,https://homechef.imgix.net/https%3A%2F%2Fasset.homechef.com%2Fuploads%2Fmeal%2Fplated%2F3429%2F3429TeriyakiGinger-GlazedSalmonReshoot3__1_of_1_-73adcd6ad23cc72b28fdba85387fa18a-73adcd6ad23cc72b28fdba85387fa18a.jpg?ixlib=rails-1.1.0&w=425&auto=format&s=9e6b37380203ec5a58a5ddb906b5ae8b,https://www.homechef.com/meals/teriyaki-ginger-glazed-salmon
Al Pastor Pork Flautas,1 count, Shallot,1 count, Lime,3 oz, Pineapple Chunks,1 oz, Queso Fresco,12 oz, Ground Pork,1 tsp, Chipotle Seasoning,6 count, Small Flour Tortillas,0.5 oz, Baby Arugula,1 oz, Sour Cream,10 oz, Ground Beef,https://homechef.imgix.net/https%3A%2F%2Fasset.homechef.com%2Fuploads%2Fmeal%2Fplated%2F4290%2F4290AlPastorPorkFlautasFinal2__1_of_1_-4e7fe04ac157a463b4d93eb57e9b93f9-4e7fe04ac157a463b4d93eb57e9b93f9.jpg?ixlib=rails-1.1.0&w=425&auto=format&s=de2e2403d7261f2697567faf5f477359,https://www.homechef.com/meals/al-pastor-pork-flautas

3 个答案:

答案 0 :(得分:2)

您正在尝试在单个http请求/响应周期内执行过多操作。对于CSV中的每一行,您都在执行一个SQL INSERT(使用dbUser.recipes.create),UPDATE(使用dbRecipe.save)和SELECT (带有Food.find_by_name(food))。

即使您进行了一些优化,您是否也确定CSV在应用程序的整个生命周期中只会有约300行?即使答案是肯定的,一般来说,还是最好的做法是尽快对用户操作做出响应,而不是让他们看着浏览器等待响应。

因此,我建议您重新考虑您的方法。如果单个操作必须执行许多sql命令,请考虑使任务异步运行的方法。这就是ActiveJobhttps://edgeguides.rubyonrails.org/active_job_basics.html)和sidekiq(https://github.com/mperham/sidekiq/)之类的工具的设计。

例如,以某种方式设计您的应用程序,使用户单击某个按钮来上传CSV并做出回应:“感谢您的提交,我们正在努力!”。用户可以随时回来查看正在处理的文件的状态或刷新屏幕。或者,您可以通过Web套接字通过AJAX轮询或双向通信获得更复杂的状态信息并自动进行状态检查。使用Railsy的方法是使用ActionCablehttps://guides.rubyonrails.org/action_cable_overview.html)。

答案 1 :(得分:1)

这实际上不是查询,而是一堆其他的东西。您正在解析文件并根据该文件的内容写入数据库。另外,如果您正在做的只是播种数据库,则完全没有理由让应用程序或浏览器参与其中。通过rake任务或从rails控制台可以更好地完成此任务。您可以通过将操作分成几部分来避免数据库超时,例如

  • 将文件加载到内存中
  • 对于文件的每一行,写入数据库并保存记录

无论如何,您应该完全将数据库的种子从应用程序中移除。

答案 2 :(得分:1)

您可以使用activerecord-import gem-主要思想是通过一个查询加载许多记录。

您必须将列名称指定为数组:

column_names = [:name, :quantity, :unit_type, :food_id]

接下来,您必须以相同的顺序从csv加载数据:

row_values = [
  ["Sirloin Steak with Blue Cheese Compound Butter", 1, "Butter", 2],
  ["Something else", 2, "else", 32],
  ...
]

并导入:

DbIngredient.import(column_names, row_values)