记一次代码优化实践之Redis集合的妙用

需求:

在用户积分兑换成功后,在页面二次访问时提示已兑换,避免重复扣除积分和重复兑换等情况。

有同学讲了,这个玩意用个计数器不就解决了吗?
说的好,如果不是问题大于方法,确实我也只需要在每次用户进页面的时候,Redis中存上唯一ID,后面再进来判断一下是否兑换过即可。

同时适用于:用户提交答卷之后,异地同时提交另一份造成覆盖等异常情况;其他适用情况,请大家集思广益。

添加数据(定时触发)

<?php
$redis = new \Redis();
$add_count = 0;
$data = Db::query("SELECT `phone`,`site_id` FROM `demo_table` WHERE `creat_time`  >= UNIX_TIMESTAMP() - 60 * 2");// 此处仅展示原始sql语句,实际使用时请注意安全性
foreach ($data as $key => $value) {
    
    
    $phone = $value['phone'];// 用户标识
    $site_id = $value['site_id'];// 站点标识
    $website = 'site_id:'.$site_id;
    $num = $redis->sadd($website, $phone);
    $add_count += $num;// 记录添加数量
}
$count = count($data);// 总数据条数
echo "本次添加{
      
      $add_count}/{
      
      $count}\n";
?>

在这里,我们首先通过查询MySQL数据库里面的手机号(用户标识,可以换成你自己的标识)、站点ID(对应的活动/网址/文章的标识,基于你自己的场景替换)的当前时间2分钟内的数据;在基于数组的循环,将其放到Redis的集合中(同时判断当前是否添加成功);完成后再统计本次总共的数据条数。

校验数据(实际使用)

<?php
$redis = new \Redis();
$site_unique = $redis->sismember('site_id:'. $site_id, $phone);
if ($site_unique !== false) {
    
    
    return false; // 已兑换
}

在这里,我们在入口处使用的时候,直接基于前面Redis中的集合数据进行过滤,如果存在集合中,则说明已经存在(基于你自己的实际场景进行设计);所以这里我们可以直接返回false。

添加数据(优化版)

<?php
$redis = new \Redis();
$add_count = 0;
$data = Db::query("SELECT `phone`,`site_id` FROM `demo_table` WHERE `creat_time`  >= UNIX_TIMESTAMP() - 60 * 2");// 此处仅展示原始 sql 语句, 实际使用时请注意安全性
$redis->multi(); // 开启 Redis 事务
foreach ($data as $key => $value) {
    
    
    $phone = $value['phone'];// 用户标识
    $site_id = $value['site_id'];// 站点标识
    $website = 'site_id:'.$site_id;
    $num = $redis->sadd($website, $phone);
    $add_count += $num;// 记录添加数量
}
$redis->exec(); // 提交 Redis 事务
$count = count($data);// 总数据条数
echo "本次添加{
      
      $add_count}/{
      
      $count}\n";

上面的优化版,是基于AI代码辅助工具(编辑器插件)生成的,由于本人对Redis管道深耕有限,上述Redis事务代码未实际使用,请谨慎尝试。

总结

通过本次优化,学习/复习到了redis集合的相关知识,同时了解到了Redis的管道即事务操作。

猜你喜欢

转载自blog.csdn.net/Glory_Sunshine/article/details/135019540
今日推荐