redis队列和秒杀应用方式

 更新时间:2025年04月22日 09:41:23   作者:程序猿John  
这篇文章主要介绍了redis队列和秒杀应用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

1.简述

redis队列一般用于缓解数据库压力 ,诸如秒杀,邮件群发,消息推送等等

redis的加入能很好的 帮助系统中 各个模块解耦。

而Redis不仅可作为缓存服务器,还可用作消息队列。它的列表类型天生支持用作消息队列。如下图所示:

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

2.秒杀的原理

秒杀基本原理比较简单

用户点击抢购按钮 -> 把uid 和时间存入redis的队列中 -> 服务器中有一个入库程序不停轮询redis队列是否有数据 -> 如果有存入数据库

这里面有2点需要注意一下:

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

3.秒杀的代码实现

用户操作秒杀:

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();

4.关于redis里的锁

4.1 先说一下乐观锁

乐观锁,顾名思义,乐观的认为数据不会被修改,只有当更新时才去判断数据是否被修改过,通常用版本号或时间戳来实现。

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

4.2 再说一下 悲观锁

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();

上面这段引用别人的代码

但是这种锁的机制还是不能保证事务的安全

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Redis全量复制与部分复制示例详解

    Redis全量复制与部分复制示例详解

    这篇文章主要给大家介绍了关于Redis全量复制与部分复制的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Redis爬虫具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-08-08
  • Redis数据一致性详解

    Redis数据一致性详解

    文章主要讨论了分布式系统中的数据一致性模型、缓存使用场景以及数据同步策略,一致性模型包括强一致性、弱一致性和最终一致性,缓存使用场景主要在高并发读取数据时提升性能,数据同步策略分为先删除缓存再更新数据库和先更新数据库再删除缓存两种
    2024-11-11
  • redis监听key过期事件的详细步骤

    redis监听key过期事件的详细步骤

    本文主要介绍了redis监听key过期事件的详细步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • redis中RDB(Redis Data Base)的机制

    redis中RDB(Redis Data Base)的机制

    本文主要介绍了redis中RDB(Redis Data Base)的机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • redis的hash类型操作方法

    redis的hash类型操作方法

    Hash 是一个 String 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象,这篇文章主要介绍了redis的hash类型的详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-06-06
  • Redis五种数据类型详解

    Redis五种数据类型详解

    Redis是基于内存的 K-V 数据库,常用于缓存、消息队列,分布式锁等场景,并且提供了常见的数据结构:字符串、哈希、列表、集合、带排序的集合,本文主要介绍了Redis的五种数据类型,感兴趣的小伙伴可以参考阅读本文
    2023-04-04
  • redis分布式锁之可重入锁的实现代码

    redis分布式锁之可重入锁的实现代码

    相信大家都知道可重入锁的作用防止在同一线程中多次获取锁而导致死锁发生,本文通过几个例子给大家分享redis分布式锁之可重入锁的实现代码,对redis分布式锁的相关知识,感兴趣的朋友一起看看吧
    2021-05-05
  • Redis分片集群的实现

    Redis分片集群的实现

    Redis 分片集群是一种将 Redis数据库分散到多个节点上的方式,以提供更高的性能和可伸缩性,本文主要介绍了Redis分片集群的实现,具有一定的参考价值,感兴趣的可以了解一下
    2025-04-04
  • Redis大key和多key拆分的解决方案

    Redis大key和多key拆分的解决方案

    大key会导致内存使用过高,多key可能导致查询效率低下,本文主要介绍了Redis大key和多key拆分的解决方案,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • Redis fork进程分配不到内存解决方案

    Redis fork进程分配不到内存解决方案

    这篇文章主要介绍了Redis fork进程分配不到内存解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11

最新评论