Redis实现秒杀的问题怎么解决
本篇内容介绍了“Redis实现秒杀的问题怎么解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
1、秒杀逻辑秒杀:解决计数器和人员记录的事务操作
1.uid和proid非空判断
2.连接redis
3.拼接key
库存key
秒杀成功用户key
4.获取库存,如果库存为null,秒杀还没开始
5.判断用户是否重复秒杀操作
6.判断商品数量,库存数量小于1,秒杀结束
7.秒杀过程
库存-1
把秒杀成功用户添加清单里面
2、存在问题2.1、连接超时原因:由于大量创建连接,十分消耗性能,并且有时获取连接不及时,出现连接超时的情况
2.2、超卖在并发的情况下发生的,就是在输出没有库存(秒杀结束)后还有商品售出导致库存数量为负数。
使用乐观锁解决问题2之后,出现问题3
如果库存数量相对并发更多,由于使用乐观锁,第一个用户秒杀成功后会修改库存键的版本号,其他抢到的用户会因为版本号不同导致无法继续购买,就会有库存遗留问题
3、解决3.1、连接超时使用连接池,工具类如下:
publicclassJedisPoolUtil{privatestaticvolatileJedisPooljedisPool=null;privateJedisPoolUtil(){}publicstaticJedisPoolgetJedisPoolInstance(){if(null==jedisPool){synchronized(JedisPoolUtil.class){if(null==jedisPool){JedisPoolConfigpoolConfig=newJedisPoolConfig();poolConfig.setMaxTotal(200);poolConfig.setMaxIdle(32);poolConfig.setMaxWaitMillis(100*1000);poolConfig.setBlockWhenExhausted(true);poolConfig.setTestOnBorrow(true);jedisPool=newJedisPool(poolConfig,"127.0.0.1",6379,60000);}}}returnjedisPool;}}//使用JedisPooljedisPoolInstance=JedisPoolUtil.getJedisPoolInstance();Jedisjedis=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.1port:6379database:0timeout:1800000lettuce:pool:max-active:20max-wait:-1max-idle:5min-idle:0
@AutowiredprivateRedisTemplateredisTemplate;3.2、超卖问题
使用Redis事务,乐观锁 + watch
//监视库存jedis.watch(kcKey);//中间代码忽略//7秒杀过程//使用事务Transactionmulti=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();returnfalse;}3.3、乐观锁导致的库存遗留问题
使用Lua嵌入式脚本语言
将复杂的或者多步的 Redis 操作,写为一个脚本,一次提交给Redis运行,减少反复连接 reids的次数。提升性能。
LUA脚本是类似 redis 事务,有一定的原子性,不会被其他命令插队,可以完成redis事务性的操作
LUA脚本功能,在Redis 2.6以上的版本才可以使用
利用 lua 脚本淘汰用户,解决超卖问题。
redis 2.6 版本以后,通过 lua 脚本解决争抢问题,实际上是 redis 利用其单线程的特性,用任务队列的方式解决多任务并发问题。
localuserid=KEYS[1];//1、2行定义两个变量,localprodid=KEYS[2];localqtkey="sk:"..prodid..":qt";//3,4行定义拼接keylocalusersKey="sk:"..prodid..":usr";localuserExists=redis.call("sismember",usersKey,userid);//5-8,判断用户是否存在,不存在return2iftonumber(userExists)==1thenreturn2;endlocalnum=redis.call("get",qtkey);//9-11,判断商品是否存在iftonumber(num)<=0thenreturn0;else//12-15,用户和商品操作redis.call("decr",qtkey);redis.call("sadd",usersKey,userid);endreturn1;//最后一行return1;秒杀成功
完整代码如下:
//定义两段Lua脚本(使用Lua脚本可以解决乐观锁带来的库存遗留问题)staticStringsecKillScript="localuserid=KEYS[1];\r\n"+"localprodid=KEYS[2];\r\n"+"localqtkey='sk:'..prodid..\":qt\";\r\n"+"localusersKey='sk:'..prodid..\":usr\";\r\n"+"localuserExists=redis.call(\"sismember\",usersKey,userid);\r\n"+"iftonumber(userExists)==1then\r\n"+"return2;\r\n"+"end\r\n"+"localnum=redis.call(\"get\",qtkey);\r\n"+"iftonumber(num)<=0then\r\n"+"return0;\r\n"+"else\r\n"+"redis.call(\"decr\",qtkey);\r\n"+"redis.call(\"sadd\",usersKey,userid);\r\n"+"end\r\n"+"return1";publicstaticbooleandoSecKill(Stringuid,Stringprodid)throwsIOException{JedisPooljedispool=JedisPoolUtil.getJedisPoolInstance();Jedisjedis=jedispool.getResource();jedis.select(2);//通过jedis的scriptLoad方法加载Lua脚本Stringsha1=jedis.scriptLoad(secKillScript);//通过jedis的evalsha方法调用Lua脚本Objectresult=jedis.evalsha(sha1,2,uid,prodid);StringreString=String.valueOf(result);if("0".equals(reString)){System.err.println("已抢空!!");}elseif("1".equals(reString)){System.out.println("抢购成功!!!!");}elseif("2".equals(reString)){System.err.println("该用户已抢过!!");}else{System.err.println("抢购异常!!");}jedis.close();returntrue;}
“Redis实现秒杀的问题怎么解决”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。