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

用Redis来数HTTP请求,统计那些访问量啥的,挺实用的方案

关于使用Redis来统计HTTP请求访问量的实用方案,其核心思想是利用Redis内存数据库读写速度极快的特性,来高效处理高并发下的计数任务,下面直接介绍几种常见的做法。

最基本也是最直接的方法,就是使用Redis的INCR命令,这个命令能把一个键(key)存储的数字值增加一,而且是原子性的,原子性的意思就是说,哪怕同时有成千上万个请求过来,这个加一的操作也不会乱,每个请求都能被准确无误地统计进去,不会少算也不会多算。

具体做起来很简单,你想统计网站总的访问量,就可以设置一个键,名字叫 total_website_visits,每次有用户访问网站,你的程序在处理HTTP请求的时候,就在后面加上一行代码,执行 INCR total_website_visits,这样,这个数字就会自动增加,你想看总访问量的时候,直接用 GET total_website_visits 命令就能取到当前的数值,这种方法超级简单,几乎不耗什么资源,非常适合统计全局性的数据。

但光有总访问量往往不够,我们经常需要更细的统计,按天统计,这时候键的设计就要花点心思了,你可以把日期信息放到键名里,今天是2023年10月27日,那么键名就可以是 daily_visits:20231027,每天的第一个请求过来时,这个键还不存在,但INCR命令很聪明,如果键不存在,它会先把值初始化为0,然后再加一,效果就是从1开始,这样,每天都会自动生成一个新的键来统计当天的访问量,要查某天的数据,就用对应的键名去GET,这种按时间维度统计的方法非常灵活,你可以轻松变出按小时统计(hourly_visits:2023102714 代表下午2点)、按月统计的键。

我们不仅想知道总的访问次数,还想知道具体是哪些页面被访问得多,或者哪个IP地址访问得特别频繁(可能是爬虫或者需要关注的对象),这时候,就可以为每个需要统计的对象创建一个键,统计具体页面的访问量:用户访问了 /products/123 这个页面,你就执行 INCR page_visits:/products/123,统计某个IP的访问次数:收到来自 168.1.100 的请求,就执行 INCR ip_visits:192.168.1.100,这样,你就能很方便地分析出最热门的页面或者发现异常访问的IP。

用Redis来数HTTP请求,统计那些访问量啥的,挺实用的方案

上面说的INCR只能统计次数,但有时候我们还需要判断某个用户或者某个IP在今天是不是已经访问过了,避免重复计数(比如一天内一个用户只算一次UV),Redis的SET数据结构正好能帮上忙,SET的特点就是里面的元素都是唯一的,不会重复。

你可以这样做:还是用一个带日期的键,unique_visitors:20231027,当一个用户访问时(通常用用户ID或者IP地址来标识),你执行 SADD unique_visitors:20231027 user_id_or_ip 命令,SADD命令的作用是向集合里添加元素,如果这个元素已经存在,就不会重复添加,这样,到了第二天,你想知道昨天有多少个独立访客,只需要执行 SCARD unique_visitors:20231027,这个命令能直接返回集合中元素的数量,也就是独立访客数,这种方法对于统计UV这类需要去重的场景非常有效。

对于按时间周期统计,比如维护一个最近一小时的访问量排行榜,Redis的有序集合(ZSET)就派上用场了,有序集合里每个成员都有一个分数(score),Redis会根据分数来排序。

用Redis来数HTTP请求,统计那些访问量啥的,挺实用的方案

思路可以是这样:每个请求过来,我们把它的事件(比如用时间戳或者IP地址作为成员)添加到ZSET中,分数就是当前的时间戳,我们需要定期清理掉过时的数据,比如统计一小时的量,你可以每分钟执行一次任务,用 ZREMRANGEBYSCORE 命令把超过一小时之前的数据(分数小于当前时间戳减3600秒)删掉,任何时候你想知道一小时内有多少访问,只需要用 ZCARD 命令查一下当前ZSET里还剩多少成员就行了,这种方法比简单的INCR要复杂一些,需要维护一个清理过期数据的任务。

很重要的一点是,Redis是内存数据库,如果这些统计键无限制地增长,总会把内存用完,所以我们需要设置过期时间,对于按天统计的键,daily_visits:20231027,在设置完值后,立刻执行 EXPIRE key 86400(86400秒就是一天),这样Redis会在一天后自动删除这个键,释放空间,对于统计独立访客的SET,也可以同样设置过期时间,而对于总访问量这种需要永久保存的键,就不需要设置过期时间了。

用Redis数HTTP请求,主要就是靠INCR、SET、ZSET这些简单又强大的数据结构和命令,通过巧妙地设计键名来区分不同的统计维度,再配合合理的过期时间管理,就能搭建出一个性能极高、非常实用的访问量统计系统,这种方法特别适合应对流量突发性高、需要实时查看统计结果的场景。

(注:以上方法思路参考了常见的Redis应用场景实践,在众多技术博客和Redis官方文档用例中均有类似提及,如 Redis 官方文档关于 INCR 命令的经典用例、以及多位技术博主如Antirez(Redis创始人)在其博客中讨论过的模式。)