当前位置: 首页 > news >正文

设计师常去的素材网站wordpress页面加顶部

设计师常去的素材网站,wordpress页面加顶部,自己做网站系统首选平台,制作自己的平台网站全局唯一ID 唯一ID的必要性 每个店铺都可以发布优惠券#xff1a; 当用户抢购时#xff0c;就会生成订单并保存到tb_voucher_order这张表中#xff0c;而订单表如果使用数据库自增ID就存在一些问题#xff1a; id的规律性太明显#xff0c;容易被用户根据id的间隔来猜测…全局唯一ID 唯一ID的必要性 每个店铺都可以发布优惠券 当用户抢购时就会生成订单并保存到tb_voucher_order这张表中而订单表如果使用数据库自增ID就存在一些问题 id的规律性太明显容易被用户根据id的间隔来猜测到销量等商业信息不够保密 受单表数据量的限制mysql的id自增长有数值约束且数据量大的情况下会进行分库分表表不同自增长id可能相同在分布式系统中是不允许的 全局ID生成器是一种在分布式系统下用来生成全局唯一ID的工具一般要满足下列特性 Redis恰好满足以上特性为了增加ID的安全性我们可以不直接使用Redis自增的数值而是拼接一些其它信息 ID的组成部分符号位1bit永远为0 时间戳31bit以秒为单位可以使用69年 序列号32bit秒内的计数器支持每秒产生2^32个不同ID这个序列号足够大几乎不可能到达极限 redis实现全局唯一ID 获取当前时间戳的秒数 LocalDateTime time LocalDateTime.of(2023, 9, 2, 0, 0, 0);long of time.toEpochSecond(ZoneOffset.UTC); 生成序列号自增长的key为了防止一直使用该key最后导致达到redis的上限故需要拼接上日期既防止达到上限又能方便统计同一天的下单量 //开始时间戳秒数private static final long BEGIN_TIMESTAMP 1693612800L;//序列号位数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;//利用redis的自增生成序列号String date now.format(DateTimeFormatter.ofPattern(yyyy:MM:dd));Long increment stringRedisTemplate.opsForValue().increment(icr: keyPrefix : date);//拼接return timestamp COUNT_BITS | increment;} 添加优惠券 每个店铺都可以发布优惠券分为平价券和特价券。平价券可以任意购买而特价券需要秒杀抢购 平价卷由于优惠力度并不是很大所以是可以任意领取 而代金券由于优惠力度大所以像第二种卷就得限制数量特价卷除了具有优惠卷的基本信息以外还具有库存抢购时间结束时间等等字段 添加特价券 {shopId:1, title:100元代金券, subTitle:周一至周五均可使用, rules:全场通用\\n无需预约\\n可无限叠加\\不兑现、不找零\\n仅限堂食, payValue:8000, actualValue:10000, type:1, stock:100, beginTime:2023-09-02T10:09:17, endTime:2023-09-26T12:09:04 } 由于没有后台管理系统故使用postman进行post请求添加需要关闭拦截器同时设置有效的开始时间和结束时间优惠券才会显示 实现秒杀下单 下单核心思路当我们点击抢购时会触发右侧的请求我们只需要编写对应的controller即可service层编写对应的代码操作数据库即可 下单时需要判断两点 秒杀是否开始或结束如果尚未开始或已经结束则无法下单 库存是否充足不足则无法下单 下单核心逻辑分析 当用户开始进行下单我们应当去查询优惠卷信息查询到优惠卷信息判断是否满足秒杀条件 比如时间是否充足如果时间充足则进一步判断库存是否足够如果两者都满足则扣减库存创建订单然后返回订单id如果有一个条件不满足则直接结束 代码实现 由于涉及到优惠券表和优惠券订单表两张表的dml操作需要加上Transactional声明事务 TransactionalOverridepublic Result seckillVoucher(Long voucherId) {SeckillVoucher seckillVoucher seckillVoucherService.getById(voucherId);//判断是否开始开始时间如果在当前时间之后就是尚未开始if (seckillVoucher.getBeginTime().isAfter(LocalDateTime.now())) {return Result.fail(秒杀尚未开始);}//判断是否结束结束时间如果在当前时间之前就是已经结束if (seckillVoucher.getEndTime().isBefore(LocalDateTime.now())) {return Result.fail(秒杀已经结束);}//判断库存是否充足if (seckillVoucher.getStock() 1) {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();//订单idlong orderId redisIdWorker.nextId(order);//用户idLong userId UserHolder.getUser().getId();voucherOrder.setUserId(userId);voucherOrder.setVoucherId(voucherId);voucherOrder.setId(orderId);save(voucherOrder);return Result.ok(orderId);} 超卖问题 模拟实现 使用jmeter模拟实现注意带上请求头authorization值为登录时的token的key 从数据库的库存中我们可以看到已经出现了超卖现象库存出现了负数 超卖原因 我们原有的代码是这么写的 if (voucher.getStock() 1) {// 库存不足return Result.fail(库存不足);}//5扣减库存boolean success seckillVoucherService.update().setSql(stock stock -1).eq(voucher_id, voucherId).update();if (!success) {//扣减库存return Result.fail(库存不足);} 假设线程1过来查询库存判断出来库存大于1正准备去扣减库存但是还没有来得及去扣减此时线程2过来线程2也去查询库存发现这个数量一定也大于1那么这两个线程都会去扣减库存最终多个线程相当于一起去扣减库存此时就会出现库存的超卖问题。 解决方案 超卖问题是典型的多线程安全问题针对这一问题的常见解决方案就是加锁而对于加锁我们通常有两种解决方案见下图 悲观锁 悲观锁可以实现对于数据的串行化执行比如syn和lock都是悲观锁的代表同时悲观锁中又可以再细分为公平锁非公平锁可重入锁等等 乐观锁 乐观锁会有一个版本号每次操作数据会对版本号1再提交回数据时会去校验是否比之前的版本大1 如果大1 则进行操作成功这套机制的核心逻辑在于如果在操作过程中版本号只比原来大1 那么就意味着操作过程中没有人对他进行过修改他的操作就是安全的如果不大1则数据被修改过当然乐观锁还有一些变种的处理方式比如cas即查值进行比对发现值没有被修改认为线程安全进行修改值解决线程安全问题 解决方案实现 采用乐观锁方案对于优惠券库存我们并需要设置版本号因为查询到的库存和最后修改数据时再查第二遍库存后我们只需要将这两次库存量进行比较就能知道库存是否被修改过即线程是否安全且为了性能我们会将修改数据时设置的条件并不需要两次库存完全相同只需要在进行修改时加上库存大于0的条件即可上面代码只需要修改此处即可 boolean success seckillVoucherService.update().setSql(stock stock -1).eq(voucher_id, voucherId).gt(stock,0).update(); 开了两百个线程之后异常率达到完美的50%同时数据库数据正常 一人一单 优惠卷是为了引流但是目前的情况是一个人可以无限制的抢这个优惠卷所以我们应当增加一层逻辑让一个用户只能下一个单而不是让一个用户下多个单 具体操作逻辑如下比如时间是否充足如果时间充足则进一步判断库存是否足够然后再根据优惠卷id和用户id查询是否已经下过这个订单如果下过这个订单则不再下单否则进行下单 初步实现在扣减库存前查询订单表该用户是否已经下过单 TransactionalOverridepublic Result seckillVoucher(Long voucherId) {SeckillVoucher seckillVoucher seckillVoucherService.getById(voucherId);//判断是否开始开始时间如果在当前时间之后就是尚未开始if (seckillVoucher.getBeginTime().isAfter(LocalDateTime.now())) {return Result.fail(秒杀尚未开始);}//判断是否结束结束时间如果在当前时间之前就是已经结束if (seckillVoucher.getEndTime().isBefore(LocalDateTime.now())) {return Result.fail(秒杀已经结束);}//判断库存是否充足if (seckillVoucher.getStock() 1) {return Result.fail(库存不足);}Long userId UserHolder.getUser().getId();// 5.1.查询订单int count query().eq(user_id, userId).eq(voucher_id, voucherId).count();// 5.2.判断是否存在if (count 0) {// 用户已经购买过了return Result.fail(用户已经购买过一次);}//扣减库存boolean success seckillVoucherService.update().setSql(stock stock -1).eq(voucher_id, voucherId).gt(stock,0).update();if (!success) {return Result.fail(库存不足);}VoucherOrder voucherOrder new VoucherOrder();//订单idlong orderId redisIdWorker.nextId(order);voucherOrder.setUserId(userId);voucherOrder.setVoucherId(voucherId);voucherOrder.setId(orderId);save(voucherOrder);return Result.ok(orderId);} 还是出现了一人多张优惠券订单的情况 存在问题现在的问题还是和之前一样并发过来查询数据库都不存在订单所以我们还是需要加锁但是乐观锁比较适合更新数据而现在是插入数据所以我们需要使用悲观锁操作可以直接在方法上直接加上synchronized 锁来解决 这样添加锁锁的粒度太粗了在使用锁过程中控制锁粒度 是一个非常重要的事情因为如果锁的粒度太大会导致每个线程进来都会锁住所以我们需要去控制锁的粒度可以将用户下单的代码封装成一个方法对该业务进行上锁将锁的范围缩小同时由于spring的事务必须等到锁释放之后才会提交如果锁释放之后有别的线程进入下单业务而此时spring事务尚未提交这就会造成订单尚未写入数据库该线程仍会查到无订单继续进行下单操作无法解决线程安全问题所以我们要先提交事务才能释放锁就能避免该问题。 最终实现 由于createVoucherOrder()要受事务控制要注入IVoucherOrderService拿到代理对象通过该代理对象调用该方法事务才能生效为了使事务提交在释放锁之前可以将锁直接锁死事务方法。 Autowiredprivate ISeckillVoucherService seckillVoucherService;Autowiredprivate RedisIdWorker redisIdWorker;Autowiredprivate IVoucherOrderService voucherOrderService;Overridepublic Result seckillVoucher(Long voucherId) {SeckillVoucher seckillVoucher seckillVoucherService.getById(voucherId);//判断是否开始开始时间如果在当前时间之后就是尚未开始if (seckillVoucher.getBeginTime().isAfter(LocalDateTime.now())) {return Result.fail(秒杀尚未开始);}//判断是否结束结束时间如果在当前时间之前就是已经结束if (seckillVoucher.getEndTime().isBefore(LocalDateTime.now())) {return Result.fail(秒杀已经结束);}//判断库存是否充足if (seckillVoucher.getStock() 1) {return Result.fail(库存不足);}Long userId UserHolder.getUser().getId();synchronized (userId.toString().intern()) {return voucherOrderService.createVoucherOrder(voucherId);}}Transactionalpublic Result createVoucherOrder(Long voucherId) {Long userId UserHolder.getUser().getId();// 5.1.查询订单int count query().eq(user_id, userId).eq(voucher_id, voucherId).count();// 5.2.判断是否存在if (count 0) {// 用户已经购买过了return Result.fail(用户已经购买过一次);}//扣减库存boolean success seckillVoucherService.update().setSql(stock stock -1).eq(voucher_id, voucherId).gt(stock, 0).update();if (!success) {return Result.fail(库存不足);}VoucherOrder voucherOrder new VoucherOrder();//订单idlong orderId redisIdWorker.nextId(order);voucherOrder.setUserId(userId);voucherOrder.setVoucherId(voucherId);voucherOrder.setId(orderId);save(voucherOrder);return Result.ok(orderId);}
http://www.w-s-a.com/news/524925/

相关文章:

  • 创造网站电商网站建设方案道客巴巴
  • 南通设计网站建设wordpress时光轴
  • 郑州做网站企起网站建设 风险
  • 北京市保障性住房建设投资中心网站6大连广告设计与制作公司
  • 建站之星网站模板国内f型网页布局的网站
  • 怎么做网站关键词优化外贸网站 开源
  • 广东公司响应式网站建设设计seo系统是什么
  • 清丰网站建设费用网站建设的前途
  • 网站上那些兼职网页怎么做的北京网页
  • 桂林建站平台哪家好品牌设计公司宣传文案
  • 平面设计和建设网站的区别公司官网静态
  • h5网站建设+案例住房住房和城乡建设部网站
  • 建设股公司网站东莞建设网网上平台
  • 湖州吴兴建设局网站加强网站建设的
  • 茌平做网站公司专业商城网站建设报价
  • 网站结构图怎么画wordpress注册不发送件
  • 个人备案网站可以做论坛吗电商推广方式有哪些
  • 网站建设 自适应国内最近的新闻
  • 校园网站开发背景吴江网站建设公司
  • 网站开发工程师发展趋势山东省建设工程电子信息网站
  • 适合大学生创业的网站建设类型吉林省舒兰市建设银行网站
  • 呼和浩特网站建设哪家好培训学校加盟费用
  • 网站如何做友情链接有道云笔记WordPress
  • 贵阳企业网站建设制作赤峰浩诚网站建设公司
  • asp官方网站微信模板素材
  • wordpress 留言给站长发邮件做百度推广员赚钱吗
  • 北京建站公司做网站价格专门找人做软件的网站
  • 商务网站的特点ui软件界面设计
  • 广州个性化网站开发网站索引量是什么意思
  • 公司网站制作专业公司python做后台网站的多吗