本文目录导读:

- 使用Redis Set集合(最常用)
- 使用有序集合(Sorted Set)去重
- 使用HyperLogLog(基数统计去重)
- 结合Bloom Filter(布隆过滤器)
- 实际去重示例:防止重复提交
- 场景选型建议
- 注意事项
在PHP项目中使用Redis进行集合去重,主要有以下几种方案:
使用Redis Set集合(最常用)
Set天然具备去重特性,同一元素只会存储一次。
添加元素(自动去重)
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// 批量添加
$redis->sAdd('user:online', 'user1', 'user2', 'user1', 'user3');
// 结果:{'user1', 'user2', 'user3'}
// 单个添加
$redis->sAdd('user:online', 'user1');
判断元素是否存在
$exists = $redis->sIsMember('user:online', 'user1'); // true/false
获取所有元素
$users = $redis->sMembers('user:online'); // 返回数组
$count = $redis->sCard('user:online'); // 获取集合大小
使用有序集合(Sorted Set)去重
需要排序或带权重的场景使用。
// 添加元素,分数可设为时间戳
$redis->zAdd('user:score', 85, 'user1');
$redis->zAdd('user:score', 90, 'user2');
$redis->zAdd('user:score', 85, 'user1'); // 不会重复,只更新分数
// 获取所有元素
$users = $redis->zRange('user:score', 0, -1); // 按分数排序
使用HyperLogLog(基数统计去重)
适合大数据量、允许误差的去重统计。
// 添加元素
$redis->pfAdd('page:uv:20240101', ['user1', 'user2', 'user1', 'user3']);
// 统计去重后的数量(有误差,约0.81%)
$count = $redis->pfCount('page:uv:20240101'); // 返回3
结合Bloom Filter(布隆过滤器)
适合超大规模数据判断元素是否存在。
// 使用RedisBloom模块
$redis->rawCommand('BF.ADD', 'url:filter', 'http://example.com/1');
$redis->rawCommand('BF.ADD', 'url:filter', 'http://example.com/2');
// 判断是否存在
$exists = $redis->rawCommand('BF.EXISTS', 'url:filter', 'http://example.com/1');
实际去重示例:防止重复提交
class RedisDeduplication {
private $redis;
public function __construct() {
$this->redis = new Redis();
$this->redis->connect('127.0.0.1', 6379);
}
/**
* 检查并记录去重
* @param string $key 去重键
* @param string $value 去重值
* @param int $expire 过期时间(秒)
* @return bool 是否为新元素
*/
public function checkAndSet($key, $value, $expire = 3600) {
// 使用Set的SISMEMBER检查是否存在
$isNew = !$this->redis->sIsMember($key, $value);
if ($isNew) {
// 使用事务确保原子性
$this->redis->multi();
$this->redis->sAdd($key, $value);
$this->redis->expire($key, $expire);
$this->redis->exec();
}
return $isNew;
}
/**
* 批量去重检查
*/
public function batchCheck($key, array $values) {
$exists = [];
foreach ($values as $value) {
$exists[$value] = $this->redis->sIsMember($key, $value);
}
return $exists;
}
/**
* 清理过期数据
*/
public function cleanExpired($key) {
$this->redis->del($key);
}
}
// 使用示例
$dedup = new RedisDeduplication();
// 防止用户重复点赞
$userId = 1001;
$postId = 2001;
$dedupKey = "like:{$postId}";
if ($dedup->checkAndSet($dedupKey, $userId, 86400)) {
echo "点赞成功";
// 更新点赞数等业务逻辑
} else {
echo "您已经点过赞了";
}
场景选型建议
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 小规模数据去重 | Redis Set | 简单、功能完整 |
| 需要排序的去重 | ZSet | 带分数排序 |
| 大规模去重(允许误差) | HyperLogLog | 内存占用极小 |
| 超大规模去重 | Bloom Filter | 空间效率极高 |
| 需要过期时间的去重 | Set + expire | 自动清理 |
注意事项
-
原子性问题:使用事务或Lua脚本保证原子性
// Lua脚本实现原子操作 $lua = " local exists = redis.call('SISMEMBER', KEYS[1], ARGV[1]) if exists == 0 then redis.call('SADD', KEYS[1], ARGV[1]) redis.call('EXPIRE', KEYS[1], ARGV[2]) return 1 end return 0 "; $result = $redis->eval($lua, [$key, $value, $expire], 1); -
内存控制:使用
MEMORY USAGE命令监控集合大小 -
序列化处理:复杂对象需序列化后再存储
-
键命名规范:使用分隔的命名空间
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播