Java使用Redis实现秒杀功能

作者:楚瑞涛 时间:2023-04-11 11:26:54 

秒杀功能

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

界面

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实现秒杀功能

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

来源:https://blog.csdn.net/cong____cong/article/details/105566983

标签:Java,Redis,秒杀
0
投稿

猜你喜欢

  • Mybatis通过数据库表自动生成实体类和xml映射文件

    2022-01-11 07:05:46
  • C#实现过滤sql特殊字符的方法集合

    2022-01-30 23:58:04
  • Spring Boot配置Thymeleaf(gradle)的简单使用

    2023-04-18 07:41:42
  • java 使用POI合并两个word文档

    2022-09-30 12:22:54
  • Java jpa外连接查询join案例详解

    2022-12-17 18:31:15
  • 基于IntelliJ IDEA的类注释和方法注释操作

    2023-03-04 02:00:31
  • Java实现插入排序算法可视化的示例代码

    2021-08-06 19:35:50
  • Java实现两人五子棋游戏(二) 画出棋盘

    2022-08-26 15:09:57
  • Spring之动态注册bean的实现方法

    2023-09-26 14:01:17
  • Java实现合并多个PDF的示例代码

    2023-04-29 13:25:32
  • C#导出数据到Excel文件的方法

    2023-11-13 17:27:05
  • Java输入输出流实例详解

    2023-05-28 15:54:35
  • 基于Spring Security前后端分离的权限控制系统问题

    2022-10-16 21:32:39
  • Java异常学习之自定义异常详解

    2023-09-25 00:57:27
  • Android优雅地处理按钮重复点击的几种方法

    2021-11-23 05:09:14
  • Java 反转带头结点的单链表并显示输出的实现过程

    2022-06-08 18:34:39
  • Android编程ProgressBar自定义样式之动画模式实现方法

    2022-02-10 12:54:29
  • Java如何实现http接口参数和返回值加密

    2023-08-23 12:28:37
  • 浅析Java的Hibernate框架中的继承关系设计

    2021-10-18 03:10:03
  • 关于Java中增强for循环使用的注意事项

    2021-08-09 16:47:43
  • asp之家 软件编程 m.aspxhome.com