当前位置: 首页 > news >正文

万级数据优化EasyExcel+mybatis流式查询导出封装

文章目录

    • 前言.万级数据优化
    • 一. 直接上流式查询封装工具代码
    • 二. 传统分页导出查询
    • 三. 流式查询概念
    • 游标查询


前言.万级数据优化

我们不妨先给大家讲一个概念,利用此概念我们正好给大家介绍一个数据库优化的小技巧: 需求如下:将一个地市表的数据导出70万条。

在这里插入图片描述

如果你不假思索,直接一条sql语句搞上去,直接就会内存溢出,因为mysql会将结果记录统一查询出来然后返还给内存:那内存可能直接OOM!

@Test
public void testQuery1()  {// 1、定义资源Connection connection = null;ResultSet resultSet = null;PreparedStatement statement = null;String sql = "select * from user";try {// 获取连接connection = DBUtil.getConnection();// 获取使用预编译的statementstatement = connection.prepareStatement(sql);long start = System.currentTimeMillis();resultSet = statement.executeQuery();while (resultSet.next()){System.out.println("name---->" + resultSet.getString("nick_name") );}long end = System.currentTimeMillis();System.out.println(end -start);} catch (SQLException e){e.printStackTrace();} finally {// 关闭资源DBUtil.closeAll(connection,statement,resultSet);}
}

所以我们通常有如下几种解决方案:

一. 直接上流式查询封装工具代码

使用2核4G云服务器 下载速度在40-50s之间波动
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

表格美化 CustomCellWeightStrategy.class
/*** @author YuanJie* @projectName vector-server* @package com.vector.common.utils.easyexcel* @className com.vector.common.utils.easyexcel.CustomCellWeightStrategy* @copyright Copyright 2020 vector, Inc All rights reserved.* @date 2023/8/28 17:58*/
public class CustomCellWeightStrategy extends AbstractColumnWidthStyleStrategy {private final Map<Integer, Map<Integer, Integer>> CACHE = new HashMap<>();@Overrideprotected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);if (needSetWidth) {Map<Integer, Integer> maxColumnWidthMap = CACHE.computeIfAbsent(writeSheetHolder.getSheetNo(), k -> new HashMap<>());int columnWidth = this.dataLength(cellDataList, cell, isHead)+8;if (columnWidth >= 0) {if (columnWidth > 254) {columnWidth = 254;}Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());if (maxColumnWidth == null || columnWidth > maxColumnWidth) {maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);Sheet sheet = writeSheetHolder.getSheet();sheet.setColumnWidth(cell.getColumnIndex(), columnWidth * 200);}//设置单元格类型cell.setCellType(CellType.STRING);// 数据总长度int length = cell.getStringCellValue().length();// 换行数int rows = cell.getStringCellValue().split("\n").length;// 默认一行高为20cell.getRow().setHeightInPoints(rows * 20);}}}/*** 计算长度** @param cellDataList* @param cell* @param isHead* @return*/private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {if (isHead) {return cell.getStringCellValue().getBytes().length;} else {CellData<?> cellData = cellDataList.get(0);CellDataTypeEnum type = cellData.getType();if (type == null) {return -1;} else {switch (type) {case STRING:// 换行符(数据需要提前解析好)int index = cellData.getStringValue().indexOf("\n");return index != -1 ?cellData.getStringValue().substring(0, index).getBytes().length + 1 : cellData.getStringValue().getBytes().length + 1;case BOOLEAN:return cellData.getBooleanValue().toString().getBytes().length;case NUMBER:return cellData.getNumberValue().toString().getBytes().length;default:return -1;}}}}
}
封装工具类EasyExcelUtil.class
/*** @author YuanJie* @projectName vector-server* @package com.vector.common.utils* @className com.vector.common.utils.easyexcel.EasyExcelUtil* @copyright Copyright 2020 vector, Inc All rights reserved.* @date 2023/8/26 1:17*/
@Slf4j
public class EasyExcelUtil {private static final String DATE_FORMAT = "yyyy-MM-dd";/*** 设置批量存储最大值,也影响sheet页数*/private static final Integer MAX_SHEET_DATA = 50000;/*** 设置内存最大值*/private static final Integer MAX_MEMORY_DATA = 1000;/*** 使用EasyExcel生成Excel  xls** @param response      响应对象* @param fileNameParam 文件名* @param sheetName     表格名* @param clazz         导出实体类* @param t             查库入参* @param func          流式查询方法 Cursor<ResultVo> listOrders(@Param("userName") String userName);*/public static <T> void writeExcelXls(HttpServletResponse response, String fileNameParam,String sheetName, Class<?> clazz, T t,Function<T, Cursor<?>> func) throws Exception {streamExportExcel(response, fileNameParam, sheetName, clazz, ExcelTypeEnum.XLS.getValue(), t, func);}/*** 使用EasyExcel生成Excel  xlsx** @param response      响应对象* @param fileNameParam 文件名* @param sheetName     表格名* @param clazz         导出实体类* @param t             查库入参* @param func          流式查询方法 Cursor<ResultVo> listOrders(@Param("userName") String userName);*/public static <T> void writeExcelXlsx(HttpServletResponse response, String fileNameParam,String sheetName, Class<?> clazz, T t,Function<T, Cursor<?>> func) throws Exception {streamExportExcel(response, fileNameParam, sheetName, clazz, ExcelTypeEnum.XLSX.getValue(), t, func);}/*** 流式导出 Excel** @param response      响应对象* @param fileNameParam 文件名* @param sheetName     表格名* @param clazz         导出实体类* @param excelType     导出类型* @param t             查库入参* @param func          流式查询方法 Cursor<ResultVo> listOrders(@Param("userName") String userName);* @throws Exception 异常*/private static <T> void streamExportExcel(HttpServletResponse response, String fileNameParam,String sheetName, Class<?> clazz, String excelType,T t, Function<T, Cursor<?>> func) throws Exception {DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATE_FORMAT);String fileName = fileNameParam + dateTimeFormatter.format(LocalDateTime.now()) + excelType;ExcelWriter excelWriter = EasyExcel.write(getOutputStream(fileName, response, excelType), clazz).registerWriteHandler(new CustomCellWeightStrategy()).build();// 内容样式WriteCellStyle contentWriteCellStyle = new WriteCellStyle();// 水平居中contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);// 垂直居中contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);// 设置自动换行,前提内容中需要加「\n」才有效contentWriteCellStyle.setWrapped(true);// 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现HorizontalCellStyleStrategy horizontalCellStyleStrategy =new HorizontalCellStyleStrategy(null, contentWriteCellStyle);Cursor<?> cursor;List<Object> list = new ArrayList<>();int page = 0;WriteSheet writeSheet = EasyExcel.writerSheet(++page, sheetName + page).registerWriteHandler(horizontalCellStyleStrategy).build();// 流式数据库查询cursor = func.apply(t);int count = 0;try {for (Object o : cursor) {list.add(o);if(list.size() == MAX_MEMORY_DATA){count += list.size();excelWriter.write(list, writeSheet);list.clear();// 每个sheet页最大存储MAX_SHEET_DATA条数据if (count >= MAX_SHEET_DATA) {writeSheet = EasyExcel.writerSheet(++page, sheetName + page).registerWriteHandler(horizontalCellStyleStrategy).build();}}}// 处理最后不足MAX_SHEET_DATA的数据if (list.size() > 0) {writeSheet = EasyExcel.writerSheet(++page, sheetName + page).registerWriteHandler(horizontalCellStyleStrategy).build();excelWriter.write(list, writeSheet);list.clear();}} catch (Exception e) {// 重置responseresponse.reset();response.setContentType("application/json");response.setCharacterEncoding("utf-8");String json = JacksonInstance.toJson(R.errorResult(EnumHttpCode.SYSTEM_ERROR, "下载文件失败" + e.getMessage()));response.getWriter().println(json);} finally {if (cursor != null) {cursor.close();}if (excelWriter != null) {excelWriter.finish();}}}/*** 导出文件时为Writer生成OutputStream** @param finalName 文件名* @param response 响应对象* @param excelType 导出文件类型* @return OutputStream*/private static OutputStream getOutputStream(String finalName, HttpServletResponse response, String excelType) throws Exception {response.reset();finalName = URLEncoder.encode(finalName, StandardCharsets.UTF_8);if (ExcelTypeEnum.XLS.getValue().equals(excelType)) {response.setContentType("application/vnd.ms-excel");} else if (ExcelTypeEnum.XLSX.getValue().equals(excelType)) {response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");}response.setCharacterEncoding("utf8");response.setHeader("Content-Disposition", "attachment; filename=" + finalName);response.setHeader("Pragma", "public");response.setHeader("Cache-Control", "no-store");response.addHeader("Cache-Control", "max-age=0");return response.getOutputStream();}
}
导出实体 Dto.class
@Data
public class Dto {@NumberFormat("#")@ExcelProperty(value = "地市编码", index = 0)Long code;@ExcelProperty(value = "地市名称", index = 1)String name;@NumberFormat("#")@ExcelProperty(value = "地市级别", index = 2)Integer level;@NumberFormat("#")@ExcelProperty(value = "地市父编码", index = 3)Long pcode;@NumberFormat("#")@ExcelProperty(value = "地市父名称", index = 4)Integer category;
}
测试用例 MeTestController.class
    @Resourceprivate ExportMapper exportMapper;@Resourceprivate HttpServletRequest request;@Resourceprivate HttpServletResponse response;@GetMapping("/export")@Transactionalpublic void export() throws Exception {Long params = 110101001000L;EasyExcelUtil.writeExcelXlsx(response,"地市信息","地市区域",Dto.class,params,param -> exportMapper.export(null));}
测试Dao ExportMapper.class
public interface ExportMapper {@Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE)@ResultType(Dto.class)@Select("select * from area_code_2023")Cursor<Dto> export(@Param("params") Long params);
}

二. 传统分页导出查询

大表的深度分页性能很差,也受制于表设计的影响
@Test
public void testQuery2()  {// 1、定义资源Connection connection = null;ResultSet resultSet = null;PreparedStatement statement = null;String sql = "select * from user limit ?,?";try {// 获取连接connection = DBUtil.getConnection();// 获取使用预编译的statementstatement = connection.prepareStatement(sql);// 获取结果集long start = System.currentTimeMillis();long begin = 0L, offset = 10000L;while (true){statement.setLong(1,begin);statement.setLong(2,offset);begin += offset;resultSet = statement.executeQuery();boolean flag = resultSet.next();if(!flag) break;while (flag){System.out.println("name---->" + resultSet.getString("nick_name") );flag = resultSet.next();}}long end = System.currentTimeMillis();System.out.println(end -start);} catch (SQLException e){e.printStackTrace();} finally {// 关闭资源DBUtil.closeAll(connection,statement,resultSet);}
}

三. 流式查询概念

采用传统的Stream流式思想,将直接提供数据替换成提供获取数据的管道,客户端读取数据时直接从管道中遍历获取;整个读取的过程需要客户端保持和服务端的连接,也很好理解,它实际是一个管道,管道得通着才能取数据。

采用传统的Stream流式思想,将直接提供数据替换成提供获取数据的管道,客户端读取数据时直接从管道中遍历获取;整个读取的过程需要客户端保持和服务端的连接,也很好理解,它实际是一个管道,管道得通着才能取数据。

流式查询有两种使用方式,一种是用Cursor作为返回值,对数据进行遍历操作;一种是不设置返回值,在入参中传入一个ResultHandler作为回调处理数据。本文将基于Mybatis具体介绍使用方法。 这两种返回值的使用方式是相似的,唯一区别就是返回值不同。Mybatis查询有两种方式,一种是基于注解加在Mapper接口上方,一种是写在xml文件中,主要需要设置以下几个属性:
ResultSetType结果集读取方式
FetchSizeMySQL服务端单次发送至客户端的数据条数
ResultType这个眼熟吧,设置返回实体类映射

ResultSetType有4种可选项

DEFAULT(-1),
FORWARD_ONLY(1003),
SCROLL_INSENSITIVE(1004),
SCROLL_SENSITIVE(1005);

FORWARD_ONLY顾名思义只能向前,即数据只能向前读取,是不是就类似一个流水的管道,读一条就相当于水流过去一些。也是我们需要选用的。
SCROLL_INSENSITIVE不敏感滚动,和下面那个差不多,都是可以向后读或向前读;这意味着已读取过的数据不能丢掉,要继续保存在内存中,因为有可能会回去再次读取他们。
SCROLL_SENSITIVE敏感滚动,和上面那个差不多。
这么一比较就看得出来,当选的一定是FORWARD_ONLY,我们亟需解决的就是大数据量对内存的影响,再用后面两个还是会放在内存中。

FetchSize这个概念在许多服务中都有提及,例如RabbitMQ中是消费者取过来预处理的消息数量,但在MySQL中完全不是一个概念。MySQL的数据传输是基于C/S的阻塞机制,即Client设置FetchSize = 1000,而Server查出来10000条数据,按照常理应该是Server智能地使用分页策略1000条1000条取;实际不是,Server查出来多少就是多少,他会放在自己特定的内存空间内,只是会根据FetchSize的大小一点一点传送给Client——利用C/S的通讯阻塞,发1000条、堵一下、发1000条、堵一下……。

JDBC官方给出的答案是设置为“Integer.MIN_VALUE”,具体原因不清楚,但我猜是为了和游标查询区分开,因为一会你会发现流式查询和游标查询唯一的区别就是FetchSize的大小。

注解式

@Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE)
@ResultType(ResultVo.class)
@Select("SELECT *, 0 orderType FROM `table`\n" +"        WHERE username = #{userName}")
Cursor<ResultVo> listOrders(@Param("userName") String userName);@Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE)
@ResultType(ResultVo.class)
@Select("SELECT *, 0 orderType FROM `table`\n" +"        WHERE username = #{userName}")
void listOrders2(@Param("userName") String userName, ResultHandler<ResultVo> handler);

使用Mybatis的注解,在 @Options 中指定查询配置参数,在@ResultType中指定返回值类型 ,在 @Select中指定查询语句。最后用Cursor接收返回值,Cursor是可遍历的,所以直接Foreach遍历即可;或者返回void 用ResultHandler处理数据回调,在调用方式时传入new ResultHandler并写明处理逻辑。

xml式

<select id="listOrders" resultType="com.vo.ResultVo" resultSetType="FORWARD_ONLY" fetchSize="Integer.MIN_VALUE">SELECT *, 1 stuffCount, 1 orderType FROM `table`WHERE username = #{userName}
</select>

需要注意的是,不可以注解 + xml混合使用,比如注解指定fetchSize,xml只写查询语句,这种只有xml语句会生效!!!要不全用注解,要不全用xml!!!

流式查询由于需要保持客户端与服务端的连接,而一般查询提交完连接就会关闭;因此我们需要保持事务开启,否则会报“A Cursor is already closed.”,即Cursor已经关闭,没法再读取了。最简单的方法就是在方法上加@Transactional,在查询完毕以前事务会一直持有这个数据库连接,但我们在使用完毕后也要自行关闭连接,显式调用Cursor.close(),或者用try with resource语句。

游标查询和流式查询的区分是fetchSize = Integer.MIN_VALUE

Cursor 还提供了三个方法:

  1. isOpen():用于在取数据之前判断 Cursor 对象是否是打开状态。只有当打开时 Cursor 才能取数据;
  2. isConsumed():用于判断查询结果是否全部取完;
  3. getCurrentIndex():返回已经获取了多少条数据。
try(Cursor cursor = OrderMapper.listOrders()) {cursor.forEach(rowObject -> {// ...});
}OrderMapper.listOrders2(queryWrapper,resultContext -> {ResultVo result = resultContext.getResultObject();//这边循环调用就可以实现业务了}

游标查询

和流式查询类似fetchSize不设置为MIN_VALUE即可
JDBC查询默认是不支持FetchSize属性的,需要在JDBC连接URL后面加上**“useCursorFetch=true”。**

useCursorFetch=true 是针对 MySQL 数据库的 JDBC 连接参数,用于启用服务器端游标获取数据。在 MyBatis 中,当使用流式查询(例如:分页查询、结果集处理和使用游标等)时,这个配置可以帮助逐行从服务器检索数据,而不是一次性将所有数据加载到内存中,从而降低内存占用。

当使用 MySQL 数据库时,在 JDBC 连接字符串中加入 useCursorFetch=true,并结合设置合适的 fetchSize,可以避免因一次性加载过多数据导致的内存溢出问题。注意,此配置仅对 MySQL 数据库有效。 如果不设置 useCursorFetch=true 这个配置,仅使用之前提到的那些配置(如设置 defaultFetchSize、分页查询、结果集处理和使用游标等),在大多数情况下,这些配置仍然可以有效地避免查询导致的内存溢出。

但需要注意的是,对于 MySQL 数据库,如果不启用服务器端游标获取数据,这可能会影响到流式查询的效果。因为在默认情况下,MySQL JDBC 驱动会一次性将所有数据加载到内存中。此时,即使使用了其他配置,也可能无法达到预期的内存优化效果。

总的来说,在使用 MySQL 数据库时,推荐在 JDBC 连接字符串中加入 useCursorFetch=true 配置,以更好地支持流式查询和降低内存占用。在其他数据库中,可以根据实际需求和场景选择合适的配置和策略来避免查询导致的内存溢出。

还要知道如何判断自己是否使用了流式查询或游标查询,下面是几个数据集的对应关系

普通分页ResultsetRowsStaticRowDataStatic
查询方式结果集类型行数据类型
流式查询ResultsetRowsStreamingRowDataDynamic
游标查询ResultsetRowsCursorRowDataCursor

这3种查询方式,常规非大数据模式下普通查询最快,其次是流式查询,最次是游标查询.

主要是由于游标查询需要和数据库进行多次网络交互,Client处理完这部分后再拉取下一部分数据,因此会比较慢。但是流式查询又会长时间占用同一个数据库连接,因此要取舍一下是能接受连接一直持有但是可能会堵住导致响应慢,还是可能占用较多连接数但单次响应快。当通过流式查询获取一个ResultSet后,在你通过next迭代出所有元素之前或者调用close关闭它之前,你不能使用同一个数据库连接去发起另外一个查询,否者抛出异常(第一次调用的正常,第二次的抛出异常)。

相关文章:

万级数据优化EasyExcel+mybatis流式查询导出封装

文章目录 前言.万级数据优化一. 直接上流式查询封装工具代码二. 传统分页导出查询三. 流式查询概念游标查询 前言.万级数据优化 我们不妨先给大家讲一个概念&#xff0c;利用此概念我们正好给大家介绍一个数据库优化的小技巧&#xff1a; 需求如下&#xff1a;将一个地市表的数…...

Unity——脚本序列化

在介绍序列化之前&#xff0c;我们先来了解一下为什么要对数据进行序列化 数据序列化有以下几个主要的应用场景和目的&#xff1a; 1. 持久化存储&#xff1a;序列化可以将对象或数据结构转换为字节序列&#xff0c;使得其可以被存储在磁盘上或数据库中。通过序列化&#xff…...

es(Elasticsearch)介绍

学习es可以参考mysql&#xff08;相比mysql而言&#xff0c;es所需的cpu、内存更多&#xff09; 什么是Elasticsearch Elasticsearch简称es&#xff0c;是由Elastic和search组成。Elastic的意思是有弹性的&#xff0c;search的意思是搜索。 弹性&#xff1a;es是一个天生支持分…...

C++中使用 do…while 循环

C中使用 do…while 循环 在有些情况&#xff08;如程序清单 6.8 所示的情况&#xff09;下&#xff0c;您需要将代码放在循环中&#xff0c;并确保它们至少执行一次。此时 do…while 循环可派上用场。 do…while 循环的语法如下&#xff1a; do {StatementBlock; // executed…...

开源vue动态表单组件

一、项目简介 vueelement的动态表单组件&#xff0c;拖拽组件到面板即可实现一个表单 二、实现功能 支持拖拽 支持输入框 支持文本框 支持数字输入框 支持下拉选择器 支持多选框 支持日期控件 支持开关 支持动态表格 支持上传图片 支持上传文件 支持标签 支持ht…...

怎么从0到1创建一个PHP框架-1?

写在前面 本人开发的框架在2021年年初开发完成&#xff0c;后面没有再做过任何维护和修改。是仅供大家参考交流的学习项目&#xff0c;请勿使用在生产环境&#xff0c;也勿用作商业用途。 框架地址&#xff1a; https://github.com/yijiebaiyi/fast_framework 整体思路 开发…...

Qt无边框青绿色主题

收费产品&#xff0c;学生党、闹眼子党勿扰 收费金额&#xff1a;500元 1 概述 最近因项目需要&#xff0c;写了一个炫酷的青绿色、无边框界面&#xff0c;和3DSMax的界面有点类似。 2 截图 首先看看3DSMax的界面 不知道大家看出来没&#xff0c;这个ui其实很简单&#xff…...

200 套基于Java开发的Java毕业设计实战项目(含源码+说明文档)

文章目录 简介前言第一部分第二部分部分截图源码咨询 简介 博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 前言 对于java方向的毕业设计题目选题&#xf…...

Ansible学习笔记7

user模块&#xff1a; user模块用于管理用户账户和用户属性。 如果是windows要换一个win_user模块。 创建用户&#xff1a;present&#xff1a; [rootlocalhost ~]# ansible group1 -m user -a "nameaaa statepresent" 192.168.17.106 | CHANGED > {"ansi…...

Python3 对列表、字典以及二者的嵌套数据(JSON)格式排序

在 Python 中&#xff0c;列表和字典都是基础数据类型&#xff0c;这两种数据类型会通过相互嵌套和多个层级形成复杂的数据类型&#xff0c;类似 JSON 数据格式&#xff0c;对列表和字典排序其实可以类比是对 JSON 排序。 列表排序 列表可以使用 sorted() 函数排序&#xff1…...

如何在B站进行学习直播

诸神缄默不语-个人CSDN博文目录 会根据我使用的情况进行持续更新 文章目录 1. 电脑 - 哔哩哔哩直播姬1. 软件的基础使用2. 素材1. 摄像头2. 窗口捕捉3. 游戏进程图片文字浏览器多媒体 3. H5插件其他注意事项 2. 手机直播3. iPad直播 1. 电脑 - 哔哩哔哩直播姬 1. 软件的基础使…...

老卫带你学---windows上安装minikube

老卫带你学—windows上安装minikube 1. 下载minikube https://storage.googleapis.com/minikube/releases/latest/minikube-installer.exe2.安装好后&#xff0c;将对应的目录添加env path 3. minikube start --kubernetes-versionv1.23.8 --image-mirror-countrycn...

Neo-reGeorg隧道搭建

目录 Neo-regeorg前言 环境搭建 具体使用 kail安装Neo-reGeorg kail内生成webshell并设置密码 kail与win10连接 windows server内打开服务 kail虚拟机访问windows server以及所在的内网 Neo-regeorg前言 regeorg为reDuh的升级版&#xff0c;主要功能就是把内网服务器的…...

Elasticsearch 7.6 - API高阶操作篇

ES 7.6 - API高阶操作篇 分片和副本索引别名添加别名查询所有别名删除别名使用别名代替索引操作代替插入代替查询 场景实操 滚动索引索引模板创建索引模板查看模板删除模板 场景实操一把索引的生命周期数据迁移APIGEO(地理)API索引准备矩形查询圆形查询多边形查询 自定义分词器…...

软件第三方验收测评介绍

软件第三方验收测试 软件项目验收测试介绍&#xff1a; 软件项目验收测试是部署软件之前的最后一个测试操作&#xff0c;是对系统进行全面的测试&#xff0c;以验证其是否符合合同要求&#xff0c;出具第三方测试报告&#xff0c;为系统验收提供依据。 验收测试的目的是&…...

HarmonyOS—使用Web组件加载页面

页面加载是 Web 组件的基本功能。根据页面加载数据来源可以分为三种常用场景&#xff0c;包括加载网络页面、加载本地页面、加载 HTML 格式的富文本数据。 页面加载过程中&#xff0c;若涉及网络资源获取&#xff0c;需要配置ohos.permission.INTERNET网络访问权限。 加载网络…...

Redis 缓存穿透、击穿、雪崩

一、缓存穿透 1、含义 缓存穿透是指查询一个缓存中和数据库中都不存在的数据&#xff0c;导致每次查询这条数据都会透过缓存&#xff0c;直接查库&#xff0c;最后返回空。 2、解决方案 1&#xff09;缓存空对象 就是当数据库中查不到数据的时候&#xff0c;我缓存一个空对象…...

设计模式-原型模式详解

文章目录 前言理论基础1. 原型模式定义2. 原型模式角色3. 原型模式工作过程4. 原型模式的优缺点 实战应用1. 原型模式适用场景2. 原型模式实现步骤3. 原型模式与单例模式的区别 原型模式的变体1. 带有原型管理器的原型模式2. 懒汉式单例模式的原型模式实现3. 细粒度原型模式 总…...

大语言模型之七- Llama-2单GPU微调SFT

&#xff08;T4 16G&#xff09;模型预训练colab脚本在github主页面。详见Finetuning_LLama_2_0_on_Colab_with_1_GPU.ipynb 在上一篇博客提到两种改进预训练模型性能的方法Retrieval-Augmented Generation (RAG) 或者 finetuning。本篇博客过一下模型微调。 微调&#xff1a…...

房地产行业专题报告:日本房地产市场借鉴

目录 1. 日本房地产泡沫的形成与崩溃 1.1 背景:实际需求减弱、宽松货币和弱金融监管推动泡沫形成 1.1.1 宏观环境:日本 80 年代起生育率降低,房地产基本面支撑力不足 1.1.2 货币政策:宽松货币政策叠加金融自由化促进泡沫生成 1.1.3 助推因素:企业积极参与土地投机、股…...

Educational Codeforces Round 154 (Rated for Div. 2)

Educational Codeforces Round 154 (Rated for Div. 2) A. Prime Deletion 思路&#xff1a; 因为1到9每个数字都有&#xff0c;所以随便判断也质素即可 代码 #include<bits/stdc.h> using namespace std; #define int long long #define rep(i,a,n) for(int ia;i<…...

elasticsearch批量删除(查询删除)

注:delete by query只适用于低于elasticsearch2.0的版本(不包含2.0)。有两种形式: 1.无请求体 curl -XDELETE localhost:9200/twitter/tweet/_query?quser:kimchy 2.有请求体 使用请求体的时候&#xff0c;请求体中只能使用query查询&#xff0c;不能使用filter curl -XD…...

容器技术Linux Namespaces和Cgroups

对操作系统了解多少&#xff0c;仅仅敲个命令吗 操作系统虚拟化&#xff08;容器技术&#xff09;的发展历程 1979 年&#xff0c;UNIX 的第 7 个版本引入了 Chroot 特性。Chroot 现在被认为是第一个操作系统虚拟化&#xff08;Operating system level virtualization&#x…...

GO语言圣经 第四章习题

练习4.1 编写一个函数&#xff0c;计算两个SHA256哈希码中不同bit的数目。&#xff08;参考2.6.2节的PopCount函数。) func PopCount(ptr *[32]byte) int {var res intfor i : 0; i < 32; i {x : int(ptr[i])for x ! 0 {res x & 1x >> 1}}return res }练习4.2 编…...

远程连接Ubuntu 22.04

远程连接Ubuntu 22.04 安装openssh-server sudo apt install openssh-server检查服务运行状态 systemctl status sshd重启服务状态 sudo systemctl restart ssh开启防火墙 sudo ufw enable开启ssh传输端口 sudo ufw allow ssh设置开机启动服务 sudo systemctl enable ssh配置服…...

字节前端实习的两道算法题,看看强度如何

最长严格递增子序列 题目描述 给你一个整数数组nums&#xff0c;找到其中最长严格递增子序列的长度。 子序列是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组 [0,3,1,6,2,2,7…...

设计模式—策略模式

目录 一、定义 二、特点 三、优点 四、缺点 五、实例 六.涉及到的知识点 1、一个类里面有哪些东西&#xff1f; 2、类和实例 什么是类&#xff1f; 什么是实例&#xff1f; 什么是实例化&#xff1f; 3、字段和属性 什么是字段&#xff1f; 属性是什么&#xff1…...

LPDDR4、DDR4

核心信息&#xff1a; 2400Mbps&#xff08;每秒传输2400*1百万bit&#xff09; 2400MT/s&#xff08;百万次/秒&#xff09; 信号...

ESP32C3 LuatOS RC522①写入数据并读取M1卡

LuatOS RC522官方示例 官方示例没有针对具体开发板&#xff0c;现以ESP32C3开发板为例。 选用的RC522模块 ESP32C3-CORE开发板 注意ESP32C3的 SPI引脚位置&#xff0c;SPI的id2 示例代码 -- LuaTools需要PROJECT和VERSION这两个信息 PROJECT "helloworld" VERSIO…...

MusicBrainz Picard for Mac :音乐文件ID3编辑器

MusicBrainz Picard for Mac是一款macOS平台的音乐文件ID3编辑器&#xff0c;能够帮助我们在Mac电脑上编辑音乐文件的ID3标签信息&#xff0c;包括艺人、专辑等信息&#xff0c;非常快速和简单方便。Picard是下一代MusicBrainz标记应用程序。 这个新的标签概念是面向专辑的&…...