建设一个新的网站需要准备什么,南昌有做网站的吗,中企动力科技股份有限公司销售,百度推广是给做网站吗2023.12.11 上一张的秒杀券下单还可以进行优化#xff0c;先来回顾一下下单流程#xff1a; 可以看出流程设计多次查询和操作数据库的操作#xff0c;并且执行顺序是一个线程串行执行#xff0c;执行性能是比较低的。 优化方案#xff1a;我们将判断秒杀库存和校验一人一单…2023.12.11 上一张的秒杀券下单还可以进行优化先来回顾一下下单流程 可以看出流程设计多次查询和操作数据库的操作并且执行顺序是一个线程串行执行执行性能是比较低的。 优化方案我们将判断秒杀库存和校验一人一单的操作放入Redis中只要满足这两条操作那我们是一定可以下单成功的不用等数据真的写进数据库当判断满足下单条件之后将优惠券id、用户id、订单id保存到阻塞队列中然后直接告诉用户下单成功就好了。至于数据库里的修改操作可以使用另外一个线程异步读取阻塞队列的信息完成减库存、创建订单操作。流程图如下所示 改进秒杀业务提高并发性能
①新增秒杀优惠券的同时将优惠券信息保存到Redis中
修改增加优惠券的代码 OverrideTransactionalpublic void addSeckillVoucher(Voucher voucher) {// 保存优惠券save(voucher);// 保存秒杀信息SeckillVoucher seckillVoucher new SeckillVoucher();seckillVoucher.setVoucherId(voucher.getId());seckillVoucher.setStock(voucher.getStock());seckillVoucher.setBeginTime(voucher.getBeginTime());seckillVoucher.setEndTime(voucher.getEndTime());seckillVoucherService.save(seckillVoucher);//保存秒杀券的库存到redis中stringRedisTemplate.opsForValue().set(SECKILL_STOCK_KEY voucher.getId(),voucher.getStock().toString());}
②基于Lua脚本判断秒杀库存、一人一单决定用户是否抢购成功
流程图如下 lua代码为
-- 1.参数列表
-- 1.1.优惠券id
local voucherId ARGV[1]
-- 1.2.用户id
local userId ARGV[2]
-- 1.3.订单id
--local orderId ARGV[3]-- 2.数据key
-- 2.1.库存key
local stockKey seckill:stock: .. voucherId
-- 2.2.订单key
local orderKey seckill:order: .. voucherId-- 3.脚本业务
-- 3.1.判断库存是否充足 get stockKey
if(tonumber(redis.call(get, stockKey)) 0) then-- 3.2.库存不足返回1return 1
end
-- 3.2.判断用户是否下单 SISMEMBER orderKey userId
if(redis.call(sismember, orderKey, userId) 1) then-- 3.3.存在说明是重复下单返回2return 2
end
-- 3.4.扣库存 incrby stockKey -1
redis.call(incrby, stockKey, -1)
-- 3.5.下单保存用户sadd orderKey userId
redis.call(sadd, orderKey, userId)
-- 3.6.发送消息到队列中 XADD stream.orders * k1 v1 k2 v2 ...
--redis.call(xadd, stream.orders, *, userId, userId, voucherId, voucherId, id, orderId)
return 0
③如果抢购成功将优惠券id和用户id封装后存入阻塞队列开启线程任务不断从阻塞队列中获取信息实现异步下单功能。
完整代码修改如下
package com.hmdp.service.impl;import com.hmdp.dto.Result;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.ISeckillVoucherService;
import com.hmdp.service.IVoucherOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.RedisIdWorker;
import com.hmdp.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** p* 服务实现类* /p*/
Slf4j
Service
public class VoucherOrderServiceImpl extends ServiceImplVoucherOrderMapper, VoucherOrder implements IVoucherOrderService {Resourceprivate RedisIdWorker redisIdWorker;Resourceprivate ISeckillVoucherService seckillVoucherService;Resourceprivate StringRedisTemplate stringRedisTemplate;Resourceprivate RedissonClient redissonClient;private static final DefaultRedisScriptLong SECKILL_SCRIPT;static {SECKILL_SCRIPT new DefaultRedisScript();SECKILL_SCRIPT.setLocation(new ClassPathResource(seckill.lua));SECKILL_SCRIPT.setResultType(Long.class);}private BlockingQueueVoucherOrder orderTasks new ArrayBlockingQueue(1024*1024);private static final ExecutorService SECKILL_ORDER_EXECUTOR Executors.newSingleThreadExecutor();//线程池PostConstructprivate void init(){SECKILL_ORDER_EXECUTOR.submit(new VoucherOrderHandler());}private class VoucherOrderHandler implements Runnable{Overridepublic void run() {while (true){try {//1.获取队列中的订单信息VoucherOrder voucherOrder orderTasks.take();//2.创建订单handleVoucherOrder(voucherOrder);} catch (Exception e) {log.error(处理订单异常,e);}}}}private void handleVoucherOrder(VoucherOrder voucherOrder) {//1.获取用户Long userId voucherOrder.getUserId();//2.创建锁对象RLock lock redissonClient.getLock(lock:order: userId);
// SimpleRedisLock lock new SimpleRedisLock(order: userId, stringRedisTemplate);//3.获取锁boolean isLock lock.tryLock();//4.判断是否获取锁成功if(!isLock){//获取锁失败不能让黄牛不断重复所以直接返回失败log.error(不允许重复下单);return ;}//获取锁成功try {proxy.createVoucherOrder(voucherOrder);} finally {//释放锁lock.unlock();}}// Override
// public Result seckillVoucher(Long voucherId) {
// //1.查询优惠券
// SeckillVoucher voucher seckillVoucherService.getById(voucherId);
// //2.判断秒杀活动是否开始
// if(voucher.getBeginTime().isAfter(LocalDateTime.now())){
// //尚未开始
// return Result.fail(秒杀尚未开始);
// }
// //3.判断秒杀活动是否已经结束
// if(voucher.getEndTime().isBefore(LocalDateTime.now())){
// //尚未开始
// return Result.fail(秒杀已经结束);
// }
// //4.判断库存是否充足
// if(voucher.getStock() 1){
// return Result.fail(库存不足);
// }
//
// //此处需要将整个函数锁起来
// Long userId UserHolder.getUser().getId();
//
// //创建锁对象
// RLock lock redissonClient.getLock(lock:order: userId);SimpleRedisLock lock new SimpleRedisLock(order: userId, stringRedisTemplate);
// //获取锁
// boolean isLock lock.tryLock();
// //判断是否获取锁成功
// if(!isLock){
// //获取锁失败不能让黄牛不断重复所以直接返回失败
// return Result.fail(不允许重复下单!);
// }
// //获取锁成功
// try {
// //获取代理对象
// IVoucherOrderService proxy (IVoucherOrderService) AopContext.currentProxy();
// return proxy.createVoucherOrder(voucherId);
// } finally {
// //释放锁
// lock.unlock();
// }
// }private IVoucherOrderService proxy;Overridepublic Result seckillVoucher(Long voucherId) {//获取用户Long userId UserHolder.getUser().getId();//1.执行lua脚本Long result stringRedisTemplate.execute(SECKILL_SCRIPT,Collections.emptyList(),voucherId.toString(),userId.toString());//2.判断结果是否为0int r result.intValue();if(r ! 0){//2.1 不为0代表没有购买资格return Result.fail(r1? 库存不足 : 不能重复下单);}//2.2 为0有购买资格将下单信息保存到阻塞队列VoucherOrder voucherOrder new VoucherOrder();//2.3订单idlong orderId redisIdWorker.nextId(order);voucherOrder.setId(orderId);//2.4用户idvoucherOrder.setUserId(userId);//2.5代金券idvoucherOrder.setVoucherId(voucherId);//2.6 放入阻塞队列orderTasks.add(voucherOrder);//3.获取代理对象proxy (IVoucherOrderService) AopContext.currentProxy();//4.返回订单idreturn Result.ok(orderId);}Transactionalpublic void createVoucherOrder(VoucherOrder voucherOrder) {//一人只能下一单Long userId voucherOrder.getUserId();int count query().eq(user_id, userId).eq(voucher_id, voucherOrder.getVoucherId()).count();//查询订单数目if (count 0) {//用户已经购买过该秒杀券log.error(用户已经购买过一次);return;}//5.满足条件扣减库存boolean success seckillVoucherService.update().setSql(stock stock - 1).eq(voucher_id, voucherOrder.getVoucherId()).gt(stock, 0)//乐观锁查询一下stock有无变化变化了就不能做修改操作.update();if (!success) {//扣减失败log.error(库存不足);return;}//6.创建订单save(voucherOrder);}
}综上秒杀优化的总体思路就是先利用Redis完成库存余量、一人一单判断完成抢单业务再将下单业务放入阻塞队列利用独立线程异步下单。