Redis实现秒杀功能

用Redis实现各种秒杀功能

前言

个人经验总结的一些观点看法,阅读了各大平台提供的关于秒杀实现的资料,总结的仅供参考,文中有写的不对的地方请指正!

秒杀设计

一般处理秒杀有三种思路:
分布式处理
异步处理
写入内存处理

分布式处理

分布式处理就是把大量的请求分散到多个服务器上运行,一般使用hash实现均匀分布(通过支持MD5与MurmurHash两种计算方式,默认是采用MurmurHash,高效的hash计算实现均匀分布;还有Paxos算法;Hash Ring实现)
分布式锁也可以实现:通过setnx(lock_timeout)实现,如果设置了锁返回1, 已经有值没有设置成功返回0
死锁问题:主要通过获取过期时间,获取到过期时间get,然后判断getset是否与get相等。

异步处理

秒杀一般时间短,并发大,负载压力大。所以使用异步处理,后台启动多个线程从内存池中异步读取数据,进行处理。异步处理通常使用MQ处理(处理性能高,还能熔断),也可以使用Celery异步,
消息队列:主要目的是减少请求响应时间和解耦。所以主要的使用场景就是将比较耗时而且不需要即时(同步)返回结果的操作作为消息放入消息队列。同时由于使用了消息队列,只要保证消息格式不变,消息的发送方和接收方并不需要彼此联系,也不需要受对方的影响,即解耦和。

写入内存处理

主要还是SSD硬盘比传统的硬盘快50~100倍吧,所以他的读写性能比较好,使系统的能力提示,能减少服务器。
出现宕机问题解决:订单持久化写入硬盘中,也可以做多机备份,而且要是热备份,在主节点不可用的情况下可以切换到备份。

并发简单处理思路:

1、秒杀活动前,把商品和库存存入Redis;
2、商品下单的方法中增加一个计数器,以商品为KEY,计数器为原子性;
3、提交订单时,从redis中获取商品的库存量,和计数器中的count比较,如果大于等于count,说明秒杀完了
4、同步库存量到数据库
这里可以加一些数据库事务,乐观锁,队列处理。

incr实现商品秒杀

每个请求过来执行一次incr,由于redis单线程特性,incr是串行执行的。当incr返回值大于抢购商品的库存时,直接提示抢购失败。如果返回值小于商品库存,抢购成功。但是用这个方法不能解决超卖问题

超卖问题

假设有100件商品,两个请求同时过来,两人都判断还剩1件商品,未达到上限,两个确认抢购成功,这就是超卖现象,实际中是经常会遇到的。
淘宝中除了用Redis处理秒杀,还要去修改数据库底层实现秒杀。
(下面两点套用了 土豆科技大佬的代码,自己懒得去写了)

1、生成库存的计数器
	public function maple()
    {
        $count=999; // 库存
        //添加到redis list中
        for($i=0;$i<$count;$i++){
            Predis::getInstance()->lpush('maple', 1234515125);
        }
        self::dd(Predis::getInstance()->lrange('maple',0,-1));
    }
2、使用lpop解决超卖
	public function sale()
	{  
    if (Predis::getInstance()->lpop('maple')) {
        $user=User::where('user_id', 1)->find();
        Predis::getInstance()->lpush('user',$user['user_id']);
		Predis::getInstance()->incr('number');
        echo '加入秒杀成功';exit();
    }else{
        echo '活动截至';
        exit();
    }
}

超发处理:

connect('127.0.0.1', 7379);
$redis->watch("mywatchlist");
$len = $redis->hlen("mywatchlist");
$rob_total = 100; //抢购数量
if ($len < $rob_total) {	
$redis->multi();
$redis->hSet("mywatchlist", "user_id_" . mt_rand(1, 999999), time());
$rob_result = $redis->exec();
file_put_contents("log.txt", $len . PHP_EOL, FILE_APPEND);
if ($rob_result) {
    $mywatchlist = $redis->hGetAll("mywatchlist");
    echo "抢购成功!
"; echo "剩余数量:" . ($rob_total - $len - 1) . "
"; echo "用户列表:
";
    var_dump($mywatchlist);
} else {
    echo "手气不好,再抢购!";
    exit;
}
} else {	
	echo "已卖光!";
	exit;
}
?>

你可能感兴趣的:(redis)