单机环境下一人一单

优惠券秒杀

添加优惠卷

店铺发布优惠券又分为平价券和特价券, 平价券可以任意购买而特价券需要秒杀抢购(限制数量和时间)

tb_voucher(平价券): 优惠券的基本信息

在这里插入图片描述

tb_seckill_voucher(秒杀券): 有voucher_id字段表示具有优惠卷的基本信息,此外还有库存,开始抢购时间,结束抢购时间等特殊字段

在这里插入图片描述

VoucherController提供了一个接口方法用来添加普通优惠券

/**
 * 新增普通券
 * @param voucher 优惠券信息
 * @return 优惠券id
 */
@PostMapping
public Result addVoucher(@RequestBody Voucher voucher) {
    // 直接将普通券的信息保存到普通券表中
    voucherService.save(voucher);
    return Result.ok(voucher.getId());
}

VoucherController提供了一个接口方法用来添加秒杀券,在VoucherService中的addSeckillVoucher方法实现添加秒杀券的业务逻辑

/**
 * 新增秒杀券
 * @param voucher 优惠券信息,包含秒杀券信息(库存,生效时间,失效时间)
 * @return 优惠券id
 */
@PostMapping("seckill")
public Result addSeckillVoucher(@RequestBody Voucher voucher) {
    voucherService.addSeckillVoucher(voucher);
    return Result.ok(voucher.getId());
}
// 新增秒杀券就是在数据库中新增普通卷和秒杀券的信息
@Override
@Transactional// 因为是操作两张表所以需要添加事务
public void addSeckillVoucher(Voucher voucher) {
    // 将秒杀券的基本信息保存到普通券表中,如果没有指定Id会自动生成
    save(voucher);
    // 保存秒杀信息
    SeckillVoucher seckillVoucher = new SeckillVoucher();
    // 关联普通券id
    seckillVoucher.setVoucherId(voucher.getId());
    // 设置库存
    seckillVoucher.setStock(voucher.getStock());
    // 设置开始时间
    seckillVoucher.setBeginTime(voucher.getBeginTime());
    // 设置结束时间
    seckillVoucher.setEndTime(voucher.getEndTime());
    // 保存信息到秒杀券表中
    seckillVoucherService.save(seckillVoucher);
}

添加秒杀券: 由于没有后台管理页面,使用Postman模拟发送POST请求http://localhost:8081/voucher/seckill来新增秒杀券(截止日期要超过当前日期否则不显示)

{
    "shopId":1,
    "title":"100元代金券",
    "subTitle":"周一至周五可用",
    "rules":"全场通用\\n无需预约\\n可无限叠加",
    // 数据库中金额的单位是分
    "payValue":8000,
    "actualValue":10000,
    "type":1,
    "stock":100,
    "beginTime":"2022-01-01T00:00:00",
    "endTime":"2023-10-31T23:59:59"
}

实现秒杀下单

当我们点击抢购时会触发右侧的请求,我们只需要在VoucherOrderController编写对应的Controller处理请求即可

在这里插入图片描述

当用户开始进行下单我们应当提交优惠券Id去查询优惠卷信息,然后判断是否满足秒杀条件即秒杀是否开始和库存是否充足

在这里插入图片描述

@RestController
@RequestMapping("/voucher-order")
public class VoucherOrderController {
    @Autowired
    private IVoucherOrderService voucherOrderService;
    @PostMapping("/seckill/{id}")
    public Result seckillVoucher(@PathVariable("id") Long voucherId) {
        return voucherOrderService.seckillVoucher(voucherId);
    }
}

public interface IVoucherOrderService extends IService<VoucherOrder> {
    Result seckillVoucher(Long voucherId);
}

@Slf4j
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
    @Resource
    private ISeckillVoucherService seckillVoucherService;
    @Autowired
    private RedisIdWorker redisIdWorker;
    @Override
    @Transactional// 操作两张表应该加上事务
    public Result seckillVoucher(Long voucherId) {
        //1.查询优惠券的基本信息
        //SeckillVoucher seckillVouche = seckillVoucherService.getById(voucherId)
        LambdaQueryWrapper<SeckillVoucher> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SeckillVoucher::getVoucherId, voucherId);
        SeckillVoucher seckillVoucher = seckillVoucherService.getOne(queryWrapper);
        //2.判断秒杀时间是否开始
        if (LocalDateTime.now().isBefore(seckillVoucher.getBeginTime())) {
            return Result.fail("秒杀还未开始,请耐心等待");
        }
        //3.判断秒杀时间是否结束
        if (LocalDateTime.now().isAfter(seckillVoucher.getEndTime())) {
            return Result.fail("秒杀已经结束!");
        }
        //4.判断库存是否充足
        if (seckillVoucher.getStock() < 1) {
            return Result.fail("优惠券已被抢光了哦,下次记得手速快点");
        }
        //5.扣减库存
        boolean success = seckillVoucherService.update()
            .setSql("stock = stock - 1")
            .eq("voucher_id",voucherId)
            .update();
        if (!success) {
            return Result.fail("库存不足");
        }
        //6. 创建订单	
        VoucherOrder voucherOrder = new VoucherOrder();
        //6.1 设置生成的订单id
        long orderId = redisIdWorker.nextId("order");
        voucherOrder.setId(orderId);
        //6.2 设置用户id
        Long id = UserHolder.getUser().getId();
        voucherOrder.setUserId(id);
        //6.3 设置代金券id
        voucherOrder.setVoucherId(voucherId);
        //7. 将订单数据保存到订单表中
        save(voucherOrder);
        //8. 返回订单id
        return Result.ok(orderId);
    }

库存超卖问题

当遇到高并发场景时会出现超卖现象,我们可以用Jmeter开200个线程来模拟抢100张优惠券的场景,结果优惠券库存为负数表示出现了超卖现象

  • 添加请求的信息头管理器携带我们登录的token(可能会过期),然后发起POST请求http://localhost:8081/voucher-order/seckill/voucher_id

在这里插入图片描述

在这里插入图片描述

乐观锁的两种实现方式

超卖问题是典型的多线程安全问题,针对这一问题的常见解决方案就是加锁(悲观锁或乐观锁)

  • 悲观锁: 悲观锁比较适合插入数据,简单粗暴但是性能一般
  • 乐观锁: 比较适合更新数据, 性能好但是成功率低(多个线程同时执行时只有一个可以执行成功),还需要访问数据库造成数据库压力过大

在这里插入图片描述

版本号法: 给数据库表增加一个版本号version字段,每次操作表中的数据时会查询版本号,修改数据时再次验证版本号有没有变化,没有变化才可以更新数据

在这里插入图片描述

CAS(Compare-And-Switch): 首先查询要修改字段的值,在修改数据时再次验证字段值有没有发生变化(或满足某种条件),没有变化(或满足条件)才会更新字段的值

在这里插入图片描述

乐观锁解决超卖问题

使用stock来充当版本号,VoucherOrderServiceImpl在扣减库存时比较查询到的优惠券库存和实际数据库中优惠券库存是否相同

  • 假设100个线程同时都拿到了100的库存, 但是100个人中只有1个人能扣减成功(其他的人在扣减时库存发现库存已经被修改过了,所以不再执行扣减操作)
boolean success = seckillVoucherService.update()
            .setSql("stock= stock -1") //set stock = stock -1
            .eq("voucher_id", voucherId).eq("stock",voucher.getStock()).update(); //where id = ? and stock = ?

使用stock>0 充当判断条件, 在扣减库存时只要判断是否有剩余优惠券,即只要数据库中的库存大于0都能顺利完成扣减库存操作

boolean success = seckillVoucherService.update()
            .setSql("stock= stock -1")
            .eq("voucher_id", voucherId).gt("stock",0).update(); where voucher_id = ? and stock > 0

单机环境下一人一单(悲观锁)

需求:修改秒杀业务要求同一个优惠券一个用户只能下一单

  • 如果时间和库存都充足,还需要根据优惠卷id和用户id查询是否已经下过这个订单,如果下过这个订单则不能再下单

在这里插入图片描述

在VoucherOrderServiceImpl中库存和时间都充足时即将扣减库存之前再增加一人一单逻辑

一个用户开了多个线程抢优惠券,在判断库存充足之后和执行一人一单逻辑之前间如果进来了多个线程,此时它们都在数据库中查询不到订单然后都会执行扣减操作

// 4. 判断库存是否充足
if (seckillVoucher.getStock() < 1) {
    return Result.fail("优惠券已被抢光了哦,下次记得手速快点");
}

// 5.根据用户id查询用户对应的订单是否存在
Long userId = UserHolder.getUser().getId();
int count = query().eq("voucher_id", voucherId).eq("user_id", userId).count();
if (count > 0) {
    // 用户已经下过单
    return Result.fail("您已经抢过优惠券了哦");
}
// 6.使用stock>0充当判断条件,在扣减库存时只要判断是否有剩余优惠券,即只要数据库中的库存大于0都能顺利完成扣减库存操作
boolean success = seckillVoucherService.update()
    .setSql("stock= stock -1")
    .eq("voucher_id", voucherId).gt("stock",0).update(); //where voucher_id = ? and stock > 0
if (!success) {
    return Result.fail("库存不足");
}

把一人一单逻辑之后生成订单记录的代码都提取到一个createVoucherOrder方法中(ctrl + alt + m)然后加悲观锁synchronized(悲观锁适合插入数据)

  • 不管哪一个线程运行到这个方法时都要检查有没有其它线程正在用这个方法(或者该类的其他同步方法),有的话要等正在使用synchronized方法的线程结束
  • 把锁加在createVoucherOrder方法上锁的范围太大(粒度太粗)会导致每个线程进来都会锁住,锁的对象是this所有用户都公用一把锁串行执行会导致效率很低
@Transactional
public synchronized Result createVoucherOrder(Long voucherId) {
    // 5.根据用户id查询用户对应的订单是否存在
    Long userId = UserHolder.getUser().getId();
    int count = query().eq("voucher_id", voucherId).eq("user_id", userId).count();
    // 判断用户是否下过单
    if (count > 0) {
        return Result.fail("您已经抢过优惠券了哦");
    }
    // 6.使用stock>0充当判断条件,在扣减库存时只要判断是否有剩余优惠券,即只要数据库中的库存大于0都能顺利完成扣减库存操作
    boolean success = seckillVoucherService.update()
        .setSql("stock= stock -1")
        .eq("voucher_id", voucherId).gt("stock",0).update(); //where voucher_id = ? and stock > 0
    if (!success) {
        return Result.fail("库存不足");
    }
    //7. 创建订单
    VoucherOrder voucherOrder = new VoucherOrder();
    //7.1 设置订单id
    long orderId = redisIdWorker.nextId("order");
    voucherOrder.setId(orderId);
    //7.2 设置用户id
    Long id = UserHolder.getUser().getId();
    voucherOrder.setUserId(id);
    //7.3 设置代金券id
    voucherOrder.setVoucherId(voucherId);
    //8. 将订单数据保存到表中
    save(voucherOrder);
    //9. 返回订单id
    return Result.ok(orderId);
}

要完成一人一单的业务应该把这个锁只加在单个用户上(用户标识可以用userId), 如果我们直接使用userId.toString()每次锁住的都不是同一个String对象

方法名功能
String intern()从常量池中拿数据,如果字符串常量池中已经包含了一个等于这个String对象的字符串(由equals方法确定)将返回池中的字符串
如果没有则将此String对象添加到池中并返回对此String对象的引用
public static String toString(long i) {
    if (i == Long.MIN_VALUE)
        return "-9223372036854775808";
    int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
    char[] buf = new char[size];
    getChars(i, size, buf);
    return new String(buf, true);
}
@Transactional
public Result createVoucherOrder(Long voucherId) {
    Long userId = UserHolder.getUser().getId();
    // toString的源码是new String所以userId.toString()拿到的也不是同一个String对象即不是同一个用户/不是同一把锁
    synchronized (userId.toString().intern()) {
        // 5.根据用户id查询用户对应的订单是否存在
        int count = query().eq("voucher_id", voucherId).eq("user_id", userId).count();
        // 判断用户是否下过单
        if (count > 0) {
            return Result.fail("您已经抢过优惠券了哦");
        }
        // 6.使用stock>0充当判断条件,在扣减库存时只要判断是否有剩余优惠券,即只要数据库中的库存大于0都能顺利完成扣减库存操作
        boolean success = seckillVoucherService.update()
            .setSql("stock= stock -1")
            .eq("voucher_id", voucherId).gt("stock",0).update(); //where voucher_id = ? and stock > 0
        if (!success) {
            return Result.fail("库存不足");
        }
        //7. 创建订单
        VoucherOrder voucherOrder = new VoucherOrder();
        //7.1 设置订单id
        long orderId = redisIdWorker.nextId("order");
        voucherOrder.setId(orderId);
        //7.2 设置用户id
        Long id = UserHolder.getUser().getId();
        voucherOrder.setUserId(id);
        //7.3 设置代金券id
        voucherOrder.setVoucherId(voucherId);
        //8. 将订单数据保存到表中
        save(voucherOrder);
        //9. 返回订单id
        return Result.ok(orderId);

    }
    //执行到这里锁已经被释放了但是可能当前事务还未提交,如果此时有线程进来不能确保事务不出问题
}

createVoucherOrder方法被Spring的事务控制,如果你在方法内部加锁可能会导致当前方法事务还没有提交但是锁已经释放了,此时新增的订单还没有写入数据库

  • 在seckillVoucher方法中将createVoucherOrder方法整体包裹起来, 保证事务提交之后才会释放锁确保数据库中有订单存在,同时控制锁的粒度
@Override
public Result seckillVoucher(Long voucherId) {
    LambdaQueryWrapper<SeckillVoucher> queryWrapper = new LambdaQueryWrapper<>();
    //1. 查询优惠券
    queryWrapper.eq(SeckillVoucher::getVoucherId, voucherId);
    SeckillVoucher seckillVoucher = seckillVoucherService.getOne(queryWrapper);
    //2. 判断秒杀时间是否开始
    if (LocalDateTime.now().isBefore(seckillVoucher.getBeginTime())) {
        return Result.fail("秒杀还未开始,请耐心等待");
    }
    //3. 判断秒杀时间是否结束
    if (LocalDateTime.now().isAfter(seckillVoucher.getEndTime())) {
        return Result.fail("秒杀已经结束!");
    }
    //4. 判断库存是否充足
    if (seckillVoucher.getStock() < 1) {
        return Result.fail("优惠券已被抢光了哦,下次记得手速快点");
    }
    // 获取用户id
    Long userId = UserHolder.getUser().getId();
    synchronized (userId.toString().intern()) {
        return createVoucherOrder(voucherId);
    }
}
@Transactional
public Result createVoucherOrder(Long voucherId) {
    // toString的源码是new String所以userId.toString()拿到的也不是同一个String对象即不是同一个用户/不是同一把锁
    synchronized (userId.toString().intern()) {
        // 5.根据用户id查询用户对应的订单是否存在
        int count = query().eq("voucher_id", voucherId).eq("user_id", userId).count();
        // 判断用户是否下过单
        if (count > 0) {
            return Result.fail("您已经抢过优惠券了哦");
        }
        // 6.使用stock>0充当判断条件,在扣减库存时只要判断是否有剩余优惠券,即只要数据库中的库存大于0都能顺利完成扣减库存操作
        boolean success = seckillVoucherService.update()
            .setSql("stock= stock -1")
            .eq("voucher_id", voucherId).gt("stock",0).update(); //where voucher_id = ? and stock > 0
        if (!success) {
            return Result.fail("库存不足");
        }
        //7. 创建订单
        VoucherOrder voucherOrder = new VoucherOrder();
        //7.1 设置订单id
        long orderId = redisIdWorker.nextId("order");
        voucherOrder.setId(orderId);
        //7.2 设置用户id
        Long id = UserHolder.getUser().getId();
        voucherOrder.setUserId(id);
        //7.3 设置代金券id
        voucherOrder.setVoucherId(voucherId);
        //8. 将订单数据保存到表中
        save(voucherOrder);
        //9. 返回订单id
        return Result.ok(orderId);

    }
    //执行到这里锁已经被释放了但是可能当前事务还未提交,如果此时有线程进来不能确保事务不出问题
}

由于seckillVoucher方法没有加事务注解,所以调用createVoucherOrder方法是this.的方式调用的,this此时是VoucherOrderServiceImpl没有事务功能

  • 事务想要生效需要利用VoucherOrderServiceImpl的代理对象,所以我们需要获得原始的事务对象来操作事务

  • 使用AopContext.currentProxy()获取当前对象的代理对象(具有事务功能),然后再用代理对象调用方法底层需要使用aspectjweaver依赖

  • 获取事务的代理对象需要在IVoucherOrderService中创建createVoucherOrder方法

  • 在启动类上加上@EnableAspectJAutoProxy(exposeProxy = true)注解

public interface IVoucherOrderService extends IService<VoucherOrder> {
    Result seckillVoucher(Long voucherId);
    Result createVoucherOrder(Long voucherId);
}
Long userId = UserHolder.getUser().getId();
synchronized (userId.toString().intern()) {
    IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
    return proxy.createVoucherOrder(voucherId);
}
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
</dependency>
@MapperScan("com.hmdp.mapper")
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
public class HmDianPingApplication {
    public static void main(String[] args) {
        SpringApplication.run(HmDianPingApplication.class, args);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/245778.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VMWare Tools 共享目录设置

vmware tools安装完成后&#xff0c;进入到工项目录设置 点击虚拟机设置->硬件->CD/DVD(SATA) &#xff0c;勾选使用物理驱动器&#xff0c;勾选自动检测 1、windows 操作系统设置 设置共享文件夹时&#xff0c;需要勾选 “ 在windows客户机中映射为网络驱动器”。 设置…

数字孪生Web3D智慧机房可视化运维云平台建设方案

前言 进入信息化时代&#xff0c;数字经济发展如火如荼&#xff0c;数据中心作为全行业数智化转型的智慧基座&#xff0c;重要性日益凸显。与此同时&#xff0c;随着东数西算工程落地和新型算力网络体系构建&#xff0c;数据中心建设规模和业务总量不断增长&#xff0c;机房管理…

AGI魔盒,会放出冥王PLUTO还是阿童木?

人机共生&#xff0c;是科幻作品永恒的主题。其中&#xff0c;《冥王PLUTO》可能是最早探讨人类与机器人如何在冲突中共存的漫画作品。 如果说阿童木是人机共生的“和平使者”&#xff0c;启蒙了几代人对机器人的信任和热爱,那么冥王PLUTO就是阿童木的反面&#xff0c;一个心怀…

nodejs+vue+微信小程序+python+PHP血液中心管理平台的设计与实现-计算机毕业设计推荐

实现采血的完整功能&#xff0c;系统用户主要分为两类&#xff0c;一类是管理员&#xff0c;一类是采血工作人员。管理员主要对采血工作人员以及血库进行管理。派发账号给员工作为采血工作人员&#xff0c;对血库的出库入库进行信息化管理。采血工作人员主要完成采血工作。通过…

快速碰撞刚性环境的机器人低阻抗控制(阻尼影响分析)

问题描述 在快速碰撞刚性环境的机器人低阻抗控制中&#xff0c;需要通过精确的碰撞检测和处理&#xff0c;以及低阻抗控制策略的优化&#xff0c;来减少碰撞对机器人和环境的影响。同时&#xff0c;我们还需要适应刚性环境&#xff0c;提高机器人的稳定性和鲁棒性&#xff0c;…

Linux(21):软件安装 RPM,SRPM 与 YUM

软件管理员简介 以原始码的方式来安装软件&#xff0c;是利用厂商释出的Tarball来进行软件的安装。 不过&#xff0c;你每次安装软件都需要侦测操作系统与环境、设定编译参数、实际的编译、最后还要依据个人喜好的方式来安装软件到定位。这过程是真的很麻烦的。 如果厂商先在他…

XUbuntu22.04之HDMI显示器设置竖屏(一百九十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

jrebel debug 启动不起来

idea更新之后jrebel debug模式启动不起来。 将下面的设置取消之后就可以了&#xff0c;希望能帮到你们… 被卡了两天… jrebel信息。 idea IntelliJ IDEA 2023.3.1 (Ultimate Edition) Build #IU-233.11799.300, built on December 12, 2023 Licensed to Alexandra Martin…

jmeter,读取CSV文件数据的循环控制

1、构造csv数据 保存文件时需要注意文件的编码格式 id,name,limit,status,address,start_time 100,小米100,1000,1,某某会展中心101,2023/8/20 14:20 101,小米101,1001,1,某某会展中心102,2023/8/21 14:20 2、在线程组下添加【CSV数据文件设置】元件 3、CSV文件数据的循环控…

【mysql】MySQL基础

什么是数据库 文件保存数据有以下几个缺点&#xff1a; 文件的安全性问题文件不利于数据查询和管理文件不利于存储海量数据文件在程序中控制不方便 所以设计出了数据库&#xff0c;用来管理数据。 数据库&#xff08;Database&#xff09;是按照数据结构来组织、存储和管理数…

QT- QT-lximagerEidtor图片编辑器

QT- QT-lximagerEidtor图片编辑器 一、演示效果二、关键程序三、下载链接 功能如下&#xff1a; 1、缩放、旋转、翻转和调整图像大小 2、幻灯片 3、缩略图栏&#xff08;左、上或下&#xff09;&#xff1b;不同的缩略图大小 4、Exif数据栏 5、内联图像重命名 6、自定义快捷方式…

DC-3靶场

DC-3 DC-3靶场链接&#xff1a;https://download.vulnhub.com/dc/DC-3-2.zip 下载后解压会有一个DC-3.ova文件&#xff0c;直接在vm虚拟机点击左上角打开-->文件--.选中这个.ova文件就能创建靶场&#xff0c;kali和靶机都调整至NAT模式 首先进行主机发现&#xff1a; arp…

Appium自动化常用adb操作封装

一、前置说明 在Appium自动化中&#xff0c;经常需要使用adb命令与设备进行交互&#xff0c;所以有必要把常用的adb操作封装成一个类 二、代码实现 import os import platform import re import subprocessfrom common import path from common.exception import AndroidSDK…

sysdig源码分析

Falco 0.6.0 Released New Features | Sysdig 在0.6.0之前&#xff0c;falco使用来自sysdig的内核模块sysdig-probe。从0.6.0开始&#xff0c;falco使用自己的内核模块falco-probe。内核模块实际上是由相同的源代码构建的&#xff0c;但是拥有一个特定于falco的内核模块允许fa…

电子科大陈瑞杰:开源不是那么的遥不可及

个人介绍 大家好&#xff0c;我是来自西安电子科技大学计算机学院软件工程专业的陈瑞杰&#xff0c;本科就读中北大学&#xff0c;本科通过校赛加入ACM校队&#xff0c;参与ACM-ICPC、CCPC等算法比赛&#xff0c;获区域赛铜奖(CCPC铜首&#xff0c;差点银&#xff0c;比较可惜…

【论文阅读】LoRA: Low-Rank Adaptation of Large Language Models

code&#xff1a;GitHub - microsoft/LoRA: Code for loralib, an implementation of "LoRA: Low-Rank Adaptation of Large Language Models" 做法&#xff1a; 把预训练LLMs里面的参数权重给冻结&#xff1b;向transformer架构中的每一层&#xff0c;注入可训练的…

IDEA中显示方法、类注释信息

目录 一、IDEA测试版本及环境二、操作步骤2.1 鼠标悬停在某一个方法上&#xff0c;从而显示方法的注释信息2.2 调用方法时同步显示方法注释信息2.3 在new一个对象时&#xff0c;这个对象有很多重载的构造方法&#xff0c;想要重载的构造函数都显示出来 一、IDEA测试版本及环境 …

2019年第八届数学建模国际赛小美赛B题数据中心冷出风口的设计解题全过程文档及程序

2019年第八届数学建模国际赛小美赛 B题 数据中心冷出风口的设计 原题再现&#xff1a; 这是数据中心空调设计面临的一个问题。在一些数据中心&#xff0c;计算机机柜是开放的&#xff0c;在一个房间里排列成三到四排。冷却后的空气通过主管进入房间&#xff0c;并分为三到四个…

聚观早报 |iOS17.3引入设备被盗保护;iPhone16或调整设计

【聚观365】12月14日消息 iOS17.3引入设备被盗保护 iPhone16或调整设计 马斯克星链网络使用量飙升 华为鸿蒙智行App正式上线 特斯拉人形机器人Optimus二代上线 iOS17.3引入设备被盗保护 苹果向iPhone用户推送了iOS17.3开发者预览版Beta更新&#xff0c;本次更新距离上次发…

【贝叶斯分析】计算机科学专业博士作业二

1 第一题 1.1 题目 已知变量A和B的取值只能为0或1&#xff0c;A⫫&#x1d469;&#xff0c;且&#x1d45d;(&#x1d434;1)0.65&#xff0c;&#x1d45d;(&#x1d435;1)0.77。C的取值与A和B有关&#xff0c;具体关系如下图所表&#xff1a; ABP(C1|A,B)000.1010.99100…