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

一.背景

最近在做一个项目的时候,由于涉及到需要将一个系统的基础数据全量同步到另外一个系统中去,结果一看,基础数据有十几万条,作为小白的我,使用单元测试,写了一段代码,直接采用了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 {@Autowiredprivate ITestUserService userService;@Testpublic 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")@Overridepublic 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")@Overridepublic 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 {@Overridepublic 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 {@Beanpublic 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 {@Autowiredprivate TestUserMapper testUserMapper;/*** 测试自定义批量新增*/@Testpublic 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 {@Autowiredprivate ITestUserService userService;@Autowiredprivate TestUserMapper testUserMapper;@Testpublic 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 {@Autowiredprivate ITestUserService userService;@Autowiredprivate TestUserMapper testUserMapper;@Testpublic 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.mzph.cn/news/732828.shtml

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

相关文章

element---tree树形结构(返回的数据与官方的不一样)

项目中要用到属性结构数据&#xff0c;后端返回的数据不是官方默认的数据结构&#xff1a; <el-tree:data"treeData":filter-node-method"filterNode":props"defaultProps"node-click"handleNodeClick"></el-tree>这是文档…

SpringCloudGateway全局过滤器

文章目录 全局过滤器的作用自定义全局过滤器过滤器执行的顺序 上一篇 Gateway理论与实践 介绍的过滤器&#xff0c;网关提供了31种&#xff0c;但每一种过滤器的作用都是固定的。如果我们希望拦截请求&#xff0c;做自己的业务逻辑则没办法实现。 全局过滤器的作用 全局过滤器的…

高级语言讲义2010计专(仅高级语言部分)

1.编写一程序&#xff0c;对输入的正整数&#xff0c;求他的约数和。 如&#xff1a;18的约数和为1236939 #include <stdio.h>int getsum(int n){int i,sum0;for(i1;i<n;i)if(n%i0)sumi;return sum; } int main(){int sum getsum(18);printf("%d",sum); …

JS直接量及其相关对象

什么是直接量 直接量是指不需要创建对象就可以直接使用的变量。ES中的直接量主要有三种类型&#xff1a;表示字符串的string类型、表示数字的number类型和表示true/false的boolean类型。当我们直接将值赋给变量后&#xff0c;ES就会自动判断其类型&#xff0c;而且当参数发生变…

Android14之编译输出system/product/vendor/odm分区(一百九十一)

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

深度学习相关概念及术语总结

目录 1.CNN2.RNN3.LSTM4.NLP5.CV6.正向传播7.反向传播8.sigmoid 函数9.ReLU函数10.假设函数11.损失函数12.代价函数 1.CNN CNN 是卷积神经网络&#xff08;Convolutional Neural Network&#xff09;的缩写。卷积神经网络是一种深度学习模型&#xff0c;专门用于处理具有网格状…

2024年3月ZZUACM 招新赛题解

2024年3月ZZUACM 招新赛 题号题目A区间次大值B上课签到C魔法森林&#xff08;一&#xff09;D魔法森林&#xff08;二&#xff09;ELOPF跳格子G猜数字H抽卡记录I安达的二维矩阵J安达的数字手术K跳楼梯L前缀和 A 区间次大值—循环/签到题 题目描述 给定一个 n n n的全排列 a i…

外包干了10天,技术退步明显···

先说一下自己的情况&#xff0c;本科生&#xff0c;通过校招进入杭州某软件公司&#xff0c;干了接近3年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试&…

【鸿蒙开发】第十七章 Web组件(一)

1 Web概述 Web组件用于在应用程序中显示Web页面内容&#xff0c;为开发者提供页面加载、页面交互、页面调试等能力。 页面加载&#xff1a;Web组件提供基础的前端页面加载的能力&#xff0c;包括&#xff1a;加载网络页面、本地页面、html格式文本数据。 页面交互&#xff1a…

JAVA使用的工具类-Hutool

文章目录 Hutool工具类简介1&#xff1a;身份证工具类相关方法1.1 身份证脱敏处理 字符串补零处理(此处是JAVA类的方法&#xff0c;并无引用StrUtil)springboot前后端分离&#xff0c;后端返回json字符串带斜杠问题处理 在这里整理一下博主常用的工具类方法-hutool工具类,这里囊…

制定一份完美的测试计划,让您的产品质量更上一层楼!

大家好&#xff0c;我是彭于晏。今天学习测试计划如何书写。 虽然很多人日常工作中都知道测试计划是什么&#xff0c;但是写好测试计划&#xff0c;其实并不容易。今天就来一起学习下测试计划如何书写。 什么是测试计划&#xff1f; 测试计划是一份为软件产品所准备的详细文档…

目标检测——监控下打架检测数据集

一、简述 首先&#xff0c;监控下打架检测是维护公共安全的重要手段。在公共场所、学校、监狱等地方&#xff0c;打架事件往往难以避免。通过安装打架检测监控系统&#xff0c;可以实时监控并准确识别打架事件&#xff0c;及时采取必要的应对措施&#xff0c;有效地减少打架事…

RNN预测正弦时间点

import torch.nn as nn import torch import numpy as np import matplotlib matplotlib.use(TkAgg) from matplotlib import pyplot as plt # net nn.RNN(100,10) #100个单词&#xff0c;每个单词10个维度 # print(net._parameters.keys()) #序列时间点预测num_time_steps 50…

java-ssm-jsp-基于ssm的宝文理学生社团管理系统

java-ssm-jsp-基于ssm的宝文理学生社团管理系统 获取源码——》公主号&#xff1a;计算机专业毕设大全

应对高并发的软件架构之道

在去年年终总结的时候&#xff0c;我提出了这样的困惑&#xff0c;究竟什么是真正的技术能力&#xff0c;是对于各种底层技术的钻研吗&#xff1f;钻研是好事&#xff0c;但实践下来&#xff0c;深入钻研并不在实际工作中有用&#xff0c;且钻研的越深&#xff0c;忘得越快&…

Leetcode : 1137. 高度检查器

学校打算为全体学生拍一张年度纪念照。根据要求&#xff0c;学生需要按照 非递减 的高度顺序排成一行。 排序后的高度情况用整数数组 expected 表示&#xff0c;其中 expected[i] 是预计排在这一行中第 i 位的学生的高度&#xff08;下标从 0 开始&#xff09;。 给你一个整数…

一篇搞懂什么是LRU缓存|一篇搞懂LRU缓存的实现|LRUCache详解和实现

LRUCache 文章目录 LRUCache前言项目代码仓库什么时候会用到缓存(Cache)缓存满了&#xff0c;怎么办&#xff1f;什么是LRUCacheLRUCache的实现LRUCache对应的OJ题实现LRUCache对应的STL风格实现 前言 这里分享我的一些博客专栏&#xff0c;都是干货满满的。 手撕数据结构专栏…

什么是测试用例?如何设计?

在学习或者实际的测试工作中经常都会提到“测试用例”这个词&#xff0c;没错&#xff0c;测试用例是测试工作的核心&#xff0c;不管要做的是什么样的测试&#xff0c;在真正动手执行测试之前&#xff0c;我们都需要先根据软件需求来设计测试用例&#xff0c;之后再依据设计好…

动态加权平衡损失:深度神经网络的类不平衡学习和置信度校准

系列文章目录 文章目录 系列文章目录前言一、研究目的二、研究方法创新点处理类不平衡的大多数方法交叉熵损失函数Brier Score 三、DWB Loss总结 前言 Dynamically Weighted Balanced Loss: ClassImbalanced Learning and Confidence Calibration of Deep Neural Networks 下载…

2024年3月10日 十二生肖 今日运势

小运播报&#xff1a;2024年3月10日&#xff0c;星期日&#xff0c;农历二月初一 &#xff08;甲辰年丁卯月癸酉日&#xff09;&#xff0c;法定节假日。 红榜生肖&#xff1a;龙、牛、蛇 需要注意&#xff1a;鸡、狗、兔 喜神方位&#xff1a;东南方 财神方位&#xff1a;…