使用memcache实现高并发计数器

在网站中很多场景都有计数的需求,比如找回密码要求一天只能找回10次。如果并发量比较高会导致数据库频繁的更新和查询,可以使用memcached来实现计数器,代码很简单:

使用2个memcache,一个用于存储增加的计数器,另一个用于上锁。锁有时间限制,如果未到期,那么就更新memcache里的计数器;如果到期,就创建一个新锁,并把memcache里的值增加到数据库里,并删除现有的memcache里的计数器。

实现代码如下:
def incrementCounter(key, update_interval=10):
  """Increments a memcached counter.
  Args:
    key: 数据库里计数器实体的key.
    update_interval: 更新频率.
  """
  lock_key = "counter_lock:%s" % key
  count_key = "counter_value:%s" % key
  if memcache.add(lock_key, None, time=update_interval):
    count = int(memcache.get(count_key) or 0) + 1
    def tx():
      entity = db.get(key)
      entity.counter += count
      entity.put()
    db.run_in_transaction(tx)
    memcache.delete(count_key)
  else:
    memcache.incr(count_key, initial_value=0)



注意那个memcache.add函数,如果未到期,那么加锁失败,直接增加memcache的值;如果到期,那么加锁成功,更新memcache到datastore。

这种方法可能存在这些缺陷:

    如果计数频率太低,会导致memcache几乎没用,每次都得更新数据库。这比直接访问数据库多用了3次memcache操作。
    如果memcache里的计数器过期,那就会丢失更新间隔内的计数值。但一般是计算频率过低导致的,而频率越低,丢失的计数值就越小。
    需要真正的计数值时,需要同时访问datastore和memcache。
    在更新数据库和删除memcache里的计数器时存在不同步,可能丢失计数值。解决办法是把delete函数改成decr函数,剪掉之前获取的计数值即可。不过这样就会长期占用memcache了。

实际上如果memcache能提供一个失效事件,每次失效时自动更新到数据库,将会是个很不错的特性。

猜你喜欢

转载自san-yun.iteye.com/blog/1602661