百万级数据的导出解决方案

news2025/1/20 10:56:46

一、传统POI的的版本优缺点比较

首先我们知道POI中我们最熟悉的莫过于WorkBook这样一个接口,我们的POI版本也在更新的同时对这个几口的实现类做了更新;
HSSFWorkbook :
这个实现类是我们早期使用最多的对象,它可以操作Excel2003以前(包含2003)的所有Excel版本。在2003以前Excel的版本后缀还是.xls
XSSFWorkbook :
这个实现类现在在很多公司都可以发现还在使用,它是操作的Excel2003--Excel2007之间的版本,Excel的扩展名是.xlsx
SXSSFWorkbook :
这个实现类是POI3.8之后的版本才有的,它可以操作Excel2007以后的所有版本Excel,扩展名是.xlsx
  1. HSSFWorkbook

它是POI版本中最常用的方式,不过:
它的缺点是 最多只能导出 65535行,也就是导出的数据函数超过这个数据就会报错;
它的优点是 不会报内存溢出。(因为数据量还不到7w所以内存一般都够用,首先你得明确知道这种方式是将数据先读取到内存中,然后再操作)
  1. XSSFWorkbook

优点:这种形式的出现是为了突破HSSFWorkbook的65535行局限,是为了针对Excel2007版本的1048576行,16384列,最多可以导出104w条数据;
缺点:伴随的问题来了,虽然导出数据行数增加了好多倍,但是随之而来的内存溢出问题也成了噩梦。因为你所创建的book,Sheet,row,cell等在写入到Excel之前,都是存放在内存中的(这还没有算Excel的一些样式格式等等),可想而知,内存不溢出就有点不科学了!!!
  1. SXSSFWorkbook

从POI 3.8版本开始,提供了一种基于XSSF的低内存占用的SXSSF方式;
优点:
这种方式不会一般不会出现内存溢出(它使用了硬盘来换取内存空间,
也就是当内存中数据达到一定程度这些数据会被持久化到硬盘中存储起来,而内存中存的都是最新的数据),
并且支持大型Excel文件的创建(存储百万条数据绰绰有余)。
缺点:
既然一部分数据持久化到了硬盘中,且不能被查看和访问那么就会导致,
在同一时间点我们只能访问一定数量的数据,也就是内存中存储的数据;
sheet.clone()方法将不再支持,还是因为持久化的原因;
不再支持对公式的求值,还是因为持久化的原因,在硬盘中的数据没法读取到内存中进行计算;
在使用模板方式下载数据的时候,不能改动表头,还是因为持久化的问题,写到了硬盘里就不能改变了;

二、 适用场景

经过了解也知道了这三种Workbook的优点和缺点,那么具体使用哪种方式还是需要看情况的:
我一般会根据这样几种情况做分析选择:
  1. 当我们经常导入导出的数据不超过7w的情况下,可以使用 HSSFWorkbook 或者 XSSFWorkbook都行;

  1. 当数据量查过7w并且导出的Excel中不牵扯对Excel的样式,公式,格式等操作的情况下,推荐使用SXSSFWorkbook;

  1. 当数据量查过7w,并且我们需要操做Excel中的表头,样式,公式等,这时候我们可以使用 XSSFWorkbook 配合进行分批查询,分批写入Excel的方式来做;

三、百万数据导入导出

想要解决问题我们首先要明白自己遇到的问题是什么?
​ 1、 我遇到的数据量超级大,使用传统的POI方式来完成导入导出很明显会内存溢出,并且效率会非常低;
​ 2、 数据量大直接使用select * from tableName肯定不行,一下子查出来300w条数据肯定会很慢;
​ 3、 300w 数据导出到Excel时肯定不能都写在一个Sheet中,这样效率会非常低;估计打开都得几分钟;
​ 4、 300w数据导出到Excel中肯定不能一行一行的导出到Excel中。频繁IO操作绝对不行;
​ 5、 导入时300万数据存储到DB如果循环一条条插入也肯定不行;
​ 6、导入时300w数据如果使用Mybatis的批量插入肯定不行,因为Mybatis的批量插入其实就是SQL
的循环;一样很慢。
7、也可采用流式查询
解决思路:
​ 针对1 :
​ 其实问题所在就是内存溢出,我们只要使用对上面介绍的POI方式即可,主要问题就是原生的POI解决起来相当麻烦。
​ 经过查阅资料翻看到阿里的一款POI封装工具EasyExcel,上面问题等到解决;
​ 针对2:
​ 不能一次性查询出全部数据,我们可以分批进行查询,只不过时多查询几次的问题,况且市面上分页插件很多。此问题好解决。
​ 针对3:
​ 可以将300w条数据写到不同的Sheet中,每一个Sheet写一百万即可。
​ 针对4:
​ 不能一行一行的写入到Excel上,我们可以将分批查询的数据分批写入到Excel中。
​ 针对5:
​ 导入到DB时我们可以将Excel中读取的数据存储到集合中,到了一定数量,直接批量插入到DB中。
​ 针对6:
​ 不能使用Mybatis的批量插入,我们可以使用JDBC的批量插入,配合事务来完成批量插入到DB。即 Excel读取分批+JDBC分批插入+事务。

EasyExcelUtil

import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.WriteTable;
import com.alibaba.excel.write.metadata.WriteWorkbook;
import com.hlframe.modules.frame.dao.JdVaccineApplyDao;
import com.hlframe.modules.frame.entity.ExcelConstants;
import com.hlframe.modules.frame.entity.JdVaccineApply;
import com.hlframe.modules.frame.service.JdVaccineApplyService;
import com.hlframe.modules.frame.template.JdVaccineApplyExport;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Slf4j
@Component
@Service
public class EasyExcelUtil {
    @Resource
    private JdVaccineApplyService service;
    @Resource
    private JdVaccineApplyDao dao;
//300w数据的导出解决思路:

/**
1、 首先在查询数据库层面,需要分批进行查询(我使用的是每次查询20w)
2、 每查询一次结束,就使用EasyExcel工具将这些数据写入一次;
3、 当一个Sheet写满了100w条数据,开始将查询的数据写入到另一个Sheet中;
4、 如此循环直到数据全部导出到Excel完毕。
注意:
    1 我们需要计算Sheet个数,以及循环写入次数。特别是最后一个Sheet的写入次数
    (因为你不知道最后一个Sheet选哟写入多少数据,可能是100w,也可能是25w因为我们这里的300w只是模拟数据,
    有可能导出的数据比300w多也可能少)
    2 我们需要计算写入次数,因为我们使用的分页查询,所以需要注意写入的次数。(其实查询数据库多少次就是写入多少次)
*/
    public void dataExport300w(HttpServletResponse response, JdVaccineApply jdVaccineApply) {

        JdVaccineApply jdParam = service.getparameter(jdVaccineApply);
        OutputStream outputStream = null;
        try {
            long startTime = System.currentTimeMillis();
            log.info("导出开始时间:{}", startTime);
            outputStream = response.getOutputStream();
            WriteWorkbook writeWorkbook = new WriteWorkbook();
            writeWorkbook.setOutputStream(outputStream);
            writeWorkbook.setExcelType(ExcelTypeEnum.XLSX);
            ExcelWriter writer = new ExcelWriter(writeWorkbook);
            String fileName = new String(("预约疫苗接种信息").getBytes(), StandardCharsets.UTF_8);

            // TODO WriteTable 标题这块可以作为公共的封装起来:通过反射获取变量上注解等
            WriteTable table = new WriteTable();

            List<List<String>> titles = new ArrayList<List<String>>();
            titles.add(Collections.singletonList("姓名"));
            titles.add(Collections.singletonList("性别"));
            titles.add(Collections.singletonList("身份证"));
            titles.add(Collections.singletonList("出生日期"));
            titles.add(Collections.singletonList("是否中国籍"));
            titles.add(Collections.singletonList("移动电话"));
            titles.add(Collections.singletonList("人群分类"));
            titles.add(Collections.singletonList("现住址编码"));
            titles.add(Collections.singletonList("乡镇(街道)"));
            titles.add(Collections.singletonList("行政村(社区)"));
            titles.add(Collections.singletonList("具体地址门牌"));
            titles.add(Collections.singletonList("在建工作单位/就读学校"));
            titles.add(Collections.singletonList("过敏反应"));
            titles.add(Collections.singletonList("发热"));
            titles.add(Collections.singletonList("注射人免疫球蛋白"));
            titles.add(Collections.singletonList("妊娠期"));
            titles.add(Collections.singletonList("接种状态"));
            titles.add(Collections.singletonList("第一剂次接种医疗机构"));
            titles.add(Collections.singletonList("第一剂次接种日期"));
            titles.add(Collections.singletonList("第一剂次疫苗名称"));
            titles.add(Collections.singletonList("第一剂次疫苗编号"));
            titles.add(Collections.singletonList("第一剂次疫苗生产厂家"));
            titles.add(Collections.singletonList("第二剂次接种医疗机构"));
            titles.add(Collections.singletonList("第二剂次接种日期"));
            titles.add(Collections.singletonList("第二剂次疫苗名称"));
            titles.add(Collections.singletonList("第二剂次疫苗编号"));
            titles.add(Collections.singletonList("第二剂次疫苗生产厂家"));
            titles.add(Collections.singletonList("第三剂次接种医疗机构"));
            titles.add(Collections.singletonList("第三剂次接种日期"));
            titles.add(Collections.singletonList("第三剂次疫苗名称"));
            titles.add(Collections.singletonList("第三剂次疫苗编号"));
            titles.add(Collections.singletonList("第三剂次疫苗生产厂家"));
            titles.add(Collections.singletonList("户籍乡镇"));
            titles.add(Collections.singletonList("户籍地址"));
            table.setHead(titles);


            // 记录总数:实际中需要根据查询条件(过滤数据)进行统计即可,
            // TODO 此处写入限定的条数进行自测
            Integer totalCount = dao.findListExportNum(jdParam);
//            Integer totalCount = 20 * 10000;
            // 每一个Sheet存放100w条数据
            Integer sheetDataRows = ExcelConstants.PER_SHEET_ROW_COUNT;
            // 每次写入的数据量20w
            Integer writeDataRows = ExcelConstants.PER_WRITE_ROW_COUNT;
            // 计算需要的Sheet数量
            int sheetNum = totalCount % sheetDataRows == 0 ? (totalCount / sheetDataRows) : (totalCount / sheetDataRows + 1);
            // 计算一般情况下每一个Sheet需要写入的次数(一般情况不包含最后一个sheet,因为最后一个sheet不确定会写入多少条数据)
            int oneSheetWriteCount = totalCount > sheetDataRows ? sheetDataRows / writeDataRows : totalCount % writeDataRows > 0 ? totalCount / writeDataRows + 1 : totalCount / writeDataRows;
            // 计算最后一个sheet需要写入的次数
            int lastSheetWriteCount = totalCount % sheetDataRows == 0 ? oneSheetWriteCount : (totalCount % sheetDataRows % writeDataRows == 0 ? (totalCount / sheetDataRows / writeDataRows) : (totalCount / sheetDataRows / writeDataRows + 1));

            // 开始分批查询分次写入
            // 注意这次的循环就需要进行嵌套循环了,外层循环是Sheet数目,内层循环是写入次数
            List<JdVaccineApplyExport> dataList = new ArrayList<>();
            for (int i = 0; i < sheetNum; i++) {
                //创建Sheet
                WriteSheet sheet = new WriteSheet();
                sheet.setSheetNo(i);
                sheet.setSheetName(fileName + (i+1));
                // 循环写入次数: j的自增条件是当不是最后一个Sheet的时候写入次数为正常的每个Sheet写入的次数,如果是最后一个就需要使用计算的次数lastSheetWriteCount
                for (int j = 0; j < (i != sheetNum - 1 || i==0 ? oneSheetWriteCount : lastSheetWriteCount); j++) {
                    // 集合复用,便于GC清理
                    dataList.clear();

                    Integer page = j + 1 + oneSheetWriteCount * i;
                            // 业务逻辑
                            //分页查询一次20w
                            dataList = dao.findListExport(jdParam,(page - 1) * writeDataRows,writeDataRows);
                            // 写数据
                            if (dataList.size()>0){
                                writer.write(dataList,sheet,table);
                            }
                }

            }

            // 下载EXCEL 以下代码可以作为公共的进行封装.
            setExcelRespProp(response, fileName);
            writer.finish();
            outputStream.flush();
            // 导出时间结束
            long endTime = System.currentTimeMillis();
            log.info("导出结束时间:{}", endTime + "ms");
            log.info("导出所用时间:{}", (endTime - startTime) / 1000 + "秒");
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 设置excel下载响应头属性
     */
    public static void setExcelRespProp(HttpServletResponse response, String rawFileName){
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码
        String fileName = null;
        try {
            fileName = URLEncoder.encode(rawFileName, "UTF-8").replaceAll("\\+", "%20");
        } catch (UnsupportedEncodingException e) {
            log.error("设置excel下载响应头属性,失败 {}",e.getMessage());
        }
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

    }


}

ExcelConstants 导出分批规则变量定义

/**
 * 导出分批规则变量定义
 */
public class ExcelConstants {
    public static final Integer PER_SHEET_ROW_COUNT = 100*10000;
    public static final Integer PER_WRITE_ROW_COUNT = 26*10000;
    public static final Integer GENERAL_ONCE_SAVE_TO_DB_ROWS_JDBC = 10*10000;
    public static final Integer GENERAL_ONCE_SAVE_TO_DB_ROWS_MYBATIS = 5*10000;
}

控制层接口

    @GetMapping(value = "/downloadVaccineData")
    @ApiOperation(value="导出预约列表信息",notes="导出预约列表信息",response = String.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name="name",value="姓名", dataType="string", required = false, paramType="form"),
            @ApiImplicitParam(name="sex",value="性别(0:保密,1:男,2:女)", dataType="int", required = false, paramType="form"),
            @ApiImplicitParam(name="idCard",value="身份证号", dataType="string", required = false, paramType="form"),
            @ApiImplicitParam(name="birthday",value="出生日期", dataType="long", required = false, paramType="form"),
            @ApiImplicitParam(name="isChina",value="是否中国籍", dataType="int", required = false, paramType="form"),
            @ApiImplicitParam(name="mobile",value="手机号", dataType="string", required = false, paramType="form"),
            @ApiImplicitParam(name="crowdClassify",value="人群分类", dataType="int", required = false, paramType="form"),
            @ApiImplicitParam(name="streetId",value="街道ID", dataType="int", required = false, paramType="form"),
            @ApiImplicitParam(name="villageId",value="村社ID", dataType="int", required = false, paramType="form"),
            @ApiImplicitParam(name="street",value="街道", dataType="string", required = false, paramType="form"),
            @ApiImplicitParam(name="village",value="村社", dataType="string", required = false, paramType="form"),
            @ApiImplicitParam(name="address",value="地址", dataType="string", required = false, paramType="form"),
            @ApiImplicitParam(name="addressCode",value="现住址编码", dataType="string", required = false, paramType="form"),
            @ApiImplicitParam(name="company",value="工作单位/就读学校", dataType="string", required = false, paramType="form"),
            @ApiImplicitParam(name="inoculateHospitalOne",value="第一次接种医院", dataType="int", required = false, paramType="form"),
            @ApiImplicitParam(name="inoculateHospitalTwo",value="第二次接种医院", dataType="int", required = false, paramType="form"),
            @ApiImplicitParam(name="inoculateHospitalThree",value="第三次接种医院", dataType="int", required = false, paramType="form"),
            @ApiImplicitParam(name="inoculateTimeOne",value="第一次接种时间", dataType="int", required = false, paramType="form"),
            @ApiImplicitParam(name="inoculateTimeTwo",value="第二次接种时间", dataType="int", required = false, paramType="form"),
            @ApiImplicitParam(name="inoculateTimeThree",value="第三次接种时间", dataType="int", required = false, paramType="form"),
            @ApiImplicitParam(name="inoculateHospitalOtherOne",value="第一次接种医院(其他)", dataType="string", required = false, paramType="form"),
            @ApiImplicitParam(name="inoculateHospitalOtherTwo",value="第二次接种医院(其他)", dataType="string", required = false, paramType="form"),
            @ApiImplicitParam(name="inoculateHospitalOtherThree",value="第三次接种医院(其他)", dataType="string", required = false, paramType="form"),
            @ApiImplicitParam(name="inoculateIsCompleted",value="是否接种完成", dataType="int", required = false, paramType="form"),
            @ApiImplicitParam(name="inoculateCompletedTime",value="接种完成时间", dataType="int", required = false, paramType="form"),
            @ApiImplicitParam(name="noticeUnInoculateTime",value="已通知(未接种)时间", dataType="int", required = false, paramType="form"),
            @ApiImplicitParam(name="noticePostponeInoculateTime",value="已通知(延期接种)时间", dataType="int", required = false, paramType="form"),
            @ApiImplicitParam(name="status",value="状态", dataType="int", required = false, paramType="form"),
            @ApiImplicitParam(name="createdAt",value="创建时间", dataType="int", required = false, paramType="form"),
            @ApiImplicitParam(name="updatedAt",value="更新时间", dataType="int", required = false, paramType="form"),
            @ApiImplicitParam(name="deletedAt",value="删除时间", dataType="int", required = false, paramType="form")
    })
    public Callable<String> downloadVaccineData(HttpServletResponse response,JdVaccineApply jdVaccineApply){
        log.info("外部线程:" + Thread.currentThread().getName());

        return new Callable<String>() {

            @Override
            public String call() throws Exception {
                try {
                    easyExcelUtil.dataExport300w(response,jdVaccineApply);
                    return  buildResultStr(buildSuccessResultData("导出预约列表信息成功"));
                }catch (Exception e){
                    log.error("导出预约列表信息失败",e);
                    return buildResultStr(buildErrorResultData(e));
                }
            }
        };
    }

导出测试 508598涉及四张表数据导出共耗时91秒

注:这里不建议使用多线程优化for循环中的sql,多线程开发的目的就在于“空间”换“时间”,而我们这里第一要解决的问题就是生产环境OOM问题,因为数据量过大而导致的内存溢出。不能本末倒置,因此要在保证服务器内存空间不溢出的前提下尽量提升程序运行速度。

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

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

相关文章

SAP 系统内核版本详解

前言&#xff1a;之前两篇简单介绍了ABAP 7.40的新特性&#xff0c;那么有的时候会有这样的情况&#xff0c;根据新特性写代码的时候校验会不通过&#xff0c;这又是为啥呢&#xff1f;会不会跟SAP的版本有关系呢&#xff1f; 那么&#xff0c;首先我们就来看一下在SAP系统中如…

基于TC397的Bootloader开发过程中遇到的问题记录

问题11 现象&#xff1a;刷新流程结束之后上位机通过22服务AFFC读取刷新计数时&#xff0c;刷新计数会偶发地置12 分析思路&#xff1a;尝试用单步调试的方法复现该现象&#xff0c;程序中涉及到刷新计数的更新有两处&#xff0c;一是在34服务中擦flash前&#xff0c;二是在31服…

【数据结构】栈及其经典面试题详解

目录前言一、栈的介绍二、数据类型重定义三、栈的结构四、栈中的常见操作五、测试栈六、栈的常见面试题前言 前面学习的线性表中包含顺序表和链表&#xff0c;这两种数据结构允许在任意位置进行插入和删除&#xff0c;那么有没有一种数据结构是不能在任意位置进行插入删除&…

全面解读MinION纳米孔测序技术及应用

全面解读MinION纳米孔测序技术及应用 link&#xff1a;https://www.seqchina.cn/467.html 【测序中国】 paper&#xff1a;The Oxford Nanopore MinION: delivery of nanopore sequencing to the genomics community https://pubmed.ncbi.nlm.nih.gov/27887629/ 纳米孔测序技术…

知识蒸馏 Knowledge distillation(学习笔记)

知识蒸馏概述 蒸馏&#xff1a;把大的 复杂的东西变成小的纯净的东西 在知识蒸馏中 大的模型为 教师模型&#xff08;teacher&#xff09;臃肿 集成 牛逼 复杂的 小的 为 学生模型&#xff08;student&#xff09;小的精干的 轻量化的 这里有一个知识的迁移 因为落地实…

相关性模型与回归模型(例题代码)

一、相关性模型&#xff08;SPSS&#xff09; 相关性模型涉及到两种最为常用的相关系数&#xff1a; 皮尔逊person相关系数斯皮尔曼spearman等级相关系数 1、皮尔逊相关系数 相关性可视化 总结&#xff1a; 1.如果两个变量本身就是线性的关系&#xff0c;那么皮尔逊相关系…

儿子小伟再婚,新儿媳紧锁眉头,农民歌唱家大衣哥有些过分了

虽然都知道大衣哥儿子小伟结婚&#xff0c;这一天早晚都要到来&#xff0c;但是却没有想到来得那么快&#xff0c;大衣哥儿子小伟的婚礼&#xff0c;在悄无声息中结束了。说起大衣哥儿子小伟&#xff0c;这已经不是第一次结婚了&#xff0c;因为结过婚有经验&#xff0c;这一次…

Linux CFS调度器之pick_next_task函数

文章目录前言一、pick_next_task二、pick_next_task_fair参考资料前言 在内核执行__schedule函数&#xff0c;进程任务切换的时候&#xff0c;__schedule函数函数会调用pick_next_task让调度器从就绪队列中选择最合适的一个进程运行&#xff0c;如下所示&#xff1a; static …

Nerdctl 原生支持 Nydus 加速镜像

文&#xff5c;李楠&#xff08;GitHub ID : loheagn&#xff09; 北京航空航天大学 21 级研究生 云原生底层系统的开发和探索工作。 本文 6369 字 阅读 16 分钟 OSPP 开源之夏是由中科院软件研究所“开源软件供应链点亮计划”发起并长期支持的一项暑期开源活动。旨在鼓励在…

关于whl,你想知道的

一、whl是什么&#xff1f;whl文件时以wheel格式保存的python安装包&#xff0c;Wheel是Python发行版的标准内置包格式。WHL文件包含Python安装的所有文件和元数据&#xff0c;其中还包括所使用的Wheel版本和打包的规范。WHL文件使用Zip压缩进行压缩&#xff0c;实际上也是一种…

二、TCO/IP---Ethernet和IP协议

TCP/ip协议栈 OSI模型TCP/IP协议栈应用层&#xff0c;表示层&#xff0c;会话层应用层传输层主机到主机层&#xff08;传输层&#xff09;网络层网络层数据链路层&#xff0c;物理层网络接入层 Ethernet协议 以太网&#xff0c;实现链路层的数据传输和地址封装&#xff08;MA…

【Qt】Qt中的拖放操作实现——拖放文件以及自定义拖放操作

文章目录Qt的拖放操作使用拖放打开文件自定义拖放操作文章参考《Qt Creator快速入门&#xff08;第三版&#xff09;》。 Qt的拖放操作 拖放操作分为拖动Drag和放下Drop&#xff0c;Qt提供了强大的拖放机制&#xff0c;可在帮助文档中通过Drag and Drop关键字查看。 在Qt中&a…

ArcGIS基础实验操作100例--实验78按栅格分区统计路网

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验78 按栅格分区统计路网 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;…

【数据结构】队列详解

前言 前面我们学习了一种数据结构&#xff1a;栈&#xff0c;栈是一种只允许在一端尽进行插入删除的数据结构&#xff0c;而今天我们将学习另一种数据结构&#xff1a;队列&#xff0c;队列是一种支持在一端进行插入&#xff0c;在另一端进行删除的数据结构。 一、队列的介绍…

PHP反序列化字符串逃逸

PHP反序列化字符串逃逸 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录PHP反序列化字符串逃逸前言一、关于反序列化和序列化二、[0ctf 2016]unserialize二、prize_p5[NSSCTF]前言 例如&#xff1a;最近日常刷题玩…

常用的传输码介绍

文章目录前导知识1.AMI码2.HDB3码3.PST码4.数字双相码5.CMI码6.nBmB码前导知识 在介绍常用的传输码之前&#xff0c;先简单介绍一下直流分量。 信号的直流分量就是信号的平均值&#xff0c;它是一个与时间无关的常数&#xff0c;直流分量的数学公式表示为&#xff1a; 判断有…

基于轻量级YOLOv5+Transformer的汽车车损检测识别分析系统

将传统NLP领域提出来的Transformer技术与yolo目标检测模型融合已经成为一种经典的做法&#xff0c;早在之前的很多论文里面就有这种组合应用的出现了&#xff0c;本文主要是借鉴前文的思路&#xff0c;开发基于yolov5transformer的汽车车损检测识别模型&#xff0c;首先看下效果…

光流相关总结

基于图像亮度恒定假设&#xff0c; 图像亮度&#xff1a;I(x⃗,t)I(\vec x, t)I(x,t), 其中x⃗[x,y]\vec x[x,y]x[x,y]&#xff0c;那么亮度恒定假设&#xff1a; I(x⃗,t)I(x⃗δx⃗,tδt)(1)I(\vec x,t)I(\vec x \delta \vec x, t \delta t) (1)I(x,t)I(xδx,tδt)(1) 对上式…

2022年值得记录的一年,事与愿违的一年

年初带着对生活的不满、怀才不遇的傲慢&#xff1b; 愿即将到来的30岁不留遗憾&#xff1b; 你放下所有去追求向往的样子&#xff1b; 那时所有的空气都是清新的&#xff0c;即使它满是灰尘&#xff1b; 不再年少的你依然充满新奇&#xff1b; 用尽力气把自己钉在那个不属…

前端与后端的技术通性

一、后端的JDK相当于前端的Node.js, 后端的JVM相当于前端的V8引擎【作用示例图&#xff0c;如下所示】 【Nodejs、JDK分别是前后端的运行环境】 二、后端的Maven&#xff08;基于项目对象模型-Project Object Model-POM的项目管理机制&#xff09;相当于前端的npm&#xff08;n…