Java使用Redis实现秒杀功能
程序员文章站
2022-03-11 10:08:46
秒杀功能秒杀场景现在已经非常常见了,各种电商平台都有秒杀的产品,接下来我们模拟一个秒杀的项目,最终能够确保高并发下的线程安全。界面比较简单,但是功能基本实现。界面点击“秒杀点我”按钮后台就会输出秒杀结...
秒杀功能
秒杀场景现在已经非常常见了,各种电商平台都有秒杀的产品,接下来我们模拟一个秒杀的项目,最终能够确保高并发下的线程安全。界面比较简单,但是功能基本实现。
界面
点击“秒杀点我”按钮后台就会输出秒杀结果。
第一版
使用redis缓存数据库,使用一个key-value存储秒杀商品数量,使用set集合存储秒杀成功的用户。我们以商品0101为示例,设置商品的初始数量为200件。不考虑并发问题,实现功能。
html、jsp、servlet文件不重要省略。
package com.redis.secondskill; import java.util.list; import redis.clients.jedis.jedis; import redis.clients.jedis.jedispool; import redis.clients.jedis.transaction; public class ss0 { public static boolean doseckill(string uid,string prodid) { jedispool jedispool = jedispolltool.getinstance(); jedis jedis = jedispool.getresource(); string productcountstr = "sec:"+prodid+":count"; string productuserstr = "sec:"+prodid+":user"; string productcount = jedis.get(productcountstr); if(null == productcount) { system.out.println("秒杀还没有开始"); jedispolltool.distroy(jedispool, jedis); return false; } if(jedis.sismember(productuserstr, uid)) { system.out.println(uid + "用户已经秒杀成功"); jedispolltool.distroy(jedispool, jedis); return false; } int prodcount = integer.parseint(productcount); if(prodcount <= 0) { system.out.println("秒杀结束"); jedispolltool.distroy(jedispool, jedis); return false; } jedis.decr(productcountstr); jedis.sadd(productuserstr, uid); jedispolltool.distroy(jedispool, jedis); system.out.println(uid + "秒杀成功"); return true; } }
使用linux httpd-tools工具进行并发测试。
ab -n 1000 -c 200 -p /test/file.txt -t "application/x-www-form-urlencoded" 192.168.0.101:8080/redis-demo/ss
结果
从结果大致来看,没有什么问题,来查看一个后台redis的数据
秒杀的结果里面居然有负数,证明卖超了。
第二版
使用redis的事务,保证没有超卖的情况发生。
package com.redis.secondskill; import java.util.list; import redis.clients.jedis.jedis; import redis.clients.jedis.jedispool; import redis.clients.jedis.transaction; public class ss1 { public static boolean doseckill(string uid,string prodid) { jedispool jedispool = jedispolltool.getinstance(); jedis jedis = jedispool.getresource(); string productcountstr = "sec:"+prodid+":count"; string productuserstr = "sec:"+prodid+":user"; jedis.watch(productcountstr); //开始监视 string productcount = jedis.get(productcountstr); if(null == productcount) { system.out.println("秒杀还没有开始"); jedispolltool.distroy(jedispool, jedis); return false; } if(jedis.sismember(productuserstr, uid)) { system.out.println(uid + "用户已经秒杀成功"); jedispolltool.distroy(jedispool, jedis); return false; } int prodcount = integer.parseint(productcount); if(prodcount <= 0) { system.out.println("秒杀结束"); jedispolltool.distroy(jedispool, jedis); return false; } transaction transaction = jedis.multi(); transaction.decr(productcountstr); transaction.sadd(productuserstr, uid); list<object> exec = transaction.exec(); if(exec == null || exec.size() == 0) { system.out.println("秒杀失败,稍后重试"); jedispolltool.distroy(jedispool, jedis); return false; } jedispolltool.distroy(jedispool, jedis); system.out.println(uid + "秒杀成功"); return true; } }
结果
由于使用了watch和事务,每次的并发线程访问中只有一个线程能够提交成功,可以保证不出现超卖的现象,但是对于一些用户来说是极其不公平的。
第三版
使用lua脚本来实现,因为redis是单线程的,又是c语言编写的,可以使用lua调用redis的命令,lua会具有排他性,所以能够保证安全。
package com.redis.secondskill; import java.util.hashset; import java.util.set; import redis.clients.jedis.hostandport; import redis.clients.jedis.jedis; import redis.clients.jedis.jedispool; public class ss2 { static string luascript ="local userid=keys[1];\r\n" + "local prodid=keys[2];\r\n" + "local qtkey='sec:'..prodid..\":count\";\r\n" + "local userskey='sec:'..prodid..\":user\";\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,string prodid) { jedispool jedispool = jedispolltool.getinstance(); jedis jedis = jedispool.getresource(); string sha1 = jedis.scriptload(luascript); 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(uid + "抢购成功!!!!"); }else if("2".equals( restring ) ) { system.err.println("该用户已抢过!!"); }else{ system.err.println("抢购异常!!"); } jedispolltool.distroy(jedispool, jedis); return true; } }
结果
这才是我们最希望看到的结果!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
下一篇: Edraw Max亿图图示怎么画海星鱼?
推荐阅读
-
python+selenium实现京东自动登录及秒杀功能
-
php使用curl模拟多线程实现批处理功能示例
-
thinkPHP5.1框架使用SemanticUI实现分页功能示例
-
使用python 3实现发送邮件功能
-
使用Jquery+Ajax+Json如何实现分页显示附JAVA+JQuery实现异步分页
-
java实现CSV文件导入与导出功能
-
ThinkPHP5&5.1实现验证码的生成、使用及点击刷新功能示例
-
java使用this调用构造函数的实现方法示例
-
使用wxapp-img-loader自定义组件实现微信小程序图片预加载功能
-
Android开发之TextView使用intent传递信息,实现注册界面功能示例