Redis实现秒杀的问题怎么解决

网友投稿 461 2023-11-24

Redis实现秒杀的问题怎么解决

本篇内容介绍了“Redis实现秒杀的问题怎么解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

1、秒杀逻辑

Redis实现秒杀的问题怎么解决

秒杀:解决计数器和人员记录的事务操作

1.uid和proid非空判断

2.连接redis

3.拼接key

库存key

秒杀成功用户key

4.获取库存,如果库存为null,秒杀还没开始

5.判断用户是否重复秒杀操作

6.判断商品数量,库存数量小于1,秒杀结束

7.秒杀过程

库存-1

把秒杀成功用户添加清单里面

2、存在问题

2.1、连接超时

原因:由于大量创建连接,十分消耗性能,并且有时获取连接不及时,出现连接超时的情况

2.2、超卖

在并发的情况下发生的,就是在输出没有库存(秒杀结束)后还有商品售出导致库存数量为负数。

2.3、库存遗留

使用乐观锁解决问题2之后,出现问题3

如果库存数量相对并发更多,由于使用乐观锁,第一个用户秒杀成功后会修改库存键的版本号,其他抢到的用户会因为版本号不同导致无法继续购买,就会有库存遗留问题

3、解决

3.1、连接超时

使用连接池,工具类如下:

public class JedisPoolUtil { private static volatile JedisPool jedisPool = null; private JedisPoolUtil() { } public static JedisPool getJedisPoolInstance() { if (null == jedisPool) { synchronized (JedisPoolUtil.class{ if (null == jedisPool) { JedisPoolConfig poolConfig = newJedisPoolConfig(); poolConfig.setMaxTotal(200); poolConfig.setMaxIdle(32); poolConfig.setMaxWaitMillis(100 * 1000); poolConfig.setBlockWhenExhausted(true); poolConfig.setTestOnBorrow(true); jedisPool =new JedisPool(poolConfig, "127.0.0.1"637960000); } } } returnjedisPool; }}//使用JedisPool jedisPoolInstance = JedisPoolUtil.getJedisPoolInstance();Jedis jedis = jedisPoolInstance.getResource();

springBoot版本(pom.xml引入,application.yml配置,然后注入对象即可)

<dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency>     <groupId>redis.clients</groupId>     <artifactId>jedis</artifactId>     <version>3.2.0</version></dependency>spring:   redis:     host: 127.0.0.1    port: 6379     database: 0     timeout: 1800000     lettuce:       pool:         max-active: 20         max-wait: -1         max-idle: 5         min-idle: 0    @Autowired     private RedisTemplate redisTemplate;

3.2、超卖问题

使用Redis事务,乐观锁 + watch

//监视库存 jedis.watch(kcKey);//中间代码忽略 //7 秒杀过程 //使用事务 Transaction multi = jedis.multi();//组队操作multi.decr(kcKey);multi.sadd(userKey,uid);//执行 List<Object> results = multi.exec();if(results == null|| results.size()==0) {     System.out.println("秒杀失败了....");     jedis.close();     return false;}

3.3、乐观锁导致的库存遗留问题

使用Lua嵌入式脚本语言

将复杂的或者多步的 Redis 操作,写为一个脚本,一次提交给Redis运行,减少反复连接 reids的次数。提升性能。

LUA脚本是类似 redis 事务,有一定的原子性,不会被其他命令插队,可以完成redis事务性的操作

LUA脚本功能,在Redis 2.6以上的版本才可以使用

利用 lua 脚本淘汰用户,解决超卖问题。

redis 2.6 版本以后,通过 lua 脚本解决争抢问题,实际上是redis 利用其单线程的特性,用任务队列的方式解决多任务并发问题

local userid=KEYS[1]; //12行定义两个变量, localprodid=KEYS[2]; local qtkey="sk:"..prodid..":qt"; //3,4行定义拼接key local usersKey="sk:"..prodid..":usr"; localuserExists=redis.call("sismember",usersKey,userid); //5-8,判断用户是否存在,不存在return 2 if tonumber(userExists)==1 then     return2; end local num=redis.call("get",qtkey); //9-11,判断商品是否存在 if tonumber(num)<=0 then     return 0; else //12-15,用户和商品操作     redis.call("decr",qtkey);     redis.call("sadd",usersKey,userid); end return1;   //最后一行return 1;  秒杀成功

完整代码如下:

// 定义两段Lua脚本(使用Lua脚本可以解决乐观锁带来的库存遗留问题) static StringsecKillScript ="local userid=KEYS[1];\r\n" + "local prodid=KEYS[2];\r\n" + "local qtkey=sk:..prodid..\":qt\";\r\n" + "local usersKey=sk:..prodid..\":usr\";\r\n" + "local userExists=redis.call(\"sismember\",usersKey,userid);\r\n" + "if tonumber(userExists)==1 then \r\n" + "   return 2;\r\n" + "end\r\n" + "local num= redis.call(\"get\" ,qtkey);\r\n" + "if tonumber(num)<=0 then \r\n" + "   return 0;\r\n" + "else \r\n" + "   redis.call(\"decr\",qtkey);\r\n" + "   redis.call(\"sadd\",usersKey,userid);\r\n" + "end\r\n" + "return 1" ;     public static boolean doSecKill(String uid,Stringprodid) throws IOException {   JedisPool jedispool =  JedisPoolUtil.getJedisPoolInstance(); Jedis jedis=jedispool.getResource(); jedis.select(2);   // 通过jedis的scriptLoad方法加载Lua脚本 String sha1=  jedis.scriptLoad(secKillScript); //通过jedis的evalsha方法调用Lua脚本 Object result= jedis.evalsha(sha1, 2, uid,prodid);   String reString=String.valueOf(result); if ("0".equals( reString )  ) { System.err.println("已抢空!!"); }else if("1".equals( reString )  )  { System.out.println("抢购成功!!!!"); }else if("2".equals( reString )  )  { System.err.println("该用户已抢过!!"); }else{ System.err.println("抢购异常!!"); } jedis.close();return true; }

“Redis实现秒杀的问题怎么解决”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:2019年10月数据库流行度排行:国产数据库鲲鹏正举 PostgreSQL同比增幅第一
下一篇:DNSPod十问简丽荣:国产数据库的月亮与六便士
相关文章

 发表评论

暂时没有评论,来抢沙发吧~