当前位置:网站首页>Redis秒杀demo
Redis秒杀demo
2022-07-01 15:24:00 【y_w_x_k】
方案一:方法上加上synchronized即可,线程安全方案,但是执行效率较低,不适合大型并发场合
方案二:采用乐观锁
controller:
@RestController
public class SecondKill {
@Autowired
SecondKillSev secondKillSev;
@RequestMapping(value = "/buy",method = RequestMethod.POST)
public Mes buy(@RequestParam("proid") String proid){
String usedId=new Random().nextInt(1000)+"";
Mes mes = secondKillSev.doSecondKill(usedId, proid);
return mes;
}
}
service:
@Service
public class SecondKillSev {
Mes message;
public SecondKillSev(){
}
@Autowired
StringRedisTemplate stringRedisTemplate;
/**
* 执行秒杀
* 方案一:synchronized,线程安全,但是执行效率较低,不适合大型并发场合
* 方案二:用乐观锁监听事务
*/
public Mes doSecondKill(String uid, String proid){
//1.判断uid和proid非空
if(uid==null || proid==null){
return Mes.getMes("400","uid和proid不能为空");
}
//2.连接redis
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
//3.拼接key,库存key,成功用户key
String kcKey="kcKey:"+proid;
String userKey="userKey:"+proid;
//乐观锁解决超卖问题
//stringRedisTemplate默认不开启事物
stringRedisTemplate.setEnableTransactionSupport(true);
List<Object> txResults = stringRedisTemplate.execute(new SessionCallback<List<Object>>() {
public List<Object> execute(RedisOperations operations) throws DataAccessException {
//监视库存
operations.watch(kcKey);
//4.获取库存,如果库存是null,则秒杀还没开始
String kc=ops.get(kcKey);
if(kc==null){
message= Mes.getMes("400","库存是null,秒杀还没开始");
return null;
}
//5.判断用户是否重复秒杀
if(operations.opsForSet().isMember(userKey, uid)){
message=Mes.getMes("400","不能重复秒杀");
return null;
}
//6.判断商品数量,如果数量小于1,则秒杀结束
if(Integer.parseInt(kc)<=0){
message=Mes.getMes("400","秒杀已经结束");
return null;
}
//7.秒杀过程,库存-1,成功用户添加到清单
//使用事务
operations.multi();
operations.opsForValue().decrement(kcKey);
operations.opsForSet().add(userKey,uid);
List exec = operations.exec();
if(exec==null || exec.size()==0){
message=Mes.getMes("400","秒杀失败");
return null;
}else{
return exec;
}
}
});
if(txResults==null ||txResults.size()==0){
return message;
}
return Mes.getMes("200","秒杀成功");
}
}
但是这种方案也有一个缺点,就是如果商品的数量是500,但是线程数是1000,即访问量不是非常多的情况下,会出现库存没有卖完的问题,即库存遗留问题,所以可以用lua脚本来解决;
方案三:lua脚本
local userid=KEYS[1];
local prodid=KEYS[2];
local qtkey="sk:"..prodid..":qt";
local userskey="sk:"..prodid..":user";
local userExists=redis.call("sismember",userskey,userid);
if tonumber(userExists)==1 then
return 2;
end
local num=redis.call("get",qtkey);
if tonumber(num)<=0 then
return 0;
else
redis.call("decr",qtkey);
redis.call("sadd",userskey,userid);
end
return 1
代码实现:
@Service
public class SeckillByScript {
static String secKillScript1 = "local userid=KEYS[1];\n" +
"local prodid=KEYS[2];\n" +
"local qtkey=\"sk:\"..prodid..\":qt\";\n" +
"local userskey=\"sk:\"..prodid..\":user\";\n" +
"local userExists=redis.call(\"sismember\",userskey,userid);" +
"if tonumber(userExists)==1 then\n" +
" return 2;\n" +
"end\n" +
"local num=redis.call(\"get\",qtkey);\n" +
"if tonumber(num)<=0 then\n" +
" return 0;\n" +
"else\n" +
" redis.call(\"decr\",qtkey);\n" +
" redis.call(\"sadd\",userskey,userid);\n" +
" end\n" +
" return 1";
public boolean doSecKill(String userid, String prodid) {
JedisPool jedisPool = JedisPoolUtil.getJedisPool();
Jedis jedis = jedisPool.getResource();
String sha1 = jedis.scriptLoad(secKillScript1);
Object result = jedis.evalsha(sha1, 2, userid, prodid);
String reString = String.valueOf(result);
if ("0".equals(reString)) {
System.out.println("已抢空!");
return false;
} else if ("1".equals(reString)) {
System.out.println("抢购成功");
return true;
} else if ("2".equals(reString)) {
System.out.println("该用户已抢过!");
return false;
} else {
System.out.println("抢购异常");
return false;
}
}
}
边栏推荐
- opencv学习笔记五--文件扫描+OCR文字识别
- Skywalking 6.4 distributed link tracking usage notes
- 【STM32-USB-MSC问题求助】STM32F411CEU6 (WeAct)+w25q64+USB-MSC Flash用SPI2 读出容量只有520KB
- 智能运维实战:银行业务流程及单笔交易追踪
- leetcode:329. Longest increasing path in matrix
- Tableapi & SQL and Kafka message acquisition of Flink example
- Qt+pcl Chapter 6 point cloud registration ICP Series 5
- Using swiper to make mobile phone rotation map
- 张驰课堂:六西格玛数据的几种类型与区别
- An intrusion detection model
猜你喜欢
微信小程序01-底部导航栏设置
The markdown editor uses basic syntax
Zhang Chi's class: several types and differences of Six Sigma data
STM32F4-TFT-SPI时序逻辑分析仪调试记录
【目标跟踪】|模板更新 时间上下文信息(UpdateNet)《Learning the Model Update for Siamese Trackers》
《性能之巅第2版》阅读笔记(五)--file-system监测
【300+精选大厂面试题持续分享】大数据运维尖刀面试题专栏(三)
phpcms后台上传图片按钮无法点击
Survey of intrusion detection systems:techniques, datasets and challenges
skywalking 6.4 分布式链路跟踪 使用笔记
随机推荐
Wechat applet 01 bottom navigation bar settings
Tableapi & SQL and MySQL insert data of Flink
采集数据工具推荐,以及采集数据列表详细图解流程
openssl客户端编程:一个不起眼的函数导致的SSL会话失败问题
【STM32学习】 基于STM32 USB存储设备的w25qxx自动判断容量检测
phpcms后台上传图片按钮无法点击
微信公众号订阅消息 wx-open-subscribe 的实现及闭坑指南
k8s部署redis哨兵的实现
《QT+PCL第九章》点云重建系列2
cmake 基本使用过程
Opencv Learning Notes 6 -- image mosaic
使用swiper制作手机端轮播图
榨汁机UL982测试项目有哪些
《QT+PCL第六章》点云配准icp系列4
MySQL 服务正在启动 MySQL 服务无法启动解决途径
Flink 系例 之 TableAPI & SQL 与 MYSQL 插入数据
Guide de conception matérielle du microcontrôleur s32k1xx
What are the test items of juicer ul982
Filter &(登录拦截)
智能运维实战:银行业务流程及单笔交易追踪