一、全局ID生成器

对于优惠券业务,亦即订单业务,其中的优惠券订单id存储到数据库中时将不采用mysql自增,因为这是一种不安全且当有庞大的订单时又不能保证分布式全局唯一性的做法。因此,就需要有一这样一个生成全局id的角色。

在分布式系统下,为了保证id的唯一性、高可用、高性能、递增性以及安全性,可以采用redis来实现全局id的生成。

基于redis的id生成器,将生成这样的复杂的id:

@Component
public class RedisIdWorker {private static final long BEGIN_TIMESTAMP=1640995200L;//开始时间戳private static final int COUNT_BITS=32;private StringRedisTemplate stringRedisTemplate;public RedisIdWorker(StringRedisTemplate stringRedisTemplate){this.stringRedisTemplate=stringRedisTemplate;}public long nextId(String keyPrefix){//时间戳LocalDateTime now = LocalDateTime.now();long nowSecond = now.toEpochSecond(ZoneOffset.UTC);long timeStamp = nowSecond - BEGIN_TIMESTAMP;//序列号String date = now.format(DateTimeFormatter.ofPattern("yyyyMMdd"));long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);//拼接返回return timeStamp<<COUNT_BITS|count;}}

测试

    @Testpublic void testIdWorker() throws InterruptedException {CountDownLatch latch = new CountDownLatch(300);Runnable task=()->{for(int i=0;i<100;i++){long id=redisIdWorker.nextId("order");System.out.println("id="+id);}latch.countDown();};long begin = System.currentTimeMillis();for(int i=0;i<300;i++){es.submit(task);}latch.await();long end = System.currentTimeMillis();System.out.println("time:"+(end-begin));}

二、添加秒杀券

使用postman发送添加优惠券请求,模拟管理平台。

三、秒杀券订单

    @Override@Transactionalpublic Result seckillVoucher(Long voucherId) {//查优惠券SeckillVoucher voucher = seckillVoucherService.getById(voucherId);//判断秒杀是否开始 是否结束LocalDateTime beginTime = voucher.getBeginTime();LocalDateTime endTime = voucher.getEndTime();//秒杀未开始或结束 返回是错误if(beginTime.isAfter(LocalDateTime.now())){return Result.fail("秒杀尚未开始");}if (endTime.isBefore(LocalDateTime.now())){return Result.fail("秒杀已结束");}//秒杀开始 判断库存是否充足//不足 返回错误if (voucher.getStock()==0){return Result.fail("库存不足");}//优惠券足够 扣减库存boolean success = seckillVoucherService.update().setSql("stock=stock-1").eq("voucher_id", voucherId).update();if(!success){return Result.fail("库存不足");}//创优惠券订单VoucherOrder voucherOrder = new VoucherOrder();long orderId = redisIdWorker.nextId("order");voucherOrder.setId(orderId);//订单idvoucherOrder.setUserId(UserHolder.getUser().getId());//用户idvoucherOrder.setVoucherId(voucherId);//优惠券idsave(voucherOrder);//返回订单idreturn Result.ok(orderId);}

四、线程安全——超卖

使用jmeter模拟高并发的秒杀场景,添加200线程。测试发现,优惠券数量出现负数,即超卖问题,因此考虑加锁。

测试结果出现了少量的超卖问题,故可以考虑采用加乐观锁。有两种做法:一是采用加版本号,而是CAS。

修改操作其实是先查询后修改,每次查询同时获取版本号,修改时先对比版本号,若不同则报错,相同则修改同时版本号+1。

也可以不加版本号。用修改字段数据代替版本号,即先对比数据,未修改过再修改。

但是,对于业务来说,只要库存大于0这种并发修改就是没有问题的,而乐观锁则会将并发修改的其余线程全部失效,因此,对于业务来说,要对乐观锁进行修改:在修改数据时不判断数据是否被修改过,而是查询优惠券库存其是否>0.

//优惠券足够 扣减库存boolean success = seckillVoucherService.update().setSql("stock=stock-1").eq("voucher_id", voucherId).gt("stock",0)//大于0.update();

五、线程安全——一人一单

一人一单的实现:当查询完库存充足后,查看券订单中的用户id是否已经存在。

但是同时,当秒杀刚开始时,众多线程同时涌入判断,将都会判定为第一次下单,因此,将整个查询订单—创建订单—扣减库存都加悲观锁。同时进行事务的处理。

    @Overridepublic Result seckillVoucher(Long voucherId) {//查优惠券SeckillVoucher voucher = seckillVoucherService.getById(voucherId);//判断秒杀是否开始 是否结束LocalDateTime beginTime = voucher.getBeginTime();LocalDateTime endTime = voucher.getEndTime();//秒杀未开始或结束 返回是错误if(beginTime.isAfter(LocalDateTime.now())){return Result.fail("秒杀尚未开始");}if (endTime.isBefore(LocalDateTime.now())){return Result.fail("秒杀已结束");}//秒杀开始 判断库存是否充足//不足 返回错误if (voucher.getStock()<1){return Result.fail("库存不足");}//优惠券足够 创建订单Long userId = UserHolder.getUser().getId();synchronized(userId.toString().intern()){//拿到这个类的代理对象IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();return proxy.createVoucherOrder(voucherId);}//先提交事务再释放悲观锁}//若将事务注解加在此处:// 事务是采用的mapper代理方式实现;// 调用此方法时 是采用this.方法()是非代理对象 是没有事务功能的//因此需要在调用处拿到这个方法的代理对象 才能使事务生效@Transactionalpublic Result createVoucherOrder(Long voucherId) {//优惠券足够 查看该用户是否已经下过单Long userId = UserHolder.getUser().getId();int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();if(count>0){return Result.fail("已经购买过这个券了");}// 扣减库存boolean success = seckillVoucherService.update().setSql("stock=stock-1").eq("voucher_id", voucherId).gt("stock",0)//大于0.update();if(!success){return Result.fail("库存不足");}//创优惠券订单VoucherOrder voucherOrder = new VoucherOrder();long orderId = redisIdWorker.nextId("order");voucherOrder.setId(orderId);//订单idvoucherOrder.setUserId(userId);//用户idvoucherOrder.setVoucherId(voucherId);//优惠券idsave(voucherOrder);//返回订单idreturn Result.ok(orderId);}

Redis实战—黑马点评项目—优惠券秒杀相关推荐

  1. 【Redis】Redis实战:黑马点评之优惠券秒杀

    Redis实战:黑马点评之优惠券秒杀 1 全局唯一ID 1.1全局唯一ID 每个店铺都可以发布优惠券: 当用户抢购时,就会生成订单并保存到tb_voucher_order这张表中,而订单表如果使用数据 ...

  2. Redis:黑马点评项目之用户短信登录

    一.表结构 建表sql语句: /*Navicat Premium Data TransferSource Server : localSource Server Type : MySQLSource ...

  3. Redis学习笔记②实战篇_黑马点评项目

    若文章内容或图片失效,请留言反馈.部分素材来自网络,若不小心影响到您的利益,请联系博主删除. 资料链接:https://pan.baidu.com/s/1189u6u4icQYHg_9_7ovWmA( ...

  4. 黑马点评项目全部功能实现及详细笔记--Redis练手项目

    目录 一.项目详情 1.1 项目简介 1.2 数据库表设计 1.3 前端部署 1.4 后端搭建 二.短信登录 2.1 发送验证码 2.2 验证码登录 2.3 登录校验拦截器 2.4 退出登录(补充) ...

  5. 菜鸟项目练习:黑马点评项目总结

    目录 1. 项目介绍 2.各个功能模块 2.1  登录模块 2.1.1 实现短信登录 2.1.2 编写拦截器 2.2 查询商户模块 2.2.1 主页面查询商户类型 2.2.3 按距离查询商户 2.3 ...

  6. 黑马点评项目-短信登录功能

    一.导入黑马点评项目 1.代码下载 视频资源链接:P25 实战篇-02.短信登录-导入黑马点评项目 代码可以直接去黑马微信公众号上搜索,或者从下面的网盘链接中下载:链接: https://pan.ba ...

  7. 黑马点评--优惠卷秒杀

    黑马点评–优惠卷秒杀 全局ID生成器: 是一种在分布式系统下用来生成全局唯一ID的工具,一般要满足下列特性: 为了增加ID的安全性,我们可以不直接使用Redis自增的数值,而是拼接一些其它信息: Re ...

  8. 黑马点评项目全面业务总结

    1 黑马点评项目 1.1 短信登陆 1.1.1 短信登陆简介 session共享问题:多台服务器并不共享session存储空间,当请求切换到不同tomcat服务时导致数据丢失的问题. 在进行短信登录时 ...

  9. 黑马点评项目笔记(四)社交、附近人、数据统计功能实现

    目录 达人探店 查看博文 点赞博文 点赞排行榜 好友关注 关注和取关 共同关注 关注推送(Feed流) Feed流的两种模式 Timeline 三种实现模式 基于推模式实现消息推送 滚动分页 附近商户 ...

最新文章

  1. TypeError: can only concatenate str (not “float“) to str
  2. Redis源码解析——字典遍历
  3. 用Python写一份独特的元宵节祝福
  4. 软件架构之美在于简单、好用、稳定、功能定位明确、代码简洁、通俗易懂
  5. 宁波机器人餐厅需要预约吗_拍婚纱照需要预约吗 预约婚纱照需要注意哪些问题...
  6. 经典C语言程序100例之五五
  7. Redis中的代理Sharding
  8. python 整数 1字节_Python程序打印代表整数的字节数组
  9. UIControl-IOS开发
  10. jmeter 脚本 排除_对Buildah脚本进行故障排除
  11. 【英语学习】【WOTD】palimpsest 释义/词源/示例
  12. struts2漏洞修复
  13. 矩池云上安装及使用Milvus教程
  14. 有哪些常用的虚拟主机管理系统
  15. oracle 查历史数据,Oracle 查询历史数据(转帖)
  16. Spring Cloud Alibaba#01.开篇立题
  17. AXI4协议学习:架构、信号定义、工作时序和握手机制
  18. 通向财务自由之路-笔记总结
  19. 上位机软件开发项目案例(一)_C#开发
  20. 物体检测及分类方法总结

热门文章

  1. 关于雷达(Radar)信道
  2. 山东春秀高考计算机本科录取率,山东2020年高考录取人数及录取率
  3. 挑战愈艰巨,胜利愈辉煌。 – 尼克·胡哲
  4. vim 搜索带有空格的字符串的姿势
  5. 安卓开机画面_iPad拜拜!虎贲芯片+安卓10全局手势触控,quot;国产之光quot;台电P20HD...
  6. 履历表范例 电脑程序员
  7. 数据挖掘——预测未来销售
  8. (17)ObjectARX2015 + vs2012创建块定义
  9. 快手电商“大搞产业带”背后
  10. 多个CAN节点收发异常,TVS结电容在高速电路中的影响