【问题标题】:Implementing likes,comment,views counter for a product为产品实施点赞、评论、浏览计数器
【发布时间】:2016-02-22 14:00:57
【问题描述】:

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

- product views
- product likes
- product comments count

我为产品数据库表拥有的当前数据库列是

 - 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 上的行锁定,如果频繁更新可能会导致应用程序中的死锁情况。但是对此的解决方案在我有一个想法的博客中有很多解释。但是,如果有多个与单个产品相关联的计数器可以在应用程序增长时同时更新怎么办。我应该如何为计数器设计数据库表。我是否必须维护单独的表格,即

likes counter table

 - id     - product_id      - count

views counter table

 - id     - product_id     - count

comments counter table

 - id     - product_id     - count

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

问题:有没有更好的方法来设计柜台的桌子?请问有什么建议吗?

【问题讨论】:

  • 考虑执行所有插入操作,而不是简单地增加一个值。这样您就可以在发生此类流量时报告(计数)。或者可能按天/月/等对递增值进行分组

标签: mysql e-commerce counter deadlock


【解决方案1】:

按照您共享的链接中的建议,使用后台队列来缓冲插入/更新是非常标准的,我也会提出同样的建议。

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

在您的工作人员中:

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

【讨论】:

    【解决方案2】:

    产品表中的视图计数器很好。

    一个单独的喜欢表,其中包含 (product_id, user_id) 等列,因此每个用户只能喜欢一个产品一次。否则他们就可以像只是一个简单的计数器一样捣碎。

    带有 (product_id、comment_text、date.. 等) 列的 cmets 的单独表

    这是你要问的吗?

    【讨论】:

    • 感谢您的回复。我们有不同的表,其中 cmets 和 likes 映射到用户。计数器表只记录 cmets 或视图的数量。但是这些计数是否应该单独维护?因为当点击次数增加时,它可能会在更新期间导致问题。
    • 我想尽可能多地分散潜在的行锁不会有坏处。不过在编写代码时会更烦人。也许您可以模拟高流量/大量用户输入的情况并检查日志以查看死锁信息。这可以帮助您确定是否值得。编写几个循环来更新您的计数器并在计数器更新之间随机休眠 1-100 毫秒并运行几个小时。
    • 出于好奇,您是否进行了任何模拟?结果如何?
    • 嗨@ChrisTrudeau 我还没有。我需要尽快进行模拟测试,与开发相关联。肯定会在这里发布结果。
    • 太棒了!期待吧
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-16
    • 1970-01-01
    • 1970-01-01
    • 2015-06-16
    • 1970-01-01
    • 2010-10-01
    • 1970-01-01
    相关资源
    最近更新 更多