欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java使用Redis实现秒杀功能

程序员文章站 2022-03-11 10:08:46
秒杀功能秒杀场景现在已经非常常见了,各种电商平台都有秒杀的产品,接下来我们模拟一个秒杀的项目,最终能够确保高并发下的线程安全。界面比较简单,但是功能基本实现。界面点击“秒杀点我”按钮后台就会输出秒杀结...

秒杀功能

秒杀场景现在已经非常常见了,各种电商平台都有秒杀的产品,接下来我们模拟一个秒杀的项目,最终能够确保高并发下的线程安全。界面比较简单,但是功能基本实现。

界面

Java使用Redis实现秒杀功能

点击“秒杀点我”按钮后台就会输出秒杀结果。

Java使用Redis实现秒杀功能

第一版

使用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

结果

Java使用Redis实现秒杀功能

从结果大致来看,没有什么问题,来查看一个后台redis的数据

秒杀的结果里面居然有负数,证明卖超了。

Java使用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;
 }
}

结果

Java使用Redis实现秒杀功能

由于使用了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;
 
 }
}

结果

Java使用Redis实现秒杀功能

这才是我们最希望看到的结果!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

相关标签: Java Redis 秒杀