国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

萬級數(shù)據(jù)優(yōu)化EasyExcel+mybatis流式查詢導(dǎo)出封裝

這篇具有很好參考價值的文章主要介紹了萬級數(shù)據(jù)優(yōu)化EasyExcel+mybatis流式查詢導(dǎo)出封裝。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。


時間 更新內(nèi)容
2023/09/23 fix: 每個sheet大小和存儲內(nèi)存條數(shù)一致的bug update: 增大一個sheet的默認(rèn)容量

前言.萬級數(shù)據(jù)優(yōu)化

我們不妨先給大家講一個概念,利用此概念我們正好給大家介紹一個數(shù)據(jù)庫優(yōu)化的小技巧: 需求如下:將一個地市表的數(shù)據(jù)導(dǎo)出70萬條。

萬級數(shù)據(jù)優(yōu)化EasyExcel+mybatis流式查詢導(dǎo)出封裝,mybatis,android

如果你不假思索,直接一條sql語句搞上去,直接就會內(nèi)存溢出,因為mysql會將結(jié)果記錄統(tǒng)一查詢出來然后返還給內(nèi)存:那內(nèi)存可能直接OOM!

@Test
public void testQuery1()  {

    // 1、定義資源
    Connection connection = null;
    ResultSet resultSet = null;
    PreparedStatement statement = null;
    String sql = "select * from user";
    try {
        // 獲取連接
        connection = DBUtil.getConnection();
        // 獲取使用預(yù)編譯的statement
        statement = 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 {
        // 關(guān)閉資源
        DBUtil.closeAll(connection,statement,resultSet);
    }
}

所以我們通常有如下幾種解決方案:

一. 直接上流式查詢封裝工具代碼

使用2核4G云服務(wù)器 下載速度在40-50s之間波動. 本機(jī)或大水管.我只能說更快了
萬級數(shù)據(jù)優(yōu)化EasyExcel+mybatis流式查詢導(dǎo)出封裝,mybatis,android

萬級數(shù)據(jù)優(yōu)化EasyExcel+mybatis流式查詢導(dǎo)出封裝,mybatis,android
萬級數(shù)據(jù)優(yōu)化EasyExcel+mybatis流式查詢導(dǎo)出封裝,mybatis,android
萬級數(shù)據(jù)優(yōu)化EasyExcel+mybatis流式查詢導(dǎo)出封裝,mybatis,android

表格美化 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<>();

    @Override
    protected 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);
                }

                //設(shè)置單元格類型
                cell.setCellType(CellType.STRING);
                // 數(shù)據(jù)總長度
                int length = cell.getStringCellValue().length();
                // 換行數(shù)
                int rows = cell.getStringCellValue().split("\n").length;
                // 默認(rèn)一行高為20
                cell.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:
                        // 換行符(數(shù)據(jù)需要提前解析好)
                        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";


    /**
     * 設(shè)置批量存儲最大值,也影響sheet頁數(shù)
     */
    private static final Integer MAX_SHEET_DATA = 100000;
    /**
     * 設(shè)置內(nèi)存最大值
     */
    private static final Integer MAX_MEMORY_DATA = 1000;

    /**
     * 使用EasyExcel生成Excel  xls
     *
     * @param response      響應(yīng)對象
     * @param fileNameParam 文件名
     * @param sheetName     表格名
     * @param clazz         導(dǎo)出實體類
     * @param t             查庫入?yún)?     * @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      響應(yīng)對象
     * @param fileNameParam 文件名
     * @param sheetName     表格名
     * @param clazz         導(dǎo)出實體類
     * @param t             查庫入?yún)?     * @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);
    }

    /**
     * 流式導(dǎo)出 Excel
     *
     * @param response      響應(yīng)對象
     * @param fileNameParam 文件名
     * @param sheetName     表格名
     * @param clazz         導(dǎo)出實體類
     * @param excelType     導(dǎo)出類型
     * @param t             查庫入?yún)?     * @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();

        // 內(nèi)容樣式
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        // 水平居中
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 垂直居中
        contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        // 設(shè)置自動換行,前提內(nèi)容中需要加「\n」才有效
        contentWriteCellStyle.setWrapped(true);
        // 這個策略是 頭是頭的樣式 內(nèi)容是內(nèi)容的樣式 其他的策略可以自己實現(xiàn)
        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();
        // 流式數(shù)據(jù)庫查詢
        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條數(shù)據(jù)
                    if (count >= MAX_SHEET_DATA) {
                        writeSheet = EasyExcel
                                .writerSheet(++page, sheetName + page)
                                .registerWriteHandler(horizontalCellStyleStrategy)
                                .build();
                        count = 0;
                    }
                }
            }
            // 處理最后不足MAX_SHEET_DATA的數(shù)據(jù)
            if (list.size() > 0) {
                writeSheet = EasyExcel
                        .writerSheet(++page, sheetName + page)
                        .registerWriteHandler(horizontalCellStyleStrategy)
                        .build();
                excelWriter.write(list, writeSheet);
                list.clear();
            }
        } catch (Exception e) {
            // 重置response
            response.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();
            }
        }
    }

    /**
     * 導(dǎo)出文件時為Writer生成OutputStream
     *
     * @param finalName 文件名
     * @param response 響應(yīng)對象
     * @param excelType 導(dǎo)出文件類型
     * @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();
    }
}
導(dǎo)出實體 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
    @Resource
    private ExportMapper exportMapper;

    @Resource
    private HttpServletRequest request;
    @Resource
    private HttpServletResponse response;
    @GetMapping("/export")
    @Transactional
    public void export() throws Exception {
        Long params = 110101001000L;
        EasyExcelUtil.writeExcelXlsx(
                response,
                "地市信息",
                "地市區(qū)域",
                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);
}


二. 傳統(tǒng)分頁導(dǎo)出查詢

大表的深度分頁性能很差,也受制于表設(shè)計的影響
@Test
public void testQuery2()  {

    // 1、定義資源
    Connection connection = null;
    ResultSet resultSet = null;
    PreparedStatement statement = null;
    String sql = "select * from user limit ?,?";
    try {
        // 獲取連接
        connection = DBUtil.getConnection();
        // 獲取使用預(yù)編譯的statement
        statement = connection.prepareStatement(sql);
        // 獲取結(jié)果集
        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 {
        // 關(guān)閉資源
        DBUtil.closeAll(connection,statement,resultSet);
    }
}

三. 流式查詢概念

采用傳統(tǒng)的Stream流式思想,將直接提供數(shù)據(jù)替換成提供獲取數(shù)據(jù)的管道,客戶端讀取數(shù)據(jù)時直接從管道中遍歷獲??;整個讀取的過程需要客戶端保持和服務(wù)端的連接,也很好理解,它實際是一個管道,管道得通著才能取數(shù)據(jù)。 流式查詢有兩種使用方式,一種是用Cursor作為返回值,對數(shù)據(jù)進(jìn)行遍歷操作;一種是不設(shè)置返回值,在入?yún)⒅袀魅胍粋€ResultHandler作為回調(diào)處理數(shù)據(jù)。本文將基于Mybatis具體介紹使用方法。 這兩種返回值的使用方式是相似的,唯一區(qū)別就是返回值不同。Mybatis查詢有兩種方式,一種是基于注解加在Mapper接口上方,一種是寫在xml文件中,主要需要設(shè)置以下幾個屬性:
ResultSetType 結(jié)果集讀取方式
FetchSize MySQL服務(wù)端單次發(fā)送至客戶端的數(shù)據(jù)條數(shù)
ResultType 這個眼熟吧,設(shè)置返回實體類映射

ResultSetType有4種可選項

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

FORWARD_ONLY顧名思義只能向前,即數(shù)據(jù)只能向前讀取,是不是就類似一個流水的管道,讀一條就相當(dāng)于水流過去一些。也是我們需要選用的。
SCROLL_INSENSITIVE,不敏感滾動,和下面那個差不多,都是可以向后讀或向前讀;這意味著已讀取過的數(shù)據(jù)不能丟掉,要繼續(xù)保存在內(nèi)存中,因為有可能會回去再次讀取他們。
SCROLL_SENSITIVE,敏感滾動,和上面那個差不多。
這么一比較就看得出來,當(dāng)選的一定是FORWARD_ONLY,我們亟需解決的就是大數(shù)據(jù)量對內(nèi)存的影響,再用后面兩個還是會放在內(nèi)存中。

FetchSize這個概念在許多服務(wù)中都有提及,例如RabbitMQ中是消費(fèi)者取過來預(yù)處理的消息數(shù)量,但在MySQL中完全不是一個概念。MySQL的數(shù)據(jù)傳輸是基于C/S的阻塞機(jī)制,即Client設(shè)置FetchSize = 1000,而Server查出來10000條數(shù)據(jù),按照常理應(yīng)該是Server智能地使用分頁策略1000條1000條取;實際不是,Server查出來多少就是多少,他會放在自己特定的內(nèi)存空間內(nèi),只是會根據(jù)FetchSize的大小一點(diǎn)一點(diǎn)傳送給Client——利用C/S的通訊阻塞,發(fā)1000條、堵一下、發(fā)1000條、堵一下……。

JDBC官方給出的答案是設(shè)置為“Integer.MIN_VALUE”,具體原因不清楚,但我猜是為了和游標(biāo)查詢區(qū)分開,因為一會你會發(fā)現(xiàn)流式查詢和游標(biāo)查詢唯一的區(qū)別就是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 中指定查詢配置參數(shù),在@ResultType中指定返回值類型 ,在 @Select中指定查詢語句。最后用Cursor接收返回值,Cursor是可遍歷的,所以直接Foreach遍歷即可;或者返回void 用ResultHandler處理數(shù)據(jù)回調(diào),在調(diào)用方式時傳入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?。?!

流式查詢由于需要保持客戶端與服務(wù)端的連接,而一般查詢提交完連接就會關(guān)閉;因此我們需要保持事務(wù)開啟,否則會報“A Cursor is already closed.”,即Cursor已經(jīng)關(guān)閉,沒法再讀取了。最簡單的方法就是在方法上加@Transactional,在查詢完畢以前事務(wù)會一直持有這個數(shù)據(jù)庫連接,但我們在使用完畢后也要自行關(guān)閉連接,顯式調(diào)用Cursor.close(),或者用try with resource語句。

游標(biāo)查詢和流式查詢的區(qū)分是fetchSize = Integer.MIN_VALUE

Cursor 還提供了三個方法:

  1. isOpen():用于在取數(shù)據(jù)之前判斷 Cursor 對象是否是打開狀態(tài)。只有當(dāng)打開時 Cursor 才能取數(shù)據(jù);
  2. isConsumed():用于判斷查詢結(jié)果是否全部取完;
  3. getCurrentIndex():返回已經(jīng)獲取了多少條數(shù)據(jù)。
try(Cursor cursor = OrderMapper.listOrders()) {
  cursor.forEach(rowObject -> {
      // ...
  });
}

  OrderMapper.listOrders2(queryWrapper,resultContext -> {
    ResultVo result = resultContext.getResultObject();
   //這邊循環(huán)調(diào)用就可以實現(xiàn)業(yè)務(wù)了

}

游標(biāo)查詢

和流式查詢類似fetchSize不設(shè)置為MIN_VALUE即可
JDBC查詢默認(rèn)是不支持FetchSize屬性的,需要在JDBC連接URL后面加上**“useCursorFetch=true”。**

useCursorFetch=true 是針對 MySQL 數(shù)據(jù)庫的 JDBC 連接參數(shù),用于啟用服務(wù)器端游標(biāo)獲取數(shù)據(jù)。在 MyBatis 中,當(dāng)使用流式查詢(例如:分頁查詢、結(jié)果集處理和使用游標(biāo)等)時,這個配置可以幫助逐行從服務(wù)器檢索數(shù)據(jù),而不是一次性將所有數(shù)據(jù)加載到內(nèi)存中,從而降低內(nèi)存占用。

當(dāng)使用 MySQL 數(shù)據(jù)庫時,在 JDBC 連接字符串中加入 useCursorFetch=true,并結(jié)合設(shè)置合適的 fetchSize,可以避免因一次性加載過多數(shù)據(jù)導(dǎo)致的內(nèi)存溢出問題。注意,此配置僅對 MySQL 數(shù)據(jù)庫有效。 如果不設(shè)置 useCursorFetch=true 這個配置,僅使用之前提到的那些配置(如設(shè)置 defaultFetchSize、分頁查詢、結(jié)果集處理和使用游標(biāo)等),在大多數(shù)情況下,這些配置仍然可以有效地避免查詢導(dǎo)致的內(nèi)存溢出。

但需要注意的是,對于 MySQL 數(shù)據(jù)庫,如果不啟用服務(wù)器端游標(biāo)獲取數(shù)據(jù),這可能會影響到流式查詢的效果。因為在默認(rèn)情況下,MySQL JDBC 驅(qū)動會一次性將所有數(shù)據(jù)加載到內(nèi)存中。此時,即使使用了其他配置,也可能無法達(dá)到預(yù)期的內(nèi)存優(yōu)化效果。

總的來說,在使用 MySQL 數(shù)據(jù)庫時,推薦在 JDBC 連接字符串中加入 useCursorFetch=true 配置,以更好地支持流式查詢和降低內(nèi)存占用。在其他數(shù)據(jù)庫中,可以根據(jù)實際需求和場景選擇合適的配置和策略來避免查詢導(dǎo)致的內(nèi)存溢出。

還要知道如何判斷自己是否使用了流式查詢或游標(biāo)查詢,下面是幾個數(shù)據(jù)集的對應(yīng)關(guān)系

普通分頁 ResultsetRowsStatic RowDataStatic
查詢方式 結(jié)果集類型 行數(shù)據(jù)類型
流式查詢 ResultsetRowsStreaming RowDataDynamic
游標(biāo)查詢 ResultsetRowsCursor RowDataCursor

這3種查詢方式,常規(guī)非大數(shù)據(jù)模式下普通查詢最快,其次是流式查詢,最次是游標(biāo)查詢.

主要是由于游標(biāo)查詢需要和數(shù)據(jù)庫進(jìn)行多次網(wǎng)絡(luò)交互,Client處理完這部分后再拉取下一部分?jǐn)?shù)據(jù),因此會比較慢。但是流式查詢又會長時間占用同一個數(shù)據(jù)庫連接,因此要取舍一下是能接受連接一直持有但是可能會堵住導(dǎo)致響應(yīng)慢,還是可能占用較多連接數(shù)但單次響應(yīng)快。當(dāng)通過流式查詢獲取一個ResultSet后,在你通過next迭代出所有元素之前或者調(diào)用close關(guān)閉它之前,你不能使用同一個數(shù)據(jù)庫連接去發(fā)起另外一個查詢,否者拋出異常(第一次調(diào)用的正常,第二次的拋出異常)。文章來源地址http://www.zghlxwxcb.cn/news/detail-681534.html

到了這里,關(guān)于萬級數(shù)據(jù)優(yōu)化EasyExcel+mybatis流式查詢導(dǎo)出封裝的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 積木報表Excel數(shù)據(jù)量大導(dǎo)出慢導(dǎo)不出問題、大量數(shù)據(jù)導(dǎo)不出問題優(yōu)化方案和分析解決思路(優(yōu)化前一萬多導(dǎo)出失敗,優(yōu)化后支持百萬級跨庫表導(dǎo)出)

    積木報表Excel數(shù)據(jù)量大導(dǎo)出慢導(dǎo)不出問題、大量數(shù)據(jù)導(dǎo)不出問題優(yōu)化方案和分析解決思路(優(yōu)化前一萬多導(dǎo)出失敗,優(yōu)化后支持百萬級跨庫表導(dǎo)出)

    原積木導(dǎo)出有兩種導(dǎo)出,直接導(dǎo)出和大數(shù)據(jù)導(dǎo)出(大數(shù)據(jù)導(dǎo)出是做了優(yōu)化去掉了一些樣式之類的,性能更好) 實測中發(fā)現(xiàn) 原積木大數(shù)據(jù)導(dǎo)出性能:1萬條數(shù)據(jù)導(dǎo)出耗時30秒,1.5萬條耗時1.5分鐘導(dǎo)出失敗,數(shù)據(jù)超過一萬條后經(jīng)常導(dǎo)出失敗,還會導(dǎo)致容器實例探活失敗/內(nèi)存撐爆重

    2024年04月11日
    瀏覽(68)
  • Java,excel大量百萬數(shù)據(jù)導(dǎo)出優(yōu)化措施,SXSSFWorkbook流式、分批次導(dǎo)出示例

    在導(dǎo)出百萬級的數(shù)據(jù)時,如果不采用適當(dāng)?shù)膬?yōu)化措施,確實可能會造成死機(jī)和內(nèi)存崩潰等問題。 為避免這些問題,可以采用以下優(yōu)化措施: 分批次讀取數(shù)據(jù):將需要導(dǎo)出的數(shù)據(jù)分成多個批次進(jìn)行讀取和寫入,每次讀取部分?jǐn)?shù)據(jù),寫入 Excel 后即時清除內(nèi)存。這樣可以避免一次

    2024年02月16日
    瀏覽(35)
  • 【EasyExcel】封裝一個分頁寫數(shù)據(jù)的通用方法(保姆級),繼上一篇easyExcel導(dǎo)出上線后的優(yōu)化

    需求:通過elasticsearch查詢出來一次性寫,在大數(shù)據(jù)量時存在OOM的隱患分頁查詢、分批次寫數(shù)據(jù),避免導(dǎo)出大數(shù)據(jù)量時內(nèi)存消耗陡增基于elasticsearch分頁查詢;mybatis-puls同理 在上個博客中解決了線上導(dǎo)出字體依賴的問題,由于涉及的導(dǎo)出模塊較多,因為打算封裝一個方法做通用

    2024年02月07日
    瀏覽(24)
  • 【QT性能優(yōu)化】QT性能優(yōu)化之QT6框架高性能模型視圖代理框架千萬級數(shù)據(jù)表分頁查詢優(yōu)化

    【QT性能優(yōu)化】QT性能優(yōu)化之QT6框架高性能模型視圖代理框架千萬級數(shù)據(jù)表分頁查詢優(yōu)化

    QT性能優(yōu)化之QT6框架高性能模型視圖代理框架千萬級數(shù)據(jù)表分頁查詢優(yōu)化 簡介 本文介紹了QT模型視圖代理框架中的QT表格控件和QT數(shù)據(jù)庫模塊中的QT數(shù)據(jù)庫查詢模型結(jié)合使用的一個應(yīng)用實踐案例:QT高性能表格控件分頁展示千萬行數(shù)據(jù)。本文介紹了這個應(yīng)用實踐案例的運(yùn)行效果

    2024年02月14日
    瀏覽(32)
  • 深入解析MySQL視圖、索引、數(shù)據(jù)導(dǎo)入導(dǎo)出:優(yōu)化查詢和提高效率

    深入解析MySQL視圖、索引、數(shù)據(jù)導(dǎo)入導(dǎo)出:優(yōu)化查詢和提高效率

    目錄 1. 視圖(View): 什么是視圖? 為什么要使用視圖? 視圖的優(yōu)缺點(diǎn) 1) 定制用戶數(shù)據(jù),聚焦特定的數(shù)據(jù) 2) 簡化數(shù)據(jù)操作 3) 提高數(shù)據(jù)的安全性 4) 共享所需數(shù)據(jù) 5) 更改數(shù)據(jù)格式 6) 重用 SQL 語句 示例操作 沒使用前 使用后 2. 索引(Index): 什么是索引? 為什么要使用索引?

    2024年02月13日
    瀏覽(39)
  • MySQL千萬級數(shù)據(jù)優(yōu)化方案

    MySQL千萬級數(shù)據(jù)優(yōu)化方案

    ? ? ? ? ? ? ? ? ? ? ? ? ? ↓↓↓處理千萬級數(shù)據(jù)的MySQL數(shù)據(jù)庫,可以采取以下優(yōu)化措施↓↓↓ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 使用索引:確保對經(jīng)常用于查詢和排序的字段添加索引。不要在查詢中使用SELECT *,而是明確指定需要的字段。

    2024年02月07日
    瀏覽(22)
  • EasyExcel導(dǎo)入和導(dǎo)出數(shù)據(jù)

    EasyExcel導(dǎo)入和導(dǎo)出數(shù)據(jù)

    1.cmtroller 調(diào)用service方法,完成導(dǎo)出 2.service 調(diào)用工具類的方法完成導(dǎo)出 傳入response,標(biāo)題控制類(標(biāo)題名稱,合并的列數(shù)),員工列表,文件名稱,excel的標(biāo)題名稱,要導(dǎo)出的數(shù)據(jù)類 3.工具類中的方法 4.控制標(biāo)題類 這個類控制數(shù)據(jù)之前的顯示內(nèi)容 效果 2022-10-28 亂碼解決 因為文件

    2023年04月09日
    瀏覽(23)
  • EasyExcel快速導(dǎo)出 100W 數(shù)據(jù)

    導(dǎo)出是后臺管理系統(tǒng)的常用功能,當(dāng)數(shù)據(jù)量特別大的時候會內(nèi)存溢出和卡頓頁面,曾經(jīng)自己封裝過一個導(dǎo)出,采用了分批查詢數(shù)據(jù)來避免內(nèi)存溢出和使用SXSSFWorkbook方式緩存數(shù)據(jù)到文件上以解決下載大文件EXCEL卡死頁面的問題。 不過一是存在封裝不太友好使用不方便的問題,二

    2024年01月16日
    瀏覽(23)
  • 數(shù)據(jù)導(dǎo)入導(dǎo)出(POI以及easyExcel)

    數(shù)據(jù)導(dǎo)入導(dǎo)出(POI以及easyExcel)

    ????????將一些數(shù)據(jù)庫信息導(dǎo)出為Excel表格 ????????將Excel表格數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫 ? ? ? ? 大量數(shù)據(jù)的導(dǎo)入導(dǎo)出操作 常?的解決?案為: Apache POI 與阿?巴巴 easyExcel Apache POI 是基于 Office Open XML 標(biāo)準(zhǔn)( OOXML )和 Microsoft 的 OLE 2 復(fù)合?檔 格式( OLE2 )處理各種?件格式的

    2024年02月13日
    瀏覽(21)
  • Spring boot easyexcel 實現(xiàn)復(fù)合數(shù)據(jù)導(dǎo)出、按模塊導(dǎo)出

    Spring boot easyexcel 實現(xiàn)復(fù)合數(shù)據(jù)導(dǎo)出、按模塊導(dǎo)出

    場景: 導(dǎo)出數(shù)據(jù)為1對多的復(fù)合數(shù)據(jù) 一個模塊是一條數(shù)據(jù),直接填充數(shù)據(jù)無法實現(xiàn) 如圖: 紅框內(nèi)為一條數(shù)據(jù)(1對多),下方箭頭指向為第二條數(shù)據(jù) 如果直接填充,只能填充第一條,第二條就沒辦法了。 由于多行都包含許多,固定表頭,只能走填充路線,怎么實現(xiàn)呢 實現(xiàn)思路

    2024年02月07日
    瀏覽(19)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包