18人参与 • 2025-04-22 • Redis
redis队列一般用于缓解数据库压力 ,诸如秒杀,邮件群发,消息推送等等
redis的加入能很好的 帮助系统中 各个模块解耦。
而redis不仅可作为缓存服务器,还可用作消息队列。它的列表类型天生支持用作消息队列。如下图所示:
对于服务器减少io 压力 有一定的帮助
秒杀基本原理比较简单
用户点击抢购按钮 -> 把uid 和时间存入redis的队列中 -> 服务器中有一个入库程序不停轮询redis队列是否有数据 -> 如果有存入数据库
这里面有2点需要注意一下:
用户操作秒杀:
header("content-type: text/html; charset=utf-8"); $redis = new redis(); $redis->connect('127.0.0.1', 6379); $redis_name= "miaosha"; //库存 $nums = 10; //用户id $user_id = $_get['uid']; if(($redis->llen($redis_name)) < $nums ){ $redis->lpush($redis_name,json_encode(array('uid'=>$user_id,'time'=>microtime()))); echo $user_id."秒杀成功!"; exit(); }else{ echo "秒杀失败!"; exit(); } $redis->close();
后台处理秒杀队列:
header("content-type: text/html; charset=utf-8"); error_reporting(e_all); require_once './db.php'; $redis = new redis(); $redis->connect('127.0.0.1', 6379); $redis_name = "miaosha"; //数据库 $configs =array('host'=>'127.0.0.1','port'=>'3306','user'=>'***','passwd'=>'','dbname'=>'test'); $mysql = new mmysql($configs); //处理开始 while ($count = $redis->llen($redis_name)) { $task = $redis->rpop($redis_name); $taskdata = json_decode($task, true); $data = array( 'uid'=>$taskdata['uid'], 'time'=>$taskdata['time'], ); $rs = $mysql->insert('redis',$data); if(!$rs){ //由于我们是在右边取,所以如果数据插入失败了要从左边放回去(重新排队),以免影响队列中其他元素的处理 $redis->lpush($redis_name,$task); echo "处理失败<br>"; }else{ echo "处理成功<br>"; } sleep(1); } $redis->close();
乐观锁,顾名思义,乐观的认为数据不会被修改,只有当更新时才去判断数据是否被修改过,通常用版本号或时间戳来实现。
redis中的事务通过watch和multi来实现。
watch命令可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行。监控一直持续到exec命令(事务中的命令是在exec之后才执行的,所以在multi命令后可以修改watch监控的键值)
$redis = new redis(); $redis->connect('127.0.0.1', 6379, 60); // $tnums = $redis->get('goods_stock_nums'); //设置商品的库存 if($tnums==0){ echo "活动已经结束,明天请早end!"; exit(); } //监视该key $redis->watch('goods_stock_nums'); //sleep(5); //开启事务 $redis->multi(); //修改库存数 $redis->decr('goods_stock_nums'); //提交事务,如果在此期间有其他请求修改了该key,那么事务会失败 if ($redis->exec()) { echo '抢购成功suc'; } else { echo '数据错误,请重新再试fail'; }
可以看到我在程序中加入了sleep(5) 这行代码。
这是方便我在客户端去实验
如果我在运行上面这段代码过程中,我用客户端修改了这个值。
那么上面这段代码就会失败,返回 数据错误,请重新再试fail
function getredis() { $redis = new redis(); $redis->connect('127.0.0.1', 6379, 60); return $redis; } function lock($key, $random) { $redis = getredis(); return $redis->set($key, $random, ['nx', 'ex' => 3]); } function unlock($key, $random) { $redis = getredis(); //使用lua脚本保证原子性 $script = 'if redis.call("get",keys[1]) == argv[1] then return redis.call("del",keys[1]) else return 0 end'; return $redis->eval($script, [$key, $random], 1); } function decrgoodsstocknums() { $redis = getredis(); //获取商品库存数 $ret = $redis->get('goods_stock_nums'); if ($ret === false) { return false; } if ($ret <= 0) { return false; } $random = mt_rand(); //先获取锁 if (lock('goods_stock_nums_lock', $random)) { //修改库存数 $redis->decr('goods_stock_nums'); //释放锁 unlock('goods_stock_nums_lock', $random); return true; } else { usleep(100); decrgoodsstocknums(); } } decrgoodsstocknums();
上面这段引用别人的代码
但是这种锁的机制还是不能保证事务的安全
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论