簡體   English   中英

最佳實踐:我應該在數據庫中存儲哪些信息?

[英]Best practice: Which information should I store in my database?

目前我正在開發一個小書評級應用程序,用戶可以對書籍進行評分和評論。

我當然有書模型:

class Book < ActiveRecord::Base
  has_many :ratings
end

和評級模型:

class Rating < ActiveRecord::Base
  belongs_to :book
end

評級對象的“總評級值”通過不同的評級類別(例如,可讀性,......)來計算。 此外,一本書的總評分應按所有給定的評級計算。

現在我問自己的問題是:我是否應該計算/查詢每本書的每個人訪問我的頁面的整體評分,還是應該在我的圖書模型中添加一個字段,其中(定期)計算和保存總體評級?

編輯:我將在這種情況下使用的“計算”是一個簡單的平均確定。

示例:一本書有大約200個評級。 每個評級都是10個類別評級的組合。 所以我想確定一個評級的平均值,並在所有200個評級中結束。

如果這些評級的平均值在計算上並不昂貴(即不需要很長時間),那么只需在運行中進行計算即可。 這符合不過早使用的想法(參見http://c2.com/cgi/wiki?PrematureOptimization )。

但是,如果您確實想要優化此計算,那么將其存儲在書籍模型上並更新評級寫入的計算是可行的方法。 這被稱為“緩存”結果。 以下是一些緩存數據庫中平均評級的代碼。 (還有其他緩存方式)。

class Book < ActiveRecord::Base
  has_many :ratings, after_add :update_average_rating

  def update_average_rating
    update_attribute(:average_rating, average_rating)
  end

  def average_rating
    rating_sum / ratings.count
  end

  def rating_sum
    ratings.reduce(0) {|sum, rating|
      sum + rating.value # assuming rating model has a value attribute
    }
  end
end

class Rating < ActiveRecord::Base
  belongs_to :book
end

注意:上面的代碼假定數據庫中book表上存在average_rating列。 請記住通過遷移添加此列。

D B

最有效(盡管不是常規)方法是使用數據庫級別的ALIAS列,允許您在每次book計算評級的AVGSUM

#app/models/book.rb
class Book < ActiveRecord::Base
   def reviews_avg category
      cat = category ? "AND `category` = \"#{category}\"" : ""
      sql = "SELECT AVG(`rating`) FROM `reviews` WHERE `book_id` = #{self.id} #{cat})
      results = ActiveRecord::Base.connection.execute(sql)
      results.first.first.to_f
   end
end

這將允許:

@book = Book.find x
@book.reviews_avg               # -> 3.5
@book.reviews_avg "readability" # -> 5

這是最有效的,因為它完全由DB處理:

在此輸入圖像描述


軌道

應該使用Rails的average功能:

#app/models/book.rb
class Book < ActiveRecord::Base
   has_many :ratings do
      def average category
        if category
          where(category: category).average(:rating)
        else
          average(:rating)
        end
      end
   end
end

以上將使您能夠調用@book實例 ,並評估其評級的averagetotal

@book = Book.find x
@book.reviews.average               #-> 3.5
@book.reviews.average "readability" #-> 5

-

您還可以在Review上使用class method / scope

#app/models.review.rb
class Review < ActiveRecord::Base
   scope :avg, (category) -> { where(category: category).average(:rating) }
end

這將允許您致電:

@book = Book.find x
@book.reviews.avg               #-> 3.5
@book.reviews.avg "readability" #-> 5

協會擴展

另一種方法(未測試)是在ActiveRecord Association Extension使用proxy_association.target對象。

雖然不如DB級查詢那么高效,但它可以讓您在內存中執行活動:

#app/models/book.rb
class Book < ActiveRecord::Base
   has_many :reviews do
     def avg category
       associative_array = proxy_association.target
       associative_array = associative_array.select{|key, hash| hash["category"] == category } if category
       ratings = associative_array.map { |a| a["rating"] }
       ratings.inject(:+) / associative_array.size #-> 35/5 = 7
     end
   end
end

這將允許您致電:

@book = Book.find x
@book.reviews.avg                # -> 3.5
@book.reviews.avg "readability"  # -> 5

根本不需要重新計算每次頁面訪問的平均總體評級,因為它只會在某人實際評價該書時發生變化。 所以只需使用字段AVG_RATING或類似的東西,並更新每個給定評級的值。

您是否考慮使用評級的緩存版本。

rating = Rails.cache.fetch("book_#{id}_rating", expires_in: 5.minutes) do
  do the actual rating calculation here
end

在大多數情況下,只需查詢數據庫即可獲得平均值:

average = book.reviews.average(:rating)

在大多數情況下,根據請求查詢將不會是一個非常昂貴的問題,而且正如Neil Atkinson所指出的那樣,預先成熟的優化可能會浪費時間和資源。

然而,當計算成本成為問題時,有幾種方法需要考慮,這取決於計算數據的性質。

如果計算出的數據是值得的資源,那么您可以將其保存在數據庫中。 例如,定期生成的報告(每日,每月,每年)以及需要查詢的報告。

否則,如果計算的數據具有較高的“流失率”(每天創建許多評論),您將使用緩存來盡可能避免昂貴的查詢,但將數據填充到數據庫中可能會導致過多的緩慢UPDATE查詢並占用您的網絡或工作流程。

有許多緩存方法相互補充:

  • etags利用客戶端緩存 - 如果響應沒有改變,不要重新渲染。
  • 片段緩存可避免數據庫查詢,並為未更改的數據重新呈現視圖塊。
  • Memcached或Redis中的模型緩存可用於避免慢速查詢。
  • 低級緩存可用於存儲平均值等內容。

請參閱使用Rails緩存:概述以獲取更多詳細信息。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM