秒杀案例-分布式锁Redisson、synchronized、RedLock

news2024/12/23 19:08:01

模拟秒杀

  • 源码地址
  • 前期准备
    • 创建数据库表
    • 导入数据
    • dependencies
    • pom
    • Controller
    • TSeckillProduct
    • TseckillProductService
    • TseckillProductServiceImpl
    • TseckillProductMapper
    • TseckillProductMapper.xml
    • 使用JMeter压力测试
    • 开始测试
    • 超卖现象
    • 原因
    • 解决办法
    • 更改数据库库存500
    • 进行JMeter压力测试
    • 还有一种办法 在扣库存的时候加锁再去查询一下库存的数量
    • sql语句恢复原来的
  • 分布式锁
  • 整合Redisson分布式锁
    • 添加依赖
    • 添加配置文件
    • 添加redisson.yml
    • 更改扣库存代码TseckillProductServiceImpl.updateStockCount
    • 更改updateStockCount
    • 吞吐量
    • RedLock
    • 配置类
    • 案例代码
    • 异步Servlet
      • 传统Servlet请求示意图
      • 异步Servlet请求示意图
    • 测试异步Servlet
  • 库存预热
    • increment()
    • putIfAbsent 初始化库存
    • 代码初始化库存和redis递减秒杀
      • 初始化库存 Controller
      • TseckillProductServiceImpl
    • 设置初始化库存
    • 压力测试访问秒杀 /sec/preheat
    • 结果 发现库存没有多扣
    • 同步吞吐量
  • 使用异步线程池的方式进行秒杀
    • 代码
      • Controller
      • TseckillProductServiceImpl
    • 异步吞吐量
    • 使用redis的increment 方法实现递增递减的时候还需要加锁吗
  • 吞吐量分析
  • 优化线程池配置
    • 异步处理吞吐量成功提高
    • 订单

源码地址

https://gitee.com/Lin-seven/seckill

前期准备

创建数据库表

CREATE TABLE `t_seckill_product` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `product_id` bigint DEFAULT NULL,
  `seckill_price` decimal(10,2) DEFAULT NULL,
  `intergral` decimal(10,0) DEFAULT NULL,
  `stock_count` int DEFAULT NULL,
  `start_date` date DEFAULT NULL,
  `time` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8mb3;

导入数据

INSERT INTO `shop-seckill`.`t_seckill_product`(`id`, `product_id`, `seckill_price`, `intergral`, `stock_count`, `start_date`, `time`) VALUES (2, 23, 3699.00, 36990, 10, '2024-07-15', 10);

dependencies

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>



        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.79</version>
        </dependency>


    </dependencies>

pom

# 应用服务 WEB 访问端口
server:
  port: 8899
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/shop-seckill?serverTimezone=GMT%2B8
    username: root
    password: 123456
  application:
    name: shop-seckill


#开启日志
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    mapper-locations: classpath*:/**/mapper/**/*.xml



Controller

package com.lx.seckill.controller;

import com.lx.seckill.pojo.TSeckillProduct;
import com.lx.seckill.service.TseckillProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import java.util.List;
import java.util.UUID;

/**
 * TODO 添加描述
 *
 * @author wangLJ
 * @date 2024/7/18 11:33
 */
@RestController
@RequestMapping("/sec")
public class SeckillContoller {

    public Integer count =0;
    @Autowired
    private TseckillProductService service;

    @GetMapping("/list")
    public List<TSeckillProduct> getList(){
        List<TSeckillProduct> list = service.list();

        System.out.println(count);
        return list;
    }
    @GetMapping("/byid")
    public  TSeckillProduct  byid(){
         TSeckillProduct  list = service.byid();


        return list;
    }
    
 @GetMapping("/kill")
    public String kill()  {
 

        TSeckillProduct  list = service.byid();

        if (list.getStockCount()>0){
            //扣减库存
            service.updateStockCount();
        }
      
        return "ok";
    }


}

TSeckillProduct

@Data
//@TableName(value = "eric_user")
public class TSeckillProduct {
  @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
  /**
   * 商品ID
   */
  private Integer productId;
  // 价格
    private Integer seckillPrice;
    //价格
    private Integer intergral;
    //数量
    private Integer stockCount;
    //场次
    private Integer time;

}

TseckillProductService

public interface TseckillProductService {
    List<TSeckillProduct> list();

    TSeckillProduct byid();

    void updateStockCount();
}

TseckillProductServiceImpl

@Service
public class TseckillProductServiceImpl extends ServiceImpl<TseckillProductMapper, TSeckillProduct> implements TseckillProductService{
    @Autowired
    TseckillProductMapper mapper;
    @Override
    public List<TSeckillProduct> list() {

        return super.list();
    }

    @Override
    public TSeckillProduct byid() {
        TSeckillProduct tSeckillProduct = mapper.selectById(2);
        return tSeckillProduct;
    }

 
    @Override
    public  void updateStockCount() {
       
            //扣减库存
            mapper.updateStockCount( );
       

    }

}

TseckillProductMapper

@Mapper
public interface  TseckillProductMapper extends BaseMapper<TSeckillProduct> {

    void updateStockCount();
}

TseckillProductMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.lx.seckill.mapper.TseckillProductMapper">


    <update id="updateStockCount">
        UPDATE  t_seckill_product set stock_count=stock_count-1 where id =2
    </update>
</mapper>

使用JMeter压力测试

安装教程https://blog.csdn.net/m0_37583655/article/details/126507267-靖节先生
线程组
在这里插入图片描述

安装好之后添加查看结果树和聚合报告
在这里插入图片描述

开始测试

在这里插入图片描述
填好对应的后端地址 点击开始

超卖现象

在这里插入图片描述
发现库存都成负数了。这是为什么呢?

原因

原因在于,有很多线程同时执行了service.byid();方法
拿到的库存,保存在自己的栈帧中。如果有一百个线程拿到的库存是大于0的,就有一百个线程去执行扣库存的方法

解决办法

使用synchronized
在去查询库存的时候加锁

  @Override
    public  synchronized TSeckillProduct byid() {
        TSeckillProduct tSeckillProduct = mapper.selectById(2);
        return tSeckillProduct;
    }

更改数据库库存500

在这里插入图片描述

进行JMeter压力测试

在这里插入图片描述
通过测试发现,库存总是等于-1
这是因为
读取与更新之间的窗口期: 当一个线程读取库存后,其他线程可能已经完成了库存的扣减操作,但当前线程由于持有库存值的本地副本,仍可能认为库存充足而继续执行扣减操作。
数据库事务隔离级别: 即使使用了@Transactional注解,如果数据库的事务隔离级别设置不当(例如,默认的READ_COMMITTED),在读取库存和更新库存之间,其他事务可能已经改变了库存值,导致当前事务基于过期数据进行操作。

为了防止库存值变为负数,您可以采取以下几种策略之一:
使用乐观锁: 在数据库表中增加一个版本号字段,每次更新库存时也更新版本号,并在更新语句中加入版本号的检查,以确保数据的一致性。

 UPDATE t_seckill_product SET stock_count = stock_count - 1 WHERE id = 2 AND stock_count > 0

使用悲观锁: 在查询库存时即锁定库存记录,直到事务结束。这可以通过SQL语句中的FOR UPDATE关键字实现。
原子操作:使用数据库提供的原子操作,如MySQL的INNODB存储引擎支持的原子自减操作,直接在数据库层面完成库存的扣减和检查。

UPDATE t_seckill_product SET stock_count = stock_count - 1 WHERE id = 2   FOR UPDATE

还有一种办法 在扣库存的时候加锁再去查询一下库存的数量

去除TSeckillProduct方法的synchronized 在updateStockCount方法加上synchronized

    @Override
    public    TSeckillProduct byid() {
        TSeckillProduct tSeckillProduct = mapper.selectById(2);
        return tSeckillProduct;
    }

    @Transactional
    @Override
    public  synchronized void updateStockCount() {
        TSeckillProduct tSeckillProduct = mapper.selectById(2);
        if (tSeckillProduct.getStockCount()>0){
            //扣减库存
            mapper.updateStockCount( );
        }
    }

sql语句恢复原来的

 <update id="updateStockCount">
        UPDATE  t_seckill_product set stock_count=stock_count-1 where id =2
    </update>

分布式锁

如果是集群架构,以上使用synchronized 就不适用了。因为synchronized 锁住的只是当前JVM中的this对象,集群状态下,还是有可能发生并发关系,同时执行到updateStockCount,当获取的库存都会存入自己的栈帧,所以还会导致库存超卖的情况

整合Redisson分布式锁

添加依赖

<!--        redisson分布式锁-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.13.6</version>
        </dependency>

添加配置文件

  # redis配置
spring:
  redis:
    database: 0
    host: 127.0.0.1
#    password: redis@pass
    port: 6379
    redisson:
      file: classpath:redisson.yml

添加redisson.yml

application.yml与 redisson.yml在同级,目录结构如下:

# 单节点配置
singleServerConfig:
  # 数据库编号
  database: 0
  # 节点地址
  address: redis://127.0.0.1:6379
  # 密码
#  password: redis@pass

更改扣库存代码TseckillProductServiceImpl.updateStockCount

注入RedissonClient

 @Resource
 RedissonClient redissonClient;

更改updateStockCount

@Transactional
    @Override
    public    void updateStockCount() {

        /**
         * 获取一把锁,只要锁的名字一样,就是同一把锁,"my-lock"是锁名,也是Redis的哈希模型的对外key
         */
        RLock lock = redissonClient.getLock("my-lock");
        //加锁
        /**
         * 阻塞式等待,默认加的锁等待时间为30s。每到20s(经过三分之一看门狗时间后)就会自动续借成30s
         * 1.锁的自动续期,如果在业务执行期间业务没有执行完成,redisson会为该锁自动续期
         * 2.加锁的业务只要运行完成,就不会自动续期,即使不手动解锁,锁在默认的30s后会自动删除
         */
       // lock.lock();
        /**
         * (推荐)指定锁的过期时间,看门狗不会自动续期:
         * 在自定义锁的存在时间时不会自动解锁
         * 注意:
         * 设置的自动解锁时间一定要稳稳地大于业务时间
         */
        lock.lock(30, TimeUnit.SECONDS);

        try {
            TSeckillProduct tSeckillProduct = mapper.selectById(2);
            if (tSeckillProduct.getStockCount()>0){
                //扣减库存
                mapper.updateStockCount( );
            }
        } finally {
            //释放锁
            lock.unlock();
        }

    }

分布式公平锁、读写锁、信号量、闭锁请查看文档
https://www.yuque.com/manmushanhe-kfrkq/pgm4gc/lpmvfoqmo1yzq8yv

吞吐量

在这里插入图片描述

RedLock

分布式锁算法,通过多个Redis实例来实现高可用的分布式锁服务,确保分布式系统中的数据安全和一致性。
适用于redis 集群模式下

依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.16.2</version>
</dependency>

配置类

@Configuration
public class RedLockConfig {

    @Bean(destroyMethod="shutdown")
    RedissonClient redissonClient() {
        Config config = new Config();
        // 可以配置多个Redis节点
        config.useClusterServers()
              .setScanInterval(2000)
              .addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001");
        return Redisson.create(config);
    }
}

案例代码

  @Autowired
    private RedissonClient redissonClient;

    public void yourMethod() {
        // 获取RedLock
        RedLock redLock = new RedLock(redissonClient);

        // 加锁
        boolean lockAcquired = redLock.lock("yourLockKey");
        try {
            if (lockAcquired) {
                // 业务逻辑
            } else {
                // 处理锁竞争
            }
        } finally {
            // 释放锁
            redLock.unlock("yourLockKey");
        }
    }

异步Servlet

传统Servlet请求示意图

在这里插入图片描述

同步阻塞: 传统Servlet采用同步阻塞的方式处理请求。每个请求到达时,Servlet容器会为其分配一个线程,直到请求处理完成超时才释放线程
线程资源消耗: 每个请求都需要一个独立的线程,当请求需要等待IO操作或其他资源时,线程会被阻塞,导致资源浪费。
**简单易用:**开发和理解成本较低,符合传统的Servlet编程模型,适用于一般性的Web应用。

适用场景:

小规模并发请求的应用场景。
对响应时间和吞吐量要求不高的应用。

异步Servlet请求示意图

在这里插入图片描述

非阻塞异步:异步Servlet通过异步处理请求,不会阻塞服务器的主线程,可以在处理请求的过程中释放线程资源,等待IO操作完成后再继续处理。
性能优化:能够在高并发情况下显著提高系统的吞吐量和响应速度,有效地利用系统资源。

适用场景:
高并发请求的应用场景,特别是需要处理大量IO操作的情况,如文件上传、推送服务等。
对于需要提高系统性能和响应速度的要求较高的应用。

传统Servlet和异步Servlet虽然都不能提高用户的等待时间但是能够提高系统的线程的利用率

作用:异步Servlet允许Servlet容器在处理请求时不阻塞线程,从而提高服务器的并发处理能力和资源利用率。它特别适合处理需要长时间计算或等待外部资源响应的请求,如文件上传、图像处理等。
实现方式:通过AsyncContext接口实现,允许Servlet在处理请求时将其放入异步处理模式,然后在后台线程完成处理,并在处理完成后返回响应。

测试异步Servlet

创建线程池

@Configuration
public class AsyncConfig {

    @Bean(name = "threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5); // 核心线程数
        executor.setMaxPoolSize(10); // 最大线程数
        executor.setQueueCapacity(20); // 队列大小
        executor.setThreadNamePrefix("MyExecutor-"); // 线程名前缀
        executor.initialize();
        return executor;
    }

}

**测试 使用CompletableFuture开启异步线程 **

 @Autowired
    private ThreadPoolTaskExecutor executor;

    /**
     *  异步请求
     * @return
     */
    @GetMapping("/asynd")
    @Async("threadPoolTaskExecutor") // 指定使用哪个TaskExecutor
    public CompletableFuture<String> handleAsyncRequest() {
        return CompletableFuture.supplyAsync(() -> {
            String threadName = Thread.currentThread().getName();
            System.out.println("阿里 - " +threadName);
            // 模拟一个耗时操作
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("Sleep interrupted", e);
            }
            return "Hello from async method!";
        }, executor);
    }

库存预热

库存每次都去查询,这样会造成每次连接数据库,就会造成数据库的压力。
可以使用redisdincrement 方法 实现递增的功能

increment()

increment() 方法是原子性的,确保多线程或多个客户端同时对同一个字段进行递增操作时的线程安全性 如果Redis中不存在这个key
的值, 会将其初始化为0然后执行递增操作。

putIfAbsent 初始化库存

opsForHash().putIfAbsent() 方法的作用是在哈希表中设置字段的值,
仅当字段不存在时才会设置成功。如果字段已经存在,这个方法不会更新现有的值,而是保持当前的值不变。
如果想要改变key的值 可以直接使用PUT 方法

代码初始化库存和redis递减秒杀

初始化库存 Controller

  /**
     * 初始化库存
     * @return
     */
    @RequestMapping("/putIfAbsent")
    @ResponseBody
    public String  putIfAbsent() {
        return service.putIfAbsent();

    }
       /**
     * 库存预热-秒杀
     * @return
     */
    @RequestMapping("/preheat")
    @ResponseBody
    public String  preheat() {
       return service.preheat();

    }


TseckillProductServiceImpl

 @Autowired
    private StringRedisTemplate redisTemplate;
 @Override
    public String putIfAbsent() {
        /**
             * opsForHash().putIfAbsent() 方法的作用是在哈希表中设置字段的值,
             * 但仅当字段不存在时才会设置成功。如果字段已经存在,这个方法不会更新现有的值,而是保持当前的值不变。
         */
        TSeckillProduct tSeckillProduct = mapper.selectById(2);
        Integer stockCount = tSeckillProduct.getStockCount();
		 /**
         * 应该使用putIfAbsent 方法
         * redisTemplate.opsForHash().putIfAbsent("product:stock", "quantity", stockCount+"");
         * 也可以使用
         *  redisTemplate.opsForValue().setIfAbsent("product:stock", stockCount + "");
         * 测试为了方便设置库存,从而采用put 方法
         */
        //redisTemplate.opsForHash().putIfAbsent("product:stock", "quantity", stockCount+"");
       redisTemplate.opsForHash().put("product:stock", "quantity", stockCount+"");
        return "库存"+stockCount ;
    }
 @Override
    public String preheat() {

        Long result = redisTemplate.opsForHash().increment("product:stock", "quantity", -1);
        System.out.println(result);
        if (result >= 0) {
            //扣减库存
            mapper.updateStockCount();
            return result + "";
        } else {
            return "库存不足";
        }
    }

设置初始化库存

初始化库存----- http://localhost:8899/sec/putIfAbsent

压力测试访问秒杀 /sec/preheat

在这里插入图片描述

结果 发现库存没有多扣

同步吞吐量

在这里插入图片描述

使用异步线程池的方式进行秒杀

代码

Controller

 @RequestMapping("/asynPreheat")
    @ResponseBody
    public CompletableFuture<String>  asynPreheat() {

        return service.asynPreheat();

    }

TseckillProductServiceImpl

  @Async("threadPoolTaskExecutor") // 指定使用哪个TaskExecutor
    @Override
    public CompletableFuture<String> asynPreheat() {
        return CompletableFuture.supplyAsync(() -> {
            Long result = redisTemplate.opsForHash().increment("product:stock", "quantity", -1);
            // 模拟一个耗时操作
            if (result >= 0) {
                //扣减库存
                mapper.updateStockCount();
                return result + "";
            } else {
                return "库存不足";
            }
        }, executor);


    }

}

异步吞吐量

在这里插入图片描述

使用redis的increment 方法实现递增递减的时候还需要加锁吗

是 Redis 提供的原子性操作,确保在并发情况下递增或递减哈希表中的字段值是线程安全的。这是因为 Redis 单个命令的执行是原子性的,不会被其他命令打断,所以递增或递减操作是安全的。

吞吐量分析

为什么使用线程池的asynPreheat的吞吐量是10.5/sec(低) 反而没有使用线程池preheat 的吞吐量是919/sec(较高)

asynPreheat() 方法:

使用了 @Async 注解,表明该方法会在一个单独的线程中执行,并且指定了一个自定义的线程池 (executor)。
异步方法通常会有额外的线程切换和调度开销,尤其是在高并发情况下,线程池可能会面临任务排队和等待执行的情况。

preheat() 方法:

这是一个同步方法,每次调用都会在当前线程中执行,没有额外的线程开销。 吞吐量高达 919/sec
可能是因为每次请求都能快速响应,并且没有异步等待线程切换的开销

优化线程池配置

  1. 更改核心线程数
  2. 更改最大线程数
  3. 更改队列大小
@Bean(name = "threadPoolTaskExecutor")
   public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
       ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
       executor.setCorePoolSize(20); // 核心线程数
       executor.setMaxPoolSize(30); // 最大线程数
       executor.setQueueCapacity(90); // 队列大小
       executor.setThreadNamePrefix("MyExecutor-"); // 线程名前缀
       executor.initialize();
       return executor;
   }

异步处理吞吐量成功提高

在这里插入图片描述

订单

如果每次库存成功之后,就去向数据库插入订单信息,就是频繁的访问数据库,造成数据库的压力。如果库存数量比较小,可以忽略,如果库存较大,而且又是热点商品,可以采用消息中间的的办法,如MQ,KAFKA 先把用户的订单信息保存起来,之后在逐步消费消息中间件的消息

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux_top命令打印结果_PID USER PR NI VIRT RES SHR S 什么意思

top命令输出结果 含义 top 命令是 Linux 和 Unix 系统中用于实时显示系统中各个进程的资源占用情况的工具。当你运行 top 命令并查看输出结果时&#xff0c;会看到类似下面的列&#xff08;具体的列可能因 top 的版本和配置而有所不同&#xff09;&#xff1a; PID: 进程ID&a…

NSS [NSSRound#4 SWPU]ez_rce

NSS [NSSRound#4 SWPU]ez_rce CVE-2021-41773 Apache Httpd Server 路径穿越漏洞 POC: GET /cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/bin/sh HTTP/1.1 Host: node4.anna.nssctf.cn:28690 Cache-Control: max-age0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Window…

C/C++教程合集(完)

C初级教程(非常基础&#xff0c;适合入门)入门C语言只需一个星期&#xff08;星期一&#xff09;入门C语言只需一个星期&#xff08;星期二&#xff09;入门C语言只需一个星期&#xff08;星期三&#xff09;入门C语言只需一个星期&#xff08;星期四)入门C语言只需一个星期&am…

NSS [NSSRound#13 Basic]flask?jwt?

NSS [NSSRound#13 Basic]flask?jwt? 开题 注册一下 要admin才能拿flag 看看是如何进行身份验证的 是flask session flask-unsign --decode --cookie .eJwtzjESwyAMBMC_UKfghJCEP-MRICZp7bjK5O9xkX6L_aR9HXE-0_Y-rnik_TXTlsiXEhUXleKGGGuG1jbmogrCEmNirZ7BEB-VJbTfIi-26hQD…

数据库实例迁移实践

背景 随着业务发展&#xff0c;数据库实例磁盘逐渐升高&#xff0c;告警频繁&#xff0c;且后续可能会对DDL产生影响&#xff08;尤其是借助ghost等工具执行的DDL&#xff09;。 该实例有多个库&#xff0c;则需要迁移其中的一个或几个单库到其他实例&#xff0c;为什么不做分…

【NPU 系列专栏 3.1 -- - NVIDIA 的 Orin 和 Altan 和 Thor 区别】

请阅读【嵌入式及芯片开发学必备专栏】 文章目录 NVIDIA Orin、Altan 和 ThorNVIDIA Orin 简介NVIDIA Orin 主要特点NVIDIA Orin 应用场景 NVIDIA Altan 简介NVIDIA Altan 主要特点NVIDIA Altan 应用场景 NVIDIA Thor 简介NVIDIA Thor 主要特点NVIDIA Thor 应用场景 与 Hopper …

CTF-NSSCTF题单[GKCTF2020]

[GKCTF 2020]CheckIN 这道题目考察&#xff1a;php7-gc-bypass漏洞 打开这道题目&#xff0c;开始以为考察反序列化&#xff0c;但实际并不是&#xff0c;这里直接用$_REQUEST传入了参数便可以利用了。这里出现了一个eval&#xff08;&#xff09;函数&#xff0c;猜测考察命…

暑期C++ 缺省参数

有任何不懂的问题可以评论区留言&#xff0c;能力范围内都会一一回答 1.缺省参数的概念 缺省参数是是声明或定义参数时为函数的参数指定一个缺省值。在调用该函数值时&#xff0c;如果没有指定实参则采用该形参的缺省值&#xff0c;否则使用指定的实参 看了上面定义后&#…

CogVLMv2环境搭建推理测试

引子 之前写过一篇CogVLM的分享&#xff0c;感兴趣的移步CogVLM/CogAgent环境搭建&推理测试-CSDN博客&#xff0c;前一阵子&#xff0c;CogVLMv2横空出世&#xff0c;支持视频理解功能&#xff0c;OK&#xff0c;那就让我们开始吧。 一、模型介绍 CogVLM2 系列模型开源了…

基于Vision Transformer的mini_ImageNet图片分类实战

【图书推荐】《PyTorch深度学习与计算机视觉实践》-CSDN博客 PyTorch计算机视觉之Vision Transformer 整体结构-CSDN博客 mini_ImageNet数据集简介与下载 mini_ImageNet数据集节选自ImageNet数据集。ImageNet是一个非常有名的大型视觉数据集&#xff0c;它的建立旨在促进视觉…

旗晟机器人仪器仪表识别AI智慧算法

在当今迅猛发展的工业4.0时代&#xff0c;智能制造和自动化运维已然成为工业发展至关重要的核心驱动力。其中智能巡检运维系统扮演着举足轻重的角色。工业场景上不仅要对人员行为监督进行监督&#xff0c;对仪器仪表识别分析更是不可缺少的一个环节。那么我们说说旗晟仪器仪表识…

AI模型大比拼:Claude 3系列 vs GPT-4系列最新模型综合评测

AI模型大比拼&#xff1a;Claude 3系列 vs GPT-4系列最新模型综合评测 引言 人工智能技术的迅猛发展带来了多款强大的语言模型。本文将对六款领先的AI模型进行全面比较&#xff1a;Claude 3.5 Sonnet、Claude 3 Opus、Claude 3 Haiku、GPT-4、GPT-4o和GPT-4o Mini。我们将从性能…

【Gin】精准应用:Gin框架中工厂模式的现代软件开发策略与实施技巧(下)

【Gin】精准应用&#xff1a;Gin框架中工厂模式的现代软件开发策略与实施技巧(下) 大家好 我是寸铁&#x1f44a; 【Gin】精准应用&#xff1a;Gin框架中工厂模式的现代软件开发策略与实施技巧(下)✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 前言 本次文章分为上下两部分&…

智能家居全在手机端进行控制,未来已来!

未来触手可及&#xff1a;智能家居&#xff0c;手机端的全控时代 艾斯视觉的观点是&#xff1a;在不远的将来&#xff0c;家&#xff0c;这个温馨的港湾&#xff0c;将不再只是我们休憩的场所&#xff0c;而是科技与智慧的结晶。想象一下&#xff0c;只需轻触手机屏幕&#xf…

如何实现CPU最大处理效率

如何实现CPU最大处理效率 CPU,或称为中央处理器,是计算机中负责执行指令和处理数据的核心部件。它的工作原理可简单概括为"取指、译码、执行、存储"四个步骤,也称为计算机的指令周期。 取指(Fetch):在取指阶段,CPU从内存中获取下一条要执行的指令,并存放在指…

回顾网络路,心率就过速

笔者上网写作已满16年&#xff0c;其间加盟过国内互联网的知名网站自媒体至少在40至50家之多&#xff0c;但由于有的被已被勒令停刊了&#xff08;如《天涯论坛》&#xff09;&#xff0c;有的则因其改版而只保留了极少数擅于唱颂的写手&#xff08;如《强国论坛》&#xff09;…

【SpringCloud】企业认证、分布式事务,分布式锁方案落地-1

目录 HR企业入驻 HR企业入驻 - 认证流程解析 HR企业入驻 - 查询企业是否存在 HR企业入驻 - 上传企业logo与营业执照 HR企业入驻 - 新企业&#xff08;数据字典与行业tree结构解析&#xff09; 行业tree 行业tree - 创建节点 行业tree - 查询一级分类 行业tree - 查询子分…

计算存储背景与发展

随着云计算、企业级应用以及物联网领域的飞速发展&#xff0c;当前的数据处理需求正以前所未有的规模增长&#xff0c;以满足存储行业不断变化的需求。这种增长导致网络带宽压力增大&#xff0c;并对主机计算资源&#xff08;如内存和CPU&#xff09;造成极大负担&#xff0c;进…

Redis的使用场景——热点数据缓存

热点数据缓存 Redis的使用场景——热点数据的缓存 1.1 什么是缓存 为了把一些经常访问的数据&#xff0c;放入缓存中以减少对数据库的访问效率&#xff0c;从而减少数据库的压力&#xff0c;提高程序的性能。【在内存中存储】 1.2 缓存的原理 查询缓存中是否存在对应的数据如…

05 capture软件创建元器件库(以STM32为例)

05 创建元器件库_以STM32为例 一、新建原理图库文件二、新建器件三、开始创建元器件 一些IC类元件&#xff0c;需要自己创建元器件库。 先看视频&#xff0c;然后自己创建STM32F103C8T6的LQFP48的元器件。 STM32F103C8T6是目前为止&#xff0c;自己用的最多的芯片。 先要有数据…