Mybaties-Plus saveBatch()、自定义批量插入、多线程批量插入性能测试和对比

news2025/6/15 16:44:38

一.背景

最近在做一个项目的时候,由于涉及到需要将一个系统的基础数据全量同步到另外一个系统中去,结果一看,基础数据有十几万条,作为小白的我,使用单元测试,写了一段代码,直接采用了MP(Mybaties-Plus)自带的saveBatch()方法,将基础数据导入到新的系统中去,但是后面涉及多次修正基础数据的情况,导致,每次重新插入数据或者更新的时候,都需要花费十几分钟的时间,后面想着以下的方案进行了优化。
其实针对自带的saveBatch()方法插入很慢,一般都是由于数据库连接url上没有配置批量操作的属性,只需要在url上加上如下属性即可,如下:

rewriteBatchedStatements=true

在配置数据库连接信息的时候,配置类似如下:

jdbc:mysql://数据库地址/数据库名?useUnicode=true&characterEncoding=UTF8&allowMultiQueries=true&rewriteBatchedStatements=true

加上之后,你就会发现,saveBatch的速度直线提升,效果还是很不错的,一万条数据估计也就在几百毫秒。
接下来的文章都是设置在rewriteBatchedStatements=false情况下,且MP(Mybaties-Plus)为3.5.3.1版本下进行测试的。

二 .优化方法

如果在 rewriteBatchedStatements=false情况下,使用自带的方法,插入几十万数据是比较慢的,我们先讲解自带的方法,再讲解MP给我们自定义空间的自定义方法,然后在加入一些多线程的情况下进行的测试和方案比较。

2.1 Mybaties-plus自带的批量saveBatch()方法

直接上代码
实体类如下:

@Data
@TableName("test_user")
public class TestUser implements Serializable {
    private String id;
    private String name;
    private String managerId;
    private String salary;
    private String age;
    private String departId;
    private String remark;
    private String province;
}

Mapper如下:

public interface TestUserMapper extends BaseMapper<TestUser> {
}

Service如下:

public interface ITestUserService extends IService<TestUser> {
}

@Service
public class TestUserServiceImpl extends ServiceImpl<TestUserMapper, TestUser> implements ITestUserService {
}

接下来我使用单元测试的方法,构造200000条数据,测试Mybaties-Plus自带的saveBatch()方法,代码如下:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,classes = JeecgSystemApplication.class)
public class UserTest {

    @Autowired
    private ITestUserService userService;

    @Test
    public void testInsertBatch(){
        List<TestUser> userList = new ArrayList<>();
        for(int i = 0; i < 199999; i++){
            TestUser user = new TestUser();
            user.setName("张三");
            user.setAge("20");
            user.setProvince("重庆市");
            user.setSalary("200000");
            user.setRemark("diitch");
            userList.add(user);
        }
        long s = System.currentTimeMillis();
        userService.saveBatch(userList);
        System.out.println("保存200000条数据消耗" + (System.currentTimeMillis() - s) + "ms");
    }
    }

测试结果如下,大概需要10s中的时间:
在这里插入图片描述
我们可以跟踪源码,它的实现如下:

  default boolean saveBatch(Collection<T> entityList) {
        return this.saveBatch(entityList, 1000);
    }

 public boolean saveBatch(Collection<T> entityList, int batchSize) {
        String sqlStatement = this.getSqlStatement(SqlMethod.INSERT_ONE);
        return this.executeBatch(entityList, batchSize, (sqlSession, entity) -> {
            sqlSession.insert(sqlStatement, entity);
        });
    }

       public static <E> boolean executeBatch(Class<?> entityClass, Log log, Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
        Assert.isFalse(batchSize < 1, "batchSize must not be less than one", new Object[0]);
        return !CollectionUtils.isEmpty(list) && executeBatch(entityClass, log, (sqlSession) -> {
            int size = list.size();
            int idxLimit = Math.min(batchSize, size);
            int i = 1;

            for(Iterator var7 = list.iterator(); var7.hasNext(); ++i) {   ## 循环执行
                E element = var7.next();
                consumer.accept(sqlSession, element);
                if (i == idxLimit) {
                    sqlSession.flushStatements();
                    idxLimit = Math.min(idxLimit + batchSize, size);
                }
            }

        });
    }
2.2 自定义批量插入或者更新的方法

直接上代码,首先我们自定义一个RootMapper, 继承BaseMapper,自定义自己的批量插入或者更新方法,如下:

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.Collection;

/**
 * @author diitich
 * @param <T>
 */
public interface RootMapper<T> extends BaseMapper<T> {

    /**
     * 批量新增
     * @param batchList
     * @return
     */
    int insertBatch(@Param("list") Collection<T> batchList);

    /**
     * 批量跟新
     * @param batchList
     * @return
     */
    int updateBatch(@Param("list")Collection<T> batchList);

}

定义InsertBatchColumn 继承 AbstractMethod ,下面基本就是一些通用的写法,不同的Mybatis-plus有一点点区别,本文用的版本为3.5.3.1版本,代码如下:

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import java.util.List;
import java.util.function.Predicate;

@Slf4j
public class InsertBatchColumn extends AbstractMethod {

    @Setter
    @Accessors(chain = true)
    private Predicate<TableFieldInfo> predicate;

    public InsertBatchColumn() {
        super("insertBatch");
    }

    public InsertBatchColumn(Predicate<TableFieldInfo> predicate) {
        // 此处的名称必须与后续的RootMapper的新增方法名称一致
        super("insertBatch");
        this.predicate = predicate;
    }

    @SuppressWarnings("Duplicates")
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
        SqlMethod sqlMethod = SqlMethod.INSERT_ONE;
        List<TableFieldInfo> fieldList = tableInfo.getFieldList();
        String insertSqlColumn = tableInfo.getKeyInsertSqlColumn(true,false) +
                this.filterTableFieldInfo(fieldList, predicate, TableFieldInfo::getInsertSqlColumn, EMPTY);
        String columnScript = LEFT_BRACKET + insertSqlColumn.substring(0, insertSqlColumn.length() - 1) + RIGHT_BRACKET;
        String insertSqlProperty = tableInfo.getKeyInsertSqlProperty(true,ENTITY_DOT, false) +
                this.filterTableFieldInfo(fieldList, predicate, i -> i.getInsertSqlProperty(ENTITY_DOT), EMPTY);
        insertSqlProperty = LEFT_BRACKET + insertSqlProperty.substring(0, insertSqlProperty.length() - 1) + RIGHT_BRACKET;
        String valuesScript = SqlScriptUtils.convertForeach(insertSqlProperty, "list", null, ENTITY, COMMA);
        String keyProperty = null;
        String keyColumn = null;
        // 表包含主键处理逻辑,如果不包含主键当普通字段处理
        if (tableInfo.havePK()) {
            if (tableInfo.getIdType() == IdType.AUTO) {
                /* 自增主键 */
                keyGenerator = Jdbc3KeyGenerator.INSTANCE;
                keyProperty = tableInfo.getKeyProperty();
                keyColumn = tableInfo.getKeyColumn();
            } else {
                if (null != tableInfo.getKeySequence()) {
                    keyGenerator = TableInfoHelper.genKeyGenerator(modelClass.getName(), tableInfo, builderAssistant);
                    keyProperty = tableInfo.getKeyProperty();
                    keyColumn = tableInfo.getKeyColumn();
                }
            }
        }
        String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript, valuesScript);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        // 注意第三个参数,需要与后续的RootMapper里面新增方法名称要一致,不然会报无法绑定异常
        return this.addInsertMappedStatement(mapperClass, modelClass, "insertBatch", sqlSource, keyGenerator, keyProperty, keyColumn);
    }
}

定义 UpdateBatchColumn 继承 AbstractMethod ,代码如下:

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

public class UpdateBatchColumn extends AbstractMethod {

    public UpdateBatchColumn(String methodName) {
        super(methodName);
    }

    @SuppressWarnings("Duplicates")
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        String sql = "<script>\n<foreach collection=\"list\" item=\"item\" separator=\";\">\nupdate %s %s where %s=#{%s} %s\n</foreach>\n</script>";
        String additional = tableInfo.isWithVersion() ? tableInfo.getVersionFieldInfo().getVersionOli("item", "item.") : "" + tableInfo.getLogicDeleteSql(true, true);
        String setSql = sqlSet(tableInfo.isWithLogicDelete(), false, tableInfo, false, "item", "item.");
        String sqlResult = String.format(sql, tableInfo.getTableName(), setSql, tableInfo.getKeyColumn(), "item." + tableInfo.getKeyProperty(), additional);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
        // 第三个参数必须和RootMapper的自定义方法名一致
        return this.addUpdateMappedStatement(mapperClass, modelClass, "updateBatch", sqlSource);
    }

自定义sql注入,MysqlInjector继承DefaultSqlInjector ,代码如下:

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import java.util.List;

public class MysqlInjector extends DefaultSqlInjector {
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
        List<AbstractMethod> methods = super.getMethodList(mapperClass,tableInfo);
        // 自定义的insert SQL注入器
        methods.add(new InsertBatchColumn());
        // 自定义的update SQL注入器,参数需要与RootMapper的批量update名称一致
        methods.add(new UpdateBatchColumn("updateBatch"));
        return methods;
    }
}

定义MybatiesPlus的配置文件,将 MysqlInjector 注入进去,代码如下:

import org.jeecg.common.sqlinject.MysqlInjector;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatiesPlusConfig {
    @Bean
    public MysqlInjector sqlInjector(){
        return new MysqlInjector();
    }
}

接下来我们还是使用单元测试,构造200000万条数据,当然我们不能一次性插入20万条数据,进行分段插入,代码如下:

public interface TestUserMapper extends RootMapper<TestUser> {
}

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,classes = JeecgSystemApplication.class)
public class UserTest {

    @Autowired
    private TestUserMapper testUserMapper;

    /**
     * 测试自定义批量新增
     */
    @Test
    public void testInsertBatchCustom(){
        List<TestUser> userList = new ArrayList<>();
        int batchSize = 5000; // 每批次插入的数据量
        long s = System.currentTimeMillis();
        for(int i = 0; i < 199999; i++){
            TestUser user = new TestUser();
            user.setName("张三");
            user.setAge("20");
            user.setProvince("重庆市");
            user.setSalary("200000");
            user.setRemark("diitch");
            userList.add(user);
            // 达到批次大小时进行插入
            if(userList.size() == batchSize){
                testUserMapper.insertBatch(userList);
                userList.clear(); // 清空列表,准备下一批数据
            }
        }
// 插入剩余数据
        if(!userList.isEmpty()){
            testUserMapper.insertBatch(userList);
        }
        System.out.println("保存200000条数据消耗" + (System.currentTimeMillis() - s) + "ms");
    }
    }

上面的代码我们设置了一次性批量插入batchSize = 5000,执行结果如下,大概需要4~5秒,batchSize值设置不同,执行效率稍微有点不同:
在这里插入图片描述

2.3 多线程更新 + MP自带saveBatch()方法

上面我们讲了自定义批量插入大概能提升一倍的性能,接下来我们使用多线程方式更新数据,首先我们先测试使用5个线程插入20万条数据,使用Mybaties-plus自带的saveBatch()方法更新,直接上代码:

import org.jeecg.JeecgSystemApplication;
import org.jeecg.modules.demo.test.entity.TestUser;
import org.jeecg.modules.demo.test.mapper.TestUserMapper;
import org.jeecg.modules.demo.test.service.ITestUserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,classes = JeecgSystemApplication.class)
public class UserTest {

    @Autowired
    private ITestUserService userService;

    @Autowired
    private TestUserMapper testUserMapper;

     @Test
    public void testInsertBatchMulThreadSaveBatch() throws Exception{
        int totalRecords = 199999;
        int batchSize = 5000;
        int threadCount = 5; // 可以根据实际情况调整线程数量

        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
        List<Future<Void>> futures = new ArrayList<>();

        long s = System.currentTimeMillis();
        for (int i = 0; i < totalRecords; i += batchSize) {
            int startIndex = i;
            int endIndex = Math.min(i + batchSize, totalRecords);

            List<TestUser> batchList = new ArrayList<>();
            for (int j = startIndex; j < endIndex; j++) {
                TestUser user = new TestUser();
                user.setName("张三");
                user.setAge("20");
                user.setProvince("重庆市");
                user.setSalary("200000");
                user.setRemark("diitch");
                batchList.add(user);
            }

            Future<Void> future = executor.submit(() -> {
                userService.saveBatch(batchList);
                return null;
            });
            futures.add(future);
        }

        // 等待所有线程执行完成
        for (Future<Void> future : futures) {
            future.get();
        }

        executor.shutdown();
        System.out.println("保存200000条数据消耗" + (System.currentTimeMillis() - s) + "ms");
    }
     }

执行结果如下,大概需要3s多:
在这里插入图片描述

2.4 多线程 + 自定义批量插入方法

接下来我们还是使用5个线程来插入数据,只是使用我们自己定义的批量插入方法来插入数据,代码如下:

import org.jeecg.JeecgSystemApplication;
import org.jeecg.modules.demo.test.entity.TestUser;
import org.jeecg.modules.demo.test.mapper.TestUserMapper;
import org.jeecg.modules.demo.test.service.ITestUserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,classes = JeecgSystemApplication.class)
public class UserTest {

    @Autowired
    private ITestUserService userService;

    @Autowired
    private TestUserMapper testUserMapper;

 
    @Test
    public voidtestInsertBatchMulThreadCustom() throws Exception{
        int totalRecords = 199999;
        int batchSize = 5000;
        int threadCount = 5;

        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
        List<Future<Void>> futures = new ArrayList<>();
        Set<String> insertedData = Collections.synchronizedSet(new HashSet<>());

        long s = System.currentTimeMillis();
        for (int i = 0; i < totalRecords; i += batchSize) {
            int startIndex = i;
            int endIndex = Math.min(i + batchSize, totalRecords);

            List<TestUser> batchList = new ArrayList<>();
            for (int j = startIndex; j < endIndex; j++) {
                TestUser user = new TestUser();
                user.setName("张三");
                user.setAge("20");
                user.setProvince("重庆市");
                user.setSalary("200000");
                user.setRemark("diitch");
                batchList.add(user);
            }

            List<TestUser> filteredList = batchList.stream()
                    .filter(user -> !insertedData.contains(user.getName()))
                    .collect(Collectors.toList());

            Future<Void> future = executor.submit(() -> {
                testUserMapper.insertBatch(filteredList);
                filteredList.forEach(user -> insertedData.add(user.getName()));
                return null;
            });
            futures.add(future);
        }

     // 等待所有线程执行完成
        for (Future<Void> future : futures) {
            future.get();
        }

        executor.shutdown();
        System.out.println("保存200000条数据消耗" + (System.currentTimeMillis() - s) + "ms");
    }
}

执行结果如下,大概需要2s左右时间
在这里插入图片描述

三.总结

一般我们设置rewriteBatchedStatements=true时,批量插入功能已经相对较快,如果还满足不了需求,我们可以使用多线程进行批量插入,下面是在设置rewriteBatchedStatements=true时,插入20万条数据saveBatch()以及 saveBatch + 多线程的方式的执行结果:
单独的saveBatch()方法,差不多也是4秒多,也达到了我们自定义的批量插入方法性能:
在这里插入图片描述
saveBatch() + 多线程的方法,执行结果如下,大概只需要1秒多,比我们自定义批量插入 + 多线程方法还要快:

在这里插入图片描述

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

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

相关文章

Java新特性

本文重点分析Java12到Java17在性能方面和云计算方面取得的进展 Java 7&#xff0c;8&#xff0c;11. 17以及还未发布的Java 21均是LTS&#xff08;Long Term Support&#xff09;版本&#xff0c;Oracle提供5年的维护周期&#xff0c;以及3年的付费额外支持&#xff0c;一共8年…

ULTRAL SCALE FPGA TRANSCEIVER速率

CPLL支持2-6.25速率 QPLL支持速率 实际使用CPLL最高可以超过这个&#xff0c;QPLL最低也可以低于这个&#xff0c;xilinx留的阈量还是比较大。

5G智能制造纺织工厂数字孪生可视化平台,推进纺织行业数字化转型

5G智能制造纺织工厂数字孪生可视化平台&#xff0c;推进纺织行业数字化转型。纺织工业作为传统制造业的重要组成部分&#xff0c;面临着转型升级的紧迫需求。随着5G技术的快速发展&#xff0c;智能制造成为纺织工业转型升级的重要方向。数字孪生可视化平台作为智能制造的核心技…

Python快速入门系列-2(Python基础语法)

第三章&#xff1a;Python基础语法 3.1 变量与数据类型3.1.1 变量的定义与赋值3.1.2 数据类型3.1.3 类型转换 3.2 注释与缩进3.2.1 注释3.2.2 缩进 3.3 条件语句与循环结构3.3.1 条件语句3.3.2 循环结构 3.4 函数与模块3.4.1 函数3.4.2 参数和返回值3.4.3 模块3.4.4 标准库中的…

基于springboot+vue实现物资仓储物流管理系统项目【项目源码+论文说明】计算机毕业设计

基于springbootvue实现物资仓储物流管理系统演示 摘要 随着我国经济及产业化结构的持续升级&#xff0c;越来越多的企业借助信息化及互联网平台实现了技术的创新以及竞争力的提升&#xff0c;在电子经济的影响下仓储物流业务也获得了更多的关注度&#xff0c;利用系统平台实现…

KubeSphere4.0企业版

一、介绍 简要介绍 在 KubeSphere 企业版 v4.0 中&#xff0c;推出了全新的 KubeSphere 架构&#xff1a;KubeSphere LuBan&#xff0c;它构建在 ​​Kubernetes​​ 之上&#xff0c;支持高度可配置和可扩展。KubeSphere LuBan&#xff0c;是一个分布式的云原生可扩展开放架…

分享关于如何解决系统设计问题的逐步框架

公司广泛采用系统设计面试&#xff0c;因为在这些面试中测试的沟通和解决问题的技能与软件工程师日常工作所需的技能相似。面试官的评估基于她如何分析一个模糊的问题以及如何逐步解决问题。测试的能力还包括她如何解释这个想法&#xff0c;与他人讨论&#xff0c;以及评估和优…

突破编程_前端_JS编程实例(自适应表格列宽)

1 开发目标 针对如下的表格组件&#xff1a; 根据表格的各个列字符串宽度动态调整表格列宽&#xff1a; 2 详细需求 本组件目标是提供一个自动调整 HTML 表格列宽的解决方案&#xff0c;通过 JS 实现动态计算并调整表格每列的宽度&#xff0c;以使得表格能够自适应容器宽度&a…

00X集——cad vba 中arc(弧)对象详解

弧是CAD中一个常见的图元&#xff0c;在vba中的类名为AcadArc,创建方法为set myarc thisdrawing.modelspace.addarc(Center, Radius, StartAngle, EndAngle) Center 圆心&#xff08;Variant[变体] (三元素双精度数组); 仅用于输入 指定圆弧圆心的三维WCS坐标。&#xff09; …

SpringBoot学习之自定义注解和AOP 切面统一保存操作日志(二十九)

一、定义一个注解 这个注解是用来控制是否需要保存操作日志的自定义注解(这个类似标记或者开关) package com.xu.demo.common.anotation;import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; i…

视频可回溯系统技术方案vue3+ts+tegg+mysql+redis+oss

一、 项目背景 保险、基金、银行等众多行业在做技术平台时都会需要一种能够准确了解用户操作行为的方式方法。诸如通过埋点、平台监控、视频可回溯等&#xff0c;通过技术手段&#xff0c;保存用户操作轨迹&#xff0c;以此规范安全销售、平台健康检查、出现纠纷时可追溯、问题…

round四舍五入在python2与python3版本间区别

round()方法返回数值的小数点四舍五入到n个数字。 语法 以下是round()方法的语法&#xff1a; round( x ,n) 参数 x --这是一个数值&#xff0c;表示需要格式化的数值 n --这也是一个数值,表示小数点后保留多少位 返回值 该方法返回 数值x 的小数点四舍五入到n个数字 …

现在海外问卷调查项目还能做么?

可以做。 目前比较好做的问卷渠道有ROM、YUNO等&#xff0c;都是非常优质的渠道&#xff0c;也是现在很多公司正在做的渠道。 这些渠道每天的题目数量是很多的&#xff0c;根本做不完。每天只要花时间做就能获得不错的收入。 我自己做这个项目很长时间了&#xff0c;这个项目…

Pytorch学习 day09(简单神经网络模型的搭建)

简单神经网络模型的搭建 针对CIFAR 10数据集的神经网络模型结构如下图&#xff1a; 由于上图的结构没有给出具体的padding、stride的值&#xff0c;所以我们需要根据以下公式&#xff0c;手动推算&#xff1a; 注意&#xff1a;当stride太大时&#xff0c;padding也会变得很大…

力扣日记3.6-【回溯算法篇】51. N 皇后

力扣日记&#xff1a;【回溯算法篇】51. N 皇后 日期&#xff1a;2023.3.6 参考&#xff1a;代码随想录、力扣 51. N 皇后 题目描述 难度&#xff1a;困难 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将…

008-slot插槽

slot插槽 1、插槽 slot 的简单使用2、插槽分类2.1 默认插槽2.2 具名插槽2.3 作用域插槽 插槽就是子组件中的提供给父组件使用的一个占位符&#xff0c;用<slot></slot> 表示&#xff0c;父组件可以在这个占位符中填充任何模板代码&#xff0c;如 HTML、组件等&…

【PHP+代码审计】PHP基础——数据类型

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收…

【滑动窗口】力扣239.滑动窗口最大值

前面的文章我们练习数十道 动态规划 的题目。相信小伙伴们对于动态规划的题目已经写的 得心应手 了。 还没看过的小伙伴赶快关注一下&#xff0c;学习如何 秒杀动态规划 吧&#xff01; 接下来我们开启一个新的篇章 —— 「滑动窗口」。 滑动窗口 滑动窗口 是一种基于 双指…

【大模型系列】根据文本检索目标(DINO/DINOv2/GroundingDINO)

文章目录 1 DINO(ICCV2021, Meta)1.1 数据增强1.2 损失函数 2 DINOv2(CVPR2023, Meta)2.1 数据采集方式2.2 训练方法 3 Grounding DINO3.1 Grounding DINO设计思路3.2 网络结构3.2.1 Feature Extraction and Enhancer3.2.2 Language-Guided Query Selection3.2.3 Cross-Modalit…

云原生构建 微服务、容器化与容器编排

第1章 何为云原生&#xff0c;云原生为何而生 SOA也就是面向服务的架构 软件架构的发展主要经历了集中式架构、分布式架构以及云原生架构这几代架构的发展。 微服务架构&#xff0c;其实是SOA的另外一种实现方式&#xff0c;属于SOA的子集。 在微服务架构下&#xff0c;系统…