当前位置:首页 > 问答 > 正文

精灵怎么用Redis计数器啊,感觉挺简单但又有点细节没说清楚

(来源:Redis 官方文档对 INCR 命令的描述)精灵想用 Redis 计数器,最核心的命令就是 INCR,这个命令特别简单,你给它一个键(page_view),它就会把这个键对应的值增加 1,如果这个键本来不存在,Redis 会先把它当成 0,然后再加 1,所以第一次用的结果就是 1,这解决了“初始化”的麻烦,你不用特意去判断这个键存不存在、要不要先设个 0。

精灵怎么用Redis计数器啊,感觉挺简单但又有点细节没说清楚

光会加 1 可不够用,比如精灵想统计的不是总浏览量,而是每小时的活跃用户数。(来源:实际应用中常见的按时间维度统计需求)这时候就需要用到 EXPIRE 命令了,你可以在第一次设置计数器的时候,给它加上一个过期时间。SET hourly_active 0 EX 3600,这行命令的意思是,设置一个叫 hourly_active 的计数器,初始值为 0,并且在 3600 秒(也就是 1 小时)后自动过期删除,每当有用户活跃时,你就用 INCR hourly_active 来增加计数,一小时后,这个计数器自动清零,新的周期又重新开始,这样你就不用写个定时任务去手动清空了,非常省心。

有时候精灵可能不想只加 1,比如用户一次操作增加了 10 个积分。(来源:Redis 官方文档对 INCRBY 命令的描述)这时候可以用 INCRBY 命令,INCRBY user_score 10,就能一次性增加 10,对应的,还有减少的命令 DECR(减 1)和 DECRBY(减指定数值)。

精灵怎么用Redis计数器啊,感觉挺简单但又有点细节没说清楚

还有一个很重要的细节是“原子性”。(来源:多线程/并发编程中的核心概念)这是个听起来专业的词,但理解起来很简单,它指的是一个操作是不可分割的,在 Redis 里,INCR 命令是原子操作,这意味着,就算有成千上万个精灵同时对这个计数器喊“加 1”,Redis 也会让它们排好队,一个一个来执行,绝对不会出现两个精灵同时读到数字 100,然后都把它改成 101,结果最终只变成 101(正确应该是 102)这种错乱的情况,这个特性是 Redis 计数器比你自己在程序里写 count = count + 1 要可靠得多的根本原因。

那如果精灵想重置计数器怎么办?(来源:常见的计数器管理操作)最简单粗暴的就是用 SET 命令直接设一个新值,SET total_visits 0,但要注意,这样操作会覆盖掉原来的值,而且如果你之前设置了过期时间,这个 SET 操作可能会清除掉过期时间(除非你重新指定),所以更安全一点的做法是先用 DEL 命令删除这个键(DEL total_visits),这样下次再用 INCR 时,它又会从 1 开始。

精灵可能还会遇到一种情况:我需要先获取当前值,然后根据这个值做一些逻辑判断,最后再决定要不要增加。(来源:需要先读后写的业务逻辑)注意! 这种情况下,你不能分开执行 GETINCR 两个命令,因为在 GETINCR 的间隙,可能有别的精灵已经修改了这个值,你拿到的已经是“过期”的数据了,为了解决这个问题,Redis 提供了更强大的工具——Lua 脚本。(来源:Redis 对复杂原子操作的支持方式)你可以把“获取值、判断、修改值”这一连串操作写在一个 Lua 脚本里,然后一次性发给 Redis 执行,对于 Redis 整个脚本的执行是原子的,中间不会被其他命令打断,这样就保证了数据的一致性。

精灵可能会想,这个计数器一直增加,万一数字太大把内存占满了怎么办?(来源:对数据持久化和增长的担忧)其实不用担心,一个纯数字的计数器占用的空间非常小,Redis 的数字类型在值比较小的时候用一种很节省空间的方式存储,只有当数字变得非常大之后,才会转换成标准的整数类型,哪怕你的网站浏览量到了几十亿,这个计数器占用的内存也依然是可控的。

精灵用 Redis 计数器,记住几个要点就行:基础增减用 INCR/DECR 系列;要自动清零就加 EXPIRE;怕抢资源不用愁,Redis 命令天生就是原子性;复杂逻辑判断就用 Lua 脚本捆在一起执行,把这些细节搞清楚了,用起来就得心应手了。

精灵怎么用Redis计数器啊,感觉挺简单但又有点细节没说清楚