统计日活用户首先需要在项目中对每一个请求做统计,排重,最终得到今天多少用户访问了这个项目。对于这种与用户请求响应无关的数据使用队列来处理。
1、设置一个路由中间件,将每一个请求以及参数投递到消息队列。
2、消费队列信息来统计。
3、一般在后台查看日活量数据。
这种场景不能使用MySQL,因为操作太过于频繁,而且redis有更好的实现方式。
方法1:使用set
将每个请求的userid作为集合的元素,因为集合是自动去重的,所以最终只需要知道集合中的元素个数就行了。key的过期时间设置为一天过一点,确保能持久化到MySQL。
加入:sadd activity:1001:20191202 21001
数量:scard activity:1001:20191202
持久化:可以选择每天凌晨回写昨天的数据,然后删除昨天的key,防止重复回写,做无用功。
方法2:使用bit操作
不熟悉的同学可以参考官方手册,bit位操作是很节省内存空间,布隆过滤器也是通过bit来存储所有的key的。因为我们只想要最终的个数,至于是哪些用户访问了也不想知道,所以我们只需要将userid对应的bit位设置为1就好了。
public static function setActiveData($userId, $cacheKey, $expire){
if(!Redis::exists($cacheKey)){
Redis::setbit($cacheKey, $userId, 1);
if($expire > 0){
Redis::expire($cacheKey, $expire);
}
}else{
Redis::setbit($cacheKey, $userId, 1);
}
}
key的过期时间和上面一样设置。
持久化bitcount key
可以获得被置为1的位的个数,也就是日活数。
我们来比较这两种方法
根据使用场景,如果每天日活在几百万,那么方法1的集合中就有几百万个元素,而实际上,我们根本就不关心具体有哪些元素,只想知道个数,显然方法2更合适。
根据这个需求,比较set和bit使用的内存量
分别存储10万个id
首先使用 info 命令查看当前使用的内存
# Memory
used_memory:257871752
used_memory_human:245.93M
used_memory_rss:257833848
used_memory_rss_human:245.89M
used_memory_peak:260611440
used_memory_peak_human:248.54M
使用set
function test11(){
$start = microtime(true);
$num = 400000;
for($i=0;$i<100000;$i++){
Redis::sadd('testbit', $num);
$num++;
}
$end = microtime(true);
$this->info('time: '.($end - $start));
}
用时 13.095961093903
占用内存:7.59M
# Memory
used_memory:265835688
used_memory_human:253.52M
used_memory_rss:265797776
used_memory_rss_human:253.48M
used_memory_peak:265835688
used_memory_peak_human:253.52M
删除testbit
使用bit
function test12(){
$start = microtime(true);
$num = 400000;
for($i=0;$i<100000;$i++){
Redis::setbit('testbit', $num, 1);
$num++;
}
$end = microtime(true);
$this->info('time: '.($end - $start));
}
用时: 12.112451076508
占用内存:0.1M
# Memory
used_memory:257973528
used_memory_human:246.02M
used_memory_rss:257935616
used_memory_rss_human:245.99M
used_memory_peak:265835688
used_memory_peak_human:253.52M
相差几十倍。