设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间23 现在时间23 缓存数据 荣耀也罢,屈辱也罢,都要以平和的心态去面对,少一些无奈与感慨,多一份从容和淡然。晚安!

荣耀也罢,屈辱也罢,都要以平和的心态去面对,少一些无奈与感慨,多一份从容和淡然。晚安!

查看: 892|回复: 0

redis队列和秒杀应用方式

[复制链接]

  离线 

TA的专栏

  • 打卡等级:即来则安
  • 打卡总天数:17
  • 打卡月天数:1
  • 打卡总奖励:224
  • 最近打卡:2025-12-04 01:32:49
等级头衔

等級:晓枫资讯-上等兵

在线时间
0 小时

积分成就
威望
0
贡献
338
主题
282
精华
0
金钱
1160
积分
656
注册时间
2023-2-11
最后登录
2025-12-4

发表于 2025-9-1 08:14:17 | 显示全部楼层 |阅读模式
1.简述

redis队列一般用于缓解数据库压力 ,诸如秒杀,邮件群发,消息推送等等
redis的加入能很好的 帮助系统中 各个模块解耦。
而Redis不仅可作为缓存服务器,还可用作消息队列。它的列表类型天生支持用作消息队列。如下图所示:
1.png

对于服务器减少io 压力 有一定的帮助

2.秒杀的原理

2.jpeg

秒杀基本原理比较简单
用户点击抢购按钮 -> 把uid 和时间存入redis的队列中 -> 服务器中有一个入库程序不停轮询redis队列是否有数据 -> 如果有存入数据库
这里面有2点需要注意一下:

  • 1. 插入队列的时候 ,需要判断库传,不能出现多插入
  • 2. 在入库的时候 如果出现数据插入失败的情况 需要进行回滚

3.秒杀的代码实现

用户操作秒杀:
  1. header("Content-type: text/html; charset=utf-8");
  2. $redis = new Redis();
  3. $redis->connect('127.0.0.1', 6379);

  4. $redis_name= "miaosha";
  5. //库存
  6. $nums = 10;
  7. //用户id
  8. $user_id = $_GET['uid'];
  9. if(($redis->llen($redis_name)) <  $nums ){
  10.         $redis->lpush($redis_name,json_encode(array('uid'=>$user_id,'time'=>microtime())));
  11.         echo $user_id."秒杀成功!";
  12.         exit();
  13. }else{
  14.         echo "秒杀失败!";
  15.         exit();
  16. }
  17. $redis->close();
复制代码
后台处理秒杀队列:
  1. header("Content-type: text/html; charset=utf-8");
  2. error_reporting(E_ALL);
  3. require_once './db.php';
  4. $redis = new Redis();
  5. $redis->connect('127.0.0.1', 6379);

  6. $redis_name = "miaosha";

  7. //数据库
  8. $configs =array('host'=>'127.0.0.1','port'=>'3306','user'=>'***','passwd'=>'','dbname'=>'test');
  9. $mysql = new MMysql($configs);

  10. //处理开始
  11. while ($count = $redis->lLen($redis_name)) {
  12.     $task = $redis->rpop($redis_name);
  13.        
  14.     $taskdata = json_decode($task, true);
  15.         $data = array(
  16.             'uid'=>$taskdata['uid'],
  17.             'time'=>$taskdata['time'],
  18.     );

  19.         $rs = $mysql->insert('redis',$data);
  20.         if(!$rs){
  21.                 //由于我们是在右边取,所以如果数据插入失败了要从左边放回去(重新排队),以免影响队列中其他元素的处理
  22.                 $redis->lpush($redis_name,$task);
  23.                 echo "处理失败<br>";
  24.         }else{
  25.                 echo "处理成功<br>";
  26.         }
  27.    
  28.         sleep(1);
  29. }

  30. $redis->close();
复制代码
4.关于redis里的锁


4.1 先说一下乐观锁

乐观锁,顾名思义,乐观的认为数据不会被修改,只有当更新时才去判断数据是否被修改过,通常用版本号或时间戳来实现。
redis中的事务通过watch和multi来实现。
WATCH命令可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行。监控一直持续到EXEC命令(事务中的命令是在EXEC之后才执行的,所以在MULTI命令后可以修改WATCH监控的键值)
  1. $redis = new Redis();
  2. $redis->connect('127.0.0.1', 6379, 60);

  3. //
  4. $tnums = $redis->get('goods_stock_nums');

  5. //设置商品的库存
  6. if($tnums==0){
  7.         echo "活动已经结束,明天请早end!";
  8.         exit();
  9. }

  10. //监视该key
  11. $redis->watch('goods_stock_nums');

  12. //sleep(5);
  13. //开启事务
  14. $redis->multi();

  15. //修改库存数
  16. $redis->decr('goods_stock_nums');

  17. //提交事务,如果在此期间有其他请求修改了该key,那么事务会失败
  18. if ($redis->exec()) {
  19.     echo '抢购成功suc';
  20. } else {
  21.     echo '数据错误,请重新再试fail';
  22. }
复制代码
可以看到我在程序中加入了sleep(5) 这行代码。
这是方便我在客户端去实验
3.png

如果我在运行上面这段代码过程中,我用客户端修改了这个值。
那么上面这段代码就会失败,返回 数据错误,请重新再试fail

4.2 再说一下 悲观锁
  1. function getRedis()
  2. {
  3.     $redis = new Redis();
  4.     $redis->connect('127.0.0.1', 6379, 60);
  5.     return $redis;
  6. }

  7. function lock($key, $random)
  8. {
  9.     $redis = getRedis();
  10.     return $redis->set($key, $random, ['nx', 'ex' => 3]);
  11. }

  12. function unlock($key, $random)
  13. {
  14.     $redis = getRedis();
  15.     //使用lua脚本保证原子性
  16.     $script = 'if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end';
  17.     return $redis->eval($script, [$key, $random], 1);
  18. }

  19. function decrGoodsStockNums()
  20. {
  21.     $redis = getRedis();

  22.     //获取商品库存数
  23.     $ret = $redis->get('goods_stock_nums');

  24.     if ($ret === false) {
  25.         return false;
  26.     }

  27.     if ($ret <= 0) {
  28.         return false;
  29.     }

  30.     $random = mt_rand();
  31.     //先获取锁
  32.     if (lock('goods_stock_nums_lock', $random)) {
  33.         //修改库存数
  34.         $redis->decr('goods_stock_nums');

  35.         //释放锁
  36.         unlock('goods_stock_nums_lock', $random);
  37.         return true;
  38.     } else {
  39.         usleep(100);
  40.         decrGoodsStockNums();
  41.     }
  42. }

  43. decrGoodsStockNums();
复制代码
上面这段引用别人的代码
但是这种锁的机制还是不能保证事务的安全

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持晓枫资讯。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表