为产品实现点赞、评论、浏览量计数器

Implementing likes,comment,views counter for a product

我正在创建一个电子商务后端,其中我的每个产品都具有以下计数器属性

- product views
- product likes
- product comments count

我的产品数据库table的当前数据库列是

 - id
 - likes_count
 - views_count
 - comments_count
 - category_id
 - category_parent_id
 - category_sub_parent_id
 - handling_charge
 - shipping_charge
 - meetup_address
 - is_additional_fields
 - status
 - is_deleted
 - created_at
 - updated_at

如下博客所示Wanelo engineering blog 实现一个可以在单行上频繁更新的计数器会导致 innodb 上的行锁定,如果频繁更新可能会导致应用程序出现死锁情况。但是这个解决方案在博客中有很多解释,我对此有所了解。但是,如果有多个与单个产品关联的计数器可以在应用程序增长时同时更新怎么办。我应该如何为计数器设计数据库table。我是否应该单独维护 tables 即

likes counter table

 - id     - product_id      - count

views counter table

 - id     - product_id     - count

comments counter table

 - id     - product_id     - count

通过维护单独的 table,即使产品同时更新(如+评论+视图),它也会单独更新并减少行死锁情况的可能性。 如果它在单个 table 中并且所有更新同时出现,则可能会导致问题。

问题: 有什么更好的方法可以设计计数器的 table 吗?有什么建议吗?

产品 table 中的浏览量计数器没问题。

一个单独的 table 用于喜欢列(product_id、user_id),因此每个用户只能喜欢一个产品一次。否则他们就可以像简单的计数器一样混搭。

一个单独的 table 用于带有诸如(product_id、comment_text、日期.. 等)

等列的评论

这是你要问的吗?

按照您分享的 link 中的建议使用后台队列进行缓冲 inserts/updates 是非常标准的,我也打算提出相同的建议。

您可以像重新计算单个计数器一样重新计算多个计数器。查看计数可以缓存在 Memcached/Redis 中,或者您可以将它们存储在单独的 table 中(尽管我建议为此使用一些分析解决方案)。

在你的工人中:

class ProductCountsWorker
  # ...

  def perform(product_id)
    Product.find(product_id).update_counts!
  end
end

在你的模型中:

class Product < ActiveRecord::Base
  # ...

  after_create :init_views_count_buffer

  private

  def init_views_count_buffer
    reset_views_count_buffer(views_count || 0)
  end

  def views_count_cache_key
    "#{cache_key}/views_count_buffer"
  end

  def reset_views_count_buffer(value = 0)
    Rails.cache.set(views_count_cache_key, value)
  end

  # Called from controllers etc
  def increment_views_count_buffer
    Rails.cache.increment(views_count_cache_key)
  end

  def update_counts!
    transaction do
      update!(
        likes_count: likes.count,
        views_count: views_count + (Rails.cache.fetch(views_count_cache_key) || 0),
        # Or, if you have a separate views table:
        # views_count: views.count,
        comments_count: comments.count,
      )
      reset_views_count_buffer
    end
  end
end

另一个建议是将此查看计数功能拆分为一个问题。

Rails Low-Level Caching docs