Redis基础之【3.Net Core对接Redis实现简单秒杀功能】

Redis基础之【3.Net Core对接Redis实现简单秒杀功能】

一. 简单秒杀

在这里插入图片描述

  1.秒杀开始之前,先将商品预设的秒杀库存数量加载到Redis中;
  2.后台服务1接收到秒杀请求时,先判断Redis库存是狗足够,如果足够执行第三步,如果库存不足时,直接返回秒杀失败;
  3.减掉Redis预设库存并将请求放入异步队列中,返回正在排队中;
  4.后台服务2用于消费队列,获取队列信息后首先判断真实库存是否足够,如果库存不足,直接返回秒杀失败,如果库存足够就生成订单信息并通知用户秒杀成功。

  在这个demo,我们设置30个Redis秒杀数量。模拟100个请求进行抢购。

  后台服务1:效果抢购效果如下

在这里插入图片描述

  后台服务2:消费队列效果如下

在这里插入图片描述
  这就完了吗?站住!滑走你就草率了。

  这样的写法会出现超卖!为什么呢???
  在这里插入图片描述
  真的出现了超卖的情况!为什么呢???想想就知道了,so easy,假设:
  当A查到redis库存为1的时候,做减库存之前,B也查到了库存,也是1,C也查到了库存,也是1;
  这个时候A和B和C都进入了redis的自减命令Decr,会执行3次Decr,就变成了-2(也就是说队列的生产者多生产了2条数据进入了队列,消费端必然会多消费这2条数据

  那么怎么解决超卖的问题呢?

-----方法1:如果是单个客户端进行抢购,对线程加锁即可,如果有多个客户端进行抢购,就需要分布式锁。(尽量能不用就不用,坑较多。)
-----方法2:怎么办,我就是不想用分布式锁,行不行?当然可以,只要利用redis单线程原子性,就可以实现

在这里插入图片描述

在这里插入图片描述

  注意:因为redis它在处理命令或者指令的时候是单线程的 (单线程原子性,不需要锁,同时避免上下文切换出现的性能消耗,速度不一定比多线程慢)。也就是说我们发送给redis服务的指令,不管在任何时候,在同一时刻只会执行一条指令,合理利用自减命令Decr是不会出现“超卖”的。

二. 总结

  本文使用String和List的完成的秒杀小Demo,目标只是为了简单入门理解。真实开发需要做的封装和优化,Redis适合做缓存和分布式锁,但是Redis做消息队列如果出现系统宕机的情况容易信息丢失并且没有自己的ack机制,可靠性不够高,适用于对信息准确性要求不高的场景。
  然后,还有,在高并发场景下,假如在队列消费端,需要真正的减去库存,这个时候数据库必然不是Redis,那么如果消费端也搭建了集群,必然会出现同时查库存减库存的操作(非原子性的库存校验导致在并发场景下,库存校验的结果不准确。这是超卖的根本原因),“超卖”问题依然困扰我们,这个时候,就要考虑使用分布式锁了
  但是话说回来,针对秒杀场景,进入队列的数据并不多,大部分情况下是没有必要搭建集群进行消费,刚好好避开了分布式锁。除非有必要,否者建议尽量不要使用分布式锁。
  
在这里插入图片描述

  
  
  

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是用.Net Core web API程序实现顺序消费redis信息的示例代码: ```csharp using StackExchange.Redis; using System; namespace RedisConsumer { public class RedisConsumer { private readonly ConnectionMultiplexer _redis; private readonly IDatabase _database; private readonly string _channelName; public RedisConsumer(string connectionString, string channelName) { _redis = ConnectionMultiplexer.Connect(connectionString); _database = _redis.GetDatabase(); _channelName = channelName; var subscriber = _redis.GetSubscriber(); subscriber.Subscribe(_channelName, (channel, value) => { Console.WriteLine($"Received message from channel {channel}: {value}"); }); } public void Consume() { // This method doesn't need to do anything, // as the subscription will handle the messages. // Just keep the program running. Console.WriteLine($"Listening for messages on channel {_channelName}..."); Console.ReadLine(); } } } ``` 在这个示例中,我们使用StackExchange.Redis库来与Redis服务器进行交互。在构造函数中,我们传入Redis服务器的连接字符串和要订阅的频道名称。然后,我们使用订阅器对象订阅该频道,并在收到消息时打印消息内容。 在Consume方法中,我们只需要等待用户在控制台上敲入任意字符,以保持程序运行状态,让订阅器可以继续监听频道并处理消息。 要使用这个类来订阅Redis频道,我们可以在.Net Core web API程序的某个入口中创建一个实例对象,然后调用Consume方法来开始消费消息。例如: ```csharp using Microsoft.AspNetCore.Mvc; namespace RedisWebApi.Controllers { [ApiController] [Route("[controller]")] public class RedisConsumerController : ControllerBase { private readonly RedisConsumer _redisConsumer; public RedisConsumerController() { _redisConsumer = new RedisConsumer("localhost:6379", "my_channel"); } [HttpGet] public IActionResult Get() { _redisConsumer.Consume(); return Ok(); } } } ``` 在这个示例中,我们创建了一个RedisConsumerController控制器,并在构造函数中创建了一个RedisConsumer对象。然后,在Get方法中,我们调用了Consume方法来开始消费消息。这个方法会一直运行,直到用户在控制台上敲入任意字符。在这个示例中,我们只是返回了一个Ok结果,表示这个方法已经成功被调用,但我们也可以返回其他类型的结果来提供更多的信息或反馈。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值