示例:使用散列键重新实现计数器

前面的《字符串》一章曾经展示过怎样使用 INCRBY 命令和 DECRBY 命令去构建一个计数器程序,在学习了 HINCRBY 命令之后,我们同样可以通过类似的原理来构建一个使用散列实现的计数器程序,就像代码清单 3-3 展示的那样。


代码清单 3-3 使用散列实现的计数器:/hash/counter.py

  1. class Counter:
  2.  
  3. def __init__(self, client, hash_key, counter_name):
  4. self.client = client
  5. self.hash_key = hash_key
  6. self.counter_name = counter_name
  7.  
  8. def increase(self, n=1):
  9. """
  10. 将计数器的值加上 n ,然后返回计数器当前的值。
  11. 如果用户没有显式地指定 n ,那么将计数器的值加上一。
  12. """
  13. return self.client.hincrby(self.hash_key, self.counter_name, n)
  14.  
  15. def decrease(self, n=1):
  16. """
  17. 将计数器的值减去 n ,然后返回计数器当前的值。
  18. 如果用户没有显式地指定 n ,那么将计数器的值减去一。
  19. """
  20. return self.client.hincrby(self.hash_key, self.counter_name, -n)
  21.  
  22. def get(self):
  23. """
  24. 返回计数器的当前值。
  25. """
  26. value = self.client.hget(self.hash_key, self.counter_name)
  27. # 如果计数器并不存在,那么返回 0 作为默认值。
  28. if value is None:
  29. return 0
  30. else:
  31. return int(value)
  32.  
  33. def reset(self):
  34. """
  35. 将计数器的值重置为 0 。
  36. """
  37. self.client.hset(self.hash_key, self.counter_name, 0)

这个计数器实现充分地发挥了散列的特长:

  • 它允许用户将多个相关联的计数器储存到同一个散列键里面实行集中管理,而不必像字符串计数器那样,为每个计数器单独设置一个字符串键。

  • 与此同时,通过对散列中的不同字段执行 HINCRBY 命令,程序可以对指定的计数器执行加法操作和减法操作,而不会影响到储存在同一散列中的其他计数器。

作为例子,以下代码展示了怎样将三个页面的浏览次数计数器储存到同一个散列里面:

  1. >>> from redis import Redis
  2. >>> from counter import Counter
  3. >>> client = Redis(decode_responses=True)
  4. >>> # 创建一个计数器,用于记录页面 /user/peter 被访问的次数
  5. >>> user_peter_counter = Counter(client, "page_view_counters", "/user/peter")
  6. >>> user_peter_counter.increase()
  7. 1L
  8. >>> user_peter_counter.increase()
  9. 2L
  10. >>> # 创建一个计数器,用于记录页面 /product/256 被访问的次数
  11. >>> product_256_counter = Counter(client, "page_view_counters", "/product/256")
  12. >>> product_256_counter.increase(100)
  13. 100L
  14. >>> # 创建一个计数器,用于记录页面 /product/512 被访问的次数
  15. >>> product_512_counter = Counter(client, "page_view_counters", "/product/512")
  16. >>> product_512_counter.increase(300)
  17. 300L

因为 user_peter_counterproduct_256_counterproduct_512_counter 这三个计数器都是用来记录页面浏览次数的,所以这些计数器都被放到了 page_view_counters 这个散列里面;与此类似,如果我们要创建一些用途完全不一样的计数器,那么只需要把新的计数器放到其他散列里面就可以了。

比如说,以下代码就展示了怎样将文件 dragon_rises.mp3 和文件 redisbook.pdf 的下载次数计数器放到 download_counters 散列里面:

  1. >>> dragon_rises_counter = Counter(client, "download_counters", "dragon_rises.mp3")
  2. >>> dragon_rises_counter.increase(10086)
  3. 10086L
  4. >>> redisbook_counter = Counter(client, "download_counters", "redisbook.pdf")
  5. >>> redisbook_counter.increase(65535)
  6. 65535L

图 3-11 展示了 page_view_countersdownload_counters 这两个散列以及它们包含的各个计数器的样子。


图 3-11 散列计数器数据结构示意图_images/IMAGE_COUNTERS_IN_HASH.png


通过使用不同的散列储存不同类型的计数器,程序能够让代码生成的数据结构变得更容易理解,并且在针对某种类型的计数器执行批量操作时也会变得更加方便。比如说,当我们不再需要下载计数器的时候,只要把 download_counters 散列删掉就可以移除所有下载计数器了。