Redis位图可高效解决PHP点赞收藏的高并发重复与一致性问题:用like:article:{id}和fav:article:{id}键分别存储,以用户ID为offset执行SETBIT原子操作实现去重,GETBIT校验状态,BITCOUNT毫秒统计总数,管道批量查询双状态。

如果您正在开发PHP网站的点赞与收藏功能,且面临高并发场景下重复操作、数据一致性差、数据库压力大等问题,则可借助Redis位图(Bitmap)实现高效、原子、去重的用户行为统计。以下是具体实施步骤:
一、理解Redis位图在点赞收藏中的核心作用
Redis位图通过单个bit位标识某用户对某资源是否执行过点赞或收藏操作,以极低内存开销(1用户1bit)实现海量用户行为标记,并利用GETBIT/SETBIT/ BITCOUNT等命令保证操作原子性,彻底规避数据库层面的重复插入与行锁竞争。
1、为每条内容(如文章ID=123)分别建立独立的Redis键,格式为:like:article:123 和 fav:article:123。
2、将用户ID作为位图偏移量(offset),例如用户ID=5000,则在其对应键中操作第5000位。
立即学习“PHP免费学习笔记(深入)”;
3、使用SETBIT命令写入1表示已点赞/收藏,GETBIT读取结果判断是否已存在,BITCOUNT统计总人数。
二、PHP中实现点赞操作(含去重与原子判断)
该方法通过一次SETBIT调用完成“设置+返回旧值”双重动作,避免先GET再SET引发的竞态条件,确保同一用户无法重复点赞。
1、在PHP中连接Redis实例,例如使用Predis\Client或phpredis扩展。
2、构造键名:$key = 'like:article:' . $articleId;。
3、执行原子写入并获取原值:$wasLiked = $redis->setBit($key, $userId, 1);。
4、若$wasLiked为0,表示此前未点赞,本次为首次操作,可触发通知或更新缓存;若为1,直接返回“已点赞”提示。
三、PHP中实现收藏操作(独立键隔离与位图复用)
收藏行为需与点赞完全隔离,防止键冲突或统计混淆,因此必须使用独立命名空间的位图键,同时复用相同位图操作逻辑保障一致性。
1、定义专属键:$favKey = 'fav:article:' . $articleId;。
2、调用相同SETBIT逻辑:$wasFaved = $redis->setBit($favKey, $userId, 1);。
3、根据$wasFaved值决定前端按钮状态(实心/空心)及响应文案。
4、如需取消收藏,执行$redis->setBit($favKey, $userId, 0);并将对应位清零。
四、统计总点赞数与收藏数(毫秒级响应)
BITCOUNT命令直接扫描位图中所有置1位的数量,不遍历用户集合,时间复杂度O(N/8),在千万级用户规模下仍保持亚毫秒响应,远优于COUNT(*)全表扫描。
1、获取点赞总数:$likeTotal = $redis->bitCount('like:article:' . $articleId);。
2、获取收藏总数:$favTotal = $redis->bitCount('fav:article:' . $articleId);。
3、将两个数值组合为关联数组返回API:['likes' => $likeTotal, 'favs' => $favTotal]。
五、用户行为批量校验与状态同步
当用户进入详情页时,需一次性获知其对当前内容的点赞与收藏状态,避免多次往返请求。Redis支持MGET多键查询,但位图不适用;此处采用GETBIT双调用合并为管道(pipeline)减少RTT延迟。
1、初始化管道:$pipe = $redis->pipeline();。
2、压入两次GETBIT指令:$pipe->getBit('like:article:' . $articleId, $userId); 和 $pipe->getBit('fav:article:' . $articleId, $userId);。
3、执行管道并接收结果数组:[$liked, $faved] = $pipe->execute();。
4、依据布尔值渲染前端图标状态,无需额外数据库JOIN查询。



















