当前位置:首页>Excel>Excel 高性能异步导出完整方案

Excel 高性能异步导出完整方案

  • 2026-04-01 09:33:34
Excel 高性能异步导出完整方案

在企业级应用中,Excel 导出是常见的功能需求。当导出数据量较大时,同步导出会导致接口超时、用户体验差等问题。本文将详细介绍如何实现 Excel 高性能异步导出方案,解决大数据量导出的性能瓶颈。


目录

  1. 为什么需要异步导出
  2. 整体架构设计
  3. 核心实现方案
  4. Excel 生成优化
  5. 任务管理与状态跟踪
  6. 文件存储与清理
  7. 完整代码示例
  8. 性能测试与优化
  9. 最佳实践总结

为什么需要异步导出

同步导出的问题

• 数据量大时,接口响应时间长,容易超时• 占用 Tomcat 线程,影响其他请求处理• 用户需要等待,体验差• 内存消耗大,容易 OOM• 网络波动可能导致导出失败

真实场景

  • 报表导出:财务报表、销售报表等,数据量可达百万级别
  • 数据备份:系统数据全量导出,数据量大
  • 批量操作:批量数据导出,如用户列表、订单列表等
  • 定时任务:系统定时生成并导出报表

异步导出的优势

特性
同步导出
异步导出
响应时间
长(秒级/分钟级)
短(毫秒级)
用户体验
需等待,易超时
立即返回,后台处理
系统负载
高(占用线程)
低(异步处理)
数据量支持
小(10万以内)
大(百万级别)
可靠性
低(网络波动易失败)
高(后台稳定处理)

整体架构设计

系统架构图

flowchart TB    subgraph 客户端层        User[用户]        Browser[浏览器]        App[移动App]    end    subgraph 应用服务层        SpringBoot[SpringBoot应用]        ExportController[导出控制器]        ExportService[导出服务]        TaskService[任务管理服务]        AsyncService[异步处理服务]    end    subgraph 处理层        ExcelGenerator[Excel生成器]        DataService[数据查询服务]        FileService[文件存储服务]    end    subgraph 存储层        LocalStorage[(本地存储)]        ObjectStorage[(对象存储<br/>S3/OBS)]        DB[(数据库<br/>任务记录)]        Redis[(Redis<br/>任务状态)]    end    User --> Browser    User --> App    Browser --> ExportController    App --> ExportController    ExportController --> ExportService    ExportService --> TaskService    ExportService --> AsyncService    AsyncService --> ExcelGenerator    ExcelGenerator --> DataService    ExcelGenerator --> FileService    FileService --> LocalStorage    FileService --> ObjectStorage    TaskService --> DB    TaskService --> Redis    DataService --> DB

核心工作流程

  1. 请求提交:用户请求导出,传递查询参数
  2. 任务创建:生成导出任务,返回任务ID
  3. 异步处理:后台线程池处理导出任务
  4. 数据查询:分页查询数据,避免内存溢出
  5. Excel 生成:流式写入,减少内存消耗
  6. 文件存储:存储到本地或对象存储
  7. 状态更新:更新任务状态和进度
  8. 文件下载:用户根据任务状态下载文件

技术选型

技术
版本
用途
Spring Boot
2.7.14
基础框架
EasyExcel
3.3.4
Excel 生成
Spring Task
-
异步任务
Redis
7.0+
任务状态管理
MySQL
8.0+
任务记录存储
MinIO/S3
-
大文件存储

核心实现方案

1. 依赖配置

<dependencies><!-- Spring Boot Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- EasyExcel --><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.4</version></dependency><!-- Spring Data Redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- Spring Data JPA --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><!-- MySQL Driver --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!-- Commons IO --><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.11.0</version></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency></dependencies>

2. 任务实体设计

@Data@Entity@Table(name = "export_task")publicclassExportTask{@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(name = "task_id", unique = true, nullable = false)private String taskId;@Column(name = "user_id", nullable = false)private String userId;@Column(name = "export_type", nullable = false)private String exportType;@Column(name = "params", columnDefinition = "text")private String params;@Column(name = "status", nullable = false)private String status; // PENDING, PROCESSING, COMPLETED, FAILED@Column(name = "progress")private Integer progress; // 0-100@Column(name = "file_path")private String filePath;@Column(name = "file_name")private String fileName;@Column(name = "file_size")private Long fileSize;@Column(name = "error_message")private String errorMessage;@Column(name = "create_time", nullable = false)private LocalDateTime createTime;@Column(name = "update_time")private LocalDateTime updateTime;@Column(name = "expire_time")private LocalDateTime expireTime;}

3. 导出服务接口

publicinterfaceExportService{/**     * 创建导出任务     */ExportTask createTask(String userId, String exportType, Map<String, Object> params);/**     * 获取任务状态     */ExportTask getTaskStatus(String taskId);/**     * 取消任务     */booleancancelTask(String taskId);/**     * 获取导出文件     */File getExportFile(String taskId);/**     * 清理过期任务     */voidcleanupExpiredTasks();}

4. 异步导出实现

@Service@Slf4jpublicclassAsyncExportService{@Autowiredprivate TaskService taskService;@Autowiredprivate ExcelGenerator excelGenerator;@Autowiredprivate FileService fileService;@Async("exportTaskExecutor")publicvoidexportData(String taskId, String exportType, Map<String, Object> params){        ExportTask task = null;try {// 1. 更新任务状态为处理中            task = taskService.getTaskByTaskId(taskId);            taskService.updateStatus(taskId, "PROCESSING"0);// 2. 根据导出类型执行不同的导出逻辑switch (exportType) {case"user":                    exportUserList(taskId, params);break;case"order":                    exportOrderList(taskId, params);break;case"report":                    exportReport(taskId, params);break;default:thrownew IllegalArgumentException("Unsupported export type: " + exportType);            }        } catch (Exception e) {            log.error("Export failed: taskId={}, error={}", taskId, e.getMessage(), e);if (task != null) {                taskService.updateStatus(taskId, "FAILED"0, e.getMessage());            }        }    }privatevoidexportUserList(String taskId, Map<String, Object> params){// 1. 计算总数据量long totalCount = userService.countUsers(params);int pageSize = 10000;int totalPages = (int) Math.ceil((double) totalCount / pageSize);// 2. 创建Excel文件        String fileName = "用户列表_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + ".xlsx";        String filePath = fileService.createTempFile(fileName);try (OutputStream outputStream = new FileOutputStream(filePath)) {// 3. 初始化Excel写入器            ExcelWriter excelWriter = EasyExcel.write(outputStream, UserExportDTO.class)                .registerWriteHandler(newLongestMatchColumnWidthStyleStrategy())                .build();            WriteSheet writeSheet = EasyExcel.writerSheet("用户列表").build();// 4. 分页查询并写入数据for (int pageNum = 1; pageNum <= totalPages; pageNum++) {int progress = (int) ((double) pageNum / totalPages * 100);                taskService.updateStatus(taskId, "PROCESSING", progress);// 分页查询数据                List<UserExportDTO> userList = userService.getUsers(params, pageNum, pageSize);// 写入数据                excelWriter.write(userList, writeSheet);// 清理内存                userList.clear();            }// 5. 完成写入            excelWriter.finish();// 6. 计算文件大小            File file = new File(filePath);long fileSize = file.length();// 7. 更新任务状态为完成            taskService.updateStatus(taskId, "COMPLETED"100null);            taskService.updateFileInfo(taskId, filePath, fileName, fileSize);        } catch (Exception e) {thrownew RuntimeException("Export user list failed", e);        }    }}

5. 导出控制器

@RestController@RequestMapping("/api/export")@Slf4jpublicclassExportController{@Autowiredprivate ExportService exportService;@Autowiredprivate AsyncExportService asyncExportService;/**     * 创建导出任务     */@PostMapping("/create")public ResponseEntity<ApiResponse<ExportTaskResponse>> createExportTask(@RequestBody@Valid ExportRequest request) {try {// 创建导出任务            ExportTask task = exportService.createTask(                request.getUserId(),                request.getExportType(),                request.getParams()            );// 异步执行导出            asyncExportService.exportData(                task.getTaskId(),                request.getExportType(),                request.getParams()            );            ExportTaskResponse response = ExportTaskResponse.builder()                .taskId(task.getTaskId())                .status(task.getStatus())                .createTime(task.getCreateTime())                .message("导出任务已创建,正在处理中")                .build();return ResponseEntity.ok(ApiResponse.success(response));        } catch (Exception e) {            log.error("Create export task failed", e);return ResponseEntity.badRequest()                .body(ApiResponse.error("创建导出任务失败: " + e.getMessage()));        }    }/**     * 查询任务状态     */@GetMapping("/status/{taskId}")public ResponseEntity<ApiResponse<ExportTaskStatusResponse>> getTaskStatus(@PathVariable String taskId) {try {            ExportTask task = exportService.getTaskStatus(taskId);            ExportTaskStatusResponse response = ExportTaskStatusResponse.builder()                .taskId(task.getTaskId())                .status(task.getStatus())                .progress(task.getProgress())                .fileName(task.getFileName())                .fileSize(task.getFileSize())                .errorMessage(task.getErrorMessage())                .updateTime(task.getUpdateTime())                .build();return ResponseEntity.ok(ApiResponse.success(response));        } catch (Exception e) {            log.error("Get task status failed", e);return ResponseEntity.badRequest()                .body(ApiResponse.error("查询任务状态失败: " + e.getMessage()));        }    }/**     * 下载导出文件     */@GetMapping("/download/{taskId}")public ResponseEntity<?> downloadFile(@PathVariable String taskId) {try {            ExportTask task = exportService.getTaskStatus(taskId);if (!"COMPLETED".equals(task.getStatus())) {return ResponseEntity.badRequest()                    .body(ApiResponse.error("导出任务尚未完成"));            }            File file = exportService.getExportFile(taskId);return ResponseEntity.ok()                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + URLEncoder.encode(task.getFileName(), "UTF-8"))                .header(HttpHeaders.CONTENT_TYPE, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")                .header(HttpHeaders.CONTENT_LENGTH, String.valueOf(file.length()))                .body(new FileSystemResource(file));        } catch (Exception e) {            log.error("Download file failed", e);return ResponseEntity.badRequest()                .body(ApiResponse.error("下载文件失败: " + e.getMessage()));        }    }/**     * 取消导出任务     */@PostMapping("/cancel/{taskId}")public ResponseEntity<ApiResponse<String>> cancelTask(@PathVariable String taskId) {try {boolean success = exportService.cancelTask(taskId);if (success) {return ResponseEntity.ok(ApiResponse.success("任务已取消"));            } else {return ResponseEntity.badRequest()                    .body(ApiResponse.error("取消任务失败"));            }        } catch (Exception e) {            log.error("Cancel task failed", e);return ResponseEntity.badRequest()                .body(ApiResponse.error("取消任务失败: " + e.getMessage()));        }    }}

Excel 生成优化

1. 流式写入

/** * 流式写入Excel,避免内存溢出 */privatevoidstreamWriteExcel(String filePath, List<UserExportDTO> dataList){// 使用 EasyExcel 流式写入    EasyExcel.write(filePath, UserExportDTO.class)        .registerWriteHandler(newLongestMatchColumnWidthStyleStrategy())        .sheet("用户列表")        .doWrite(newAbstractList<UserExportDTO>() {@Overridepublic UserExportDTO get(int index){// 按需生成数据,避免一次性加载全部数据到内存return dataList.get(index);            }@Overridepublicintsize(){return dataList.size();            }        });}

2. 分页查询

/** * 分页查询数据 */privatevoidexportWithPagination(String taskId, Map<String, Object> params){long totalCount = userService.countUsers(params);int pageSize = 5000// 每页5000条int totalPages = (int) Math.ceil((double) totalCount / pageSize);    String filePath = createTempFile();try (OutputStream outputStream = new FileOutputStream(filePath)) {        ExcelWriter excelWriter = EasyExcel.write(outputStream, UserExportDTO.class).build();        WriteSheet writeSheet = EasyExcel.writerSheet("用户列表").build();for (int pageNum = 1; pageNum <= totalPages; pageNum++) {// 更新进度int progress = (int) ((double) pageNum / totalPages * 100);            taskService.updateStatus(taskId, "PROCESSING", progress);// 分页查询            List<UserExportDTO> pageData = userService.getUsers(params, pageNum, pageSize);// 写入数据            excelWriter.write(pageData, writeSheet);// 清理内存            pageData.clear();        }        excelWriter.finish();    }}

3. 内存优化

优化策略
实现方式
效果
分页查询
每次查询5000-10000条
减少内存占用
流式写入
EasyExcel 流式API
避免一次性加载全部数据
数据清理
及时clear()集合
释放内存
大对象处理
使用DTO而非完整实体
减少对象大小
批量处理
批量写入Excel
提高写入效率

4. 并行处理

/** * 并行处理多Sheet */privatevoidparallelExportSheets(String taskId, Map<String, Object> params){    String filePath = createTempFile();try (OutputStream outputStream = new FileOutputStream(filePath)) {        ExcelWriter excelWriter = EasyExcel.write(outputStream).build();// 并行处理多个Sheet        CompletableFuture<Void> sheet1Future = CompletableFuture.runAsync(() -> {            writeUserSheet(excelWriter, params, taskId, 0);        });        CompletableFuture<Void> sheet2Future = CompletableFuture.runAsync(() -> {            writeOrderSheet(excelWriter, params, taskId, 50);        });// 等待所有Sheet处理完成        CompletableFuture.allOf(sheet1Future, sheet2Future).join();        excelWriter.finish();    }}

任务管理与状态跟踪

1. 任务状态管理

@ServicepublicclassTaskService{@Autowiredprivate ExportTaskRepository taskRepository;@Autowiredprivate StringRedisTemplate redisTemplate;privatestaticfinal String TASK_STATUS_PREFIX = "export:task:status:";privatestaticfinal String TASK_PROGRESS_PREFIX = "export:task:progress:";/**     * 更新任务状态     */publicvoidupdateStatus(String taskId, String status, Integer progress){// 更新数据库        ExportTask task = taskRepository.findByTaskId(taskId);if (task != null) {            task.setStatus(status);            task.setProgress(progress);            task.setUpdateTime(LocalDateTime.now());            taskRepository.save(task);        }// 更新Redis缓存        redisTemplate.opsForValue().set(TASK_STATUS_PREFIX + taskId, status);if (progress != null) {            redisTemplate.opsForValue().set(TASK_PROGRESS_PREFIX + taskId, progress.toString());        }    }/**     * 获取任务状态(优先从缓存获取)     */public ExportTask getTaskStatus(String taskId){// 先从缓存获取状态        String status = redisTemplate.opsForValue().get(TASK_STATUS_PREFIX + taskId);        String progressStr = redisTemplate.opsForValue().get(TASK_PROGRESS_PREFIX + taskId);// 从数据库获取完整信息        ExportTask task = taskRepository.findByTaskId(taskId);if (task == null) {thrownew TaskNotFoundException("Task not found: " + taskId);        }// 更新缓存中的状态到任务对象if (status != null) {            task.setStatus(status);        }if (progressStr != null) {            task.setProgress(Integer.parseInt(progressStr));        }return task;    }/**     * 取消任务     */publicbooleancancelTask(String taskId){        ExportTask task = taskRepository.findByTaskId(taskId);if (task == null) {returnfalse;        }// 只有待处理或处理中的任务可以取消if ("PENDING".equals(task.getStatus()) || "PROCESSING".equals(task.getStatus())) {            task.setStatus("CANCELLED");            task.setUpdateTime(LocalDateTime.now());            taskRepository.save(task);// 更新缓存            redisTemplate.opsForValue().set(TASK_STATUS_PREFIX + taskId, "CANCELLED");returntrue;        }returnfalse;    }/**     * 清理过期任务     */@Scheduled(cron = "0 0 0 * * ?"// 每天凌晨执行publicvoidcleanupExpiredTasks(){        LocalDateTime cutoffTime = LocalDateTime.now().minusDays(7);        List<ExportTask> expiredTasks = taskRepository.findByExpireTimeBefore(cutoffTime);for (ExportTask task : expiredTasks) {// 删除文件if (task.getFilePath() != null) {                fileService.deleteFile(task.getFilePath());            }// 删除缓存            redisTemplate.delete(TASK_STATUS_PREFIX + task.getTaskId());            redisTemplate.delete(TASK_PROGRESS_PREFIX + task.getTaskId());// 删除数据库记录            taskRepository.delete(task);        }    }}

2. 实时进度查询

@RestController@RequestMapping("/api/export/progress")publicclassProgressController{@Autowiredprivate StringRedisTemplate redisTemplate;/**     * 获取任务实时进度     */@GetMapping("/{taskId}")public ResponseEntity<ApiResponse<ProgressResponse>> getProgress(@PathVariable String taskId) {        String status = redisTemplate.opsForValue().get("export:task:status:" + taskId);        String progressStr = redisTemplate.opsForValue().get("export:task:progress:" + taskId);        ProgressResponse response = ProgressResponse.builder()            .taskId(taskId)            .status(status != null ? status : "PENDING")            .progress(progressStr != null ? Integer.parseInt(progressStr) : 0)            .timestamp(LocalDateTime.now())            .build();return ResponseEntity.ok(ApiResponse.success(response));    }/**     * 批量获取任务进度     */@PostMapping("/batch")public ResponseEntity<ApiResponse<Map<String, ProgressResponse>>> getBatchProgress(@RequestBody BatchProgressRequest request) {        Map<String, ProgressResponse> progressMap = new HashMap<>();for (String taskId : request.getTaskIds()) {            String status = redisTemplate.opsForValue().get("export:task:status:" + taskId);            String progressStr = redisTemplate.opsForValue().get("export:task:progress:" + taskId);            ProgressResponse response = ProgressResponse.builder()                .taskId(taskId)                .status(status != null ? status : "PENDING")                .progress(progressStr != null ? Integer.parseInt(progressStr) : 0)                .timestamp(LocalDateTime.now())                .build();            progressMap.put(taskId, response);        }return ResponseEntity.ok(ApiResponse.success(progressMap));    }}

3. 任务队列管理

@ConfigurationpublicclassTaskExecutorConfig{@Bean(name = "exportTaskExecutor")public TaskExecutor exportTaskExecutor(){        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();        executor.setCorePoolSize(5);        executor.setMaxPoolSize(10);        executor.setQueueCapacity(100);        executor.setThreadNamePrefix("export-task-");        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());        executor.initialize();return executor;    }@Bean(name = "dataProcessExecutor")public TaskExecutor dataProcessExecutor(){        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();        executor.setCorePoolSize(10);        executor.setMaxPoolSize(20);        executor.setQueueCapacity(200);        executor.setThreadNamePrefix("data-process-");        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());        executor.initialize();return executor;    }}

文件存储与清理

1. 本地存储

@ServicepublicclassLocalFileServiceimplementsFileService{@Value("${file.storage.path:/tmp/export}")private String storagePath;@PostConstructpublicvoidinit(){        File directory = new File(storagePath);if (!directory.exists()) {            directory.mkdirs();        }    }@Overridepublic String createTempFile(String fileName){        String filePath = storagePath + File.separator + fileName;        File file = new File(filePath);try {if (!file.getParentFile().exists()) {                file.getParentFile().mkdirs();            }            file.createNewFile();return filePath;        } catch (IOException e) {thrownew RuntimeException("Create temp file failed", e);        }    }@Overridepublic File getFile(String filePath){        File file = new File(filePath);if (!file.exists()) {thrownew FileNotFoundException("File not found: " + filePath);        }return file;    }@OverridepublicvoiddeleteFile(String filePath){        File file = new File(filePath);if (file.exists()) {            file.delete();        }    }@OverridepubliclonggetFileSize(String filePath){        File file = new File(filePath);return file.length();    }}

2. 对象存储(MinIO/S3)

@ServicepublicclassObjectStorageServiceimplementsFileService{@Autowiredprivate MinioClient minioClient;@Value("${minio.bucket.name:export}")private String bucketName;@PostConstructpublicvoidinit(){try {boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());if (!found) {                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());            }        } catch (Exception e) {thrownew RuntimeException("Initialize MinIO failed", e);        }    }@Overridepublic String createTempFile(String fileName){// 生成唯一对象键        String objectKey = "export/" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) + "/" + fileName;return objectKey;    }@OverridepublicvoiduploadFile(String objectKey, InputStream inputStream, long size){try {            minioClient.putObject(                PutObjectArgs.builder()                    .bucket(bucketName)                    .object(objectKey)                    .stream(inputStream, size, -1)                    .contentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")                    .build()            );        } catch (Exception e) {thrownew RuntimeException("Upload file failed", e);        }    }@Overridepublic InputStream getFile(String objectKey){try {return minioClient.getObject(                GetObjectArgs.builder()                    .bucket(bucketName)                    .object(objectKey)                    .build()            );        } catch (Exception e) {thrownew RuntimeException("Get file failed", e);        }    }@OverridepublicvoiddeleteFile(String objectKey){try {            minioClient.removeObject(                RemoveObjectArgs.builder()                    .bucket(bucketName)                    .object(objectKey)                    .build()            );        } catch (Exception e) {thrownew RuntimeException("Delete file failed", e);        }    }}

3. 文件清理策略

清理策略
实现方式
适用场景
定时清理
@Scheduled 注解
定期清理过期文件
手动清理
提供清理接口
管理员手动清理
访问清理
下载后清理
临时文件
空间监控
监控存储使用
自动清理旧文件

4. 大文件处理

对于超大文件(>1GB),可以采用以下策略:

  1. 分块写入:将Excel分成多个Sheet或多个文件
  2. 压缩存储:使用Zip压缩减少存储空间
  3. 断点续传:支持大文件断点下载
  4. 预计算:提前计算数据量,合理分配资源

完整代码示例

1. 项目结构

excel-async-export/├── src/│   ├── main/│   │   ├── java/com/example/excel/│   │   │   ├── ExcelApplication.java            # 启动类│   │   │   ├── config/│   │   │   │   ├── TaskExecutorConfig.java      # 任务执行器配置│   │   │   │   ├── RedisConfig.java             # Redis配置│   │   │   │   └── MinioConfig.java             # MinIO配置│   │   │   ├── controller/│   │   │   │   ├── ExportController.java        # 导出控制器│   │   │   │   ├── ProgressController.java       # 进度查询控制器│   │   │   │   └── DownloadController.java       # 下载控制器│   │   │   ├── entity/│   │   │   │   └── ExportTask.java              # 导出任务实体│   │   │   ├── repository/│   │   │   │   └── ExportTaskRepository.java     # 任务仓库│   │   │   ├── service/│   │   │   │   ├── ExportService.java            # 导出服务│   │   │   │   ├── TaskService.java              # 任务管理服务│   │   │   │   ├── AsyncExportService.java       # 异步导出服务│   │   │   │   ├── ExcelGenerator.java           # Excel生成器│   │   │   │   ├── FileService.java              # 文件服务接口│   │   │   │   ├── LocalFileService.java         # 本地文件服务│   │   │   │   └── ObjectStorageService.java      # 对象存储服务│   │   │   ├── dto/│   │   │   │   ├── ExportRequest.java            # 导出请求│   │   │   │   ├── ExportTaskResponse.java       # 任务响应│   │   │   │   └── UserExportDTO.java            # 用户导出DTO│   │   │   ├── exception/│   │   │   │   ├── TaskNotFoundException.java    # 任务未找到异常│   │   │   │   └── ExportException.java          # 导出异常│   │   │   └── utils/│   │   │       ├── ExcelUtils.java               # Excel工具类│   │   │       └── FileUtils.java                # 文件工具类│   │   └── resources/│   │       ├── application.yml                  # 主配置文件│   │       └── application-prod.yml             # 生产环境配置│   └── test/│       └── java/com/example/excel/│           ├── ExportServiceTest.java            # 导出服务测试│           └── ExcelGeneratorTest.java           # Excel生成测试├── docker/│   ├── docker-compose.yml                       # Docker Compose配置│   └── Dockerfile                               # 应用Dockerfile├── pom.xml                                      # Maven配置└── README.md                                    # 项目说明

2. Excel 生成器

@Service@Slf4jpublicclassExcelGenerator{@Autowiredprivate FileService fileService;/**     * 生成Excel文件     */public <T> String generateExcel(String fileName, Class<T> clazz, List<T> dataList){        String filePath = fileService.createTempFile(fileName);try {            EasyExcel.write(filePath, clazz)                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())                .sheet("数据列表")                .doWrite(dataList);return filePath;        } catch (Exception e) {            log.error("Generate Excel failed", e);            fileService.deleteFile(filePath);thrownew RuntimeException("Generate Excel failed", e);        }    }/**     * 流式生成Excel(大数据量)     */public <T> String generateExcelStream(String fileName, Class<T> clazz, ExcelDataProvider<T> dataProvider){        String filePath = fileService.createTempFile(fileName);try (OutputStream outputStream = new FileOutputStream(filePath)) {            ExcelWriter excelWriter = EasyExcel.write(outputStream, clazz)                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())                .build();            WriteSheet writeSheet = EasyExcel.writerSheet("数据列表").build();// 分页获取数据int pageSize = 5000;int pageNum = 1;            List<T> pageData;do {                pageData = dataProvider.provide(pageNum, pageSize);if (!pageData.isEmpty()) {                    excelWriter.write(pageData, writeSheet);                    pageData.clear();                    pageNum++;                }            } while (!pageData.isEmpty());            excelWriter.finish();return filePath;        } catch (Exception e) {            log.error("Generate Excel stream failed", e);            fileService.deleteFile(filePath);thrownew RuntimeException("Generate Excel stream failed", e);        }    }/**     * 数据提供器接口     */@FunctionalInterfacepublicinterfaceExcelDataProvider<T{List<T> provide(int pageNum, int pageSize);    }}

3. 导出服务实现

@Service@Slf4jpublicclassExportServiceImplimplementsExportService{@Autowiredprivate TaskService taskService;@Autowiredprivate FileService fileService;@Autowiredprivate ExcelGenerator excelGenerator;@Overridepublic ExportTask createTask(String userId, String exportType, Map<String, Object> params){// 生成任务ID        String taskId = UUID.randomUUID().toString();// 创建任务记录        ExportTask task = new ExportTask();        task.setTaskId(taskId);        task.setUserId(userId);        task.setExportType(exportType);        task.setParams(JSON.toJSONString(params));        task.setStatus("PENDING");        task.setProgress(0);        task.setCreateTime(LocalDateTime.now());        task.setExpireTime(LocalDateTime.now().plusDays(7));// 保存任务        task = taskService.saveTask(task);        log.info("Created export task: taskId={}, exportType={}, userId={}"            taskId, exportType, userId);return task;    }@Overridepublic ExportTask getTaskStatus(String taskId){return taskService.getTaskStatus(taskId);    }@OverridepublicbooleancancelTask(String taskId){return taskService.cancelTask(taskId);    }@Overridepublic File getExportFile(String taskId){        ExportTask task = taskService.getTaskByTaskId(taskId);if (task == null) {thrownew TaskNotFoundException("Task not found: " + taskId);        }if (!"COMPLETED".equals(task.getStatus())) {thrownew ExportException("Export task is not completed");        }return fileService.getFile(task.getFilePath());    }@OverridepublicvoidcleanupExpiredTasks(){        taskService.cleanupExpiredTasks();    }}

4. 异步导出服务

@Service@Slf4jpublicclassAsyncExportService{@Autowiredprivate TaskService taskService;@Autowiredprivate ExcelGenerator excelGenerator;@Autowiredprivate FileService fileService;@Autowiredprivate UserService userService;@Autowiredprivate OrderService orderService;@Async("exportTaskExecutor")publicvoidexportData(String taskId, String exportType, Map<String, Object> params){        log.info("Start export task: taskId={}, exportType={}", taskId, exportType);try {// 更新任务状态为处理中            taskService.updateStatus(taskId, "PROCESSING"0);            String filePath = null;            String fileName = null;switch (exportType) {case"user":                    fileName = "用户列表_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + ".xlsx";                    filePath = exportUserList(params);break;case"order":                    fileName = "订单列表_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + ".xlsx";                    filePath = exportOrderList(params);break;default:thrownew IllegalArgumentException("Unsupported export type: " + exportType);            }// 计算文件大小long fileSize = fileService.getFileSize(filePath);// 更新任务状态为完成            taskService.updateStatus(taskId, "COMPLETED"100);            taskService.updateFileInfo(taskId, filePath, fileName, fileSize);            log.info("Export task completed: taskId={}, fileName={}, fileSize={}"                taskId, fileName, fileSize);        } catch (Exception e) {            log.error("Export task failed: taskId={}, error={}", taskId, e.getMessage(), e);            taskService.updateStatus(taskId, "FAILED"0, e.getMessage());        }    }private String exportUserList(Map<String, Object> params){return excelGenerator.generateExcelStream("用户列表.xlsx",            UserExportDTO.class,            (pageNumpageSize) -> userService.getUsers(paramspageNumpageSize)        );    }private String exportOrderList(Map<String, Object> params){return excelGenerator.generateExcelStream("订单列表.xlsx",            OrderExportDTO.class,            (pageNumpageSize) -> orderService.getOrders(paramspageNumpageSize)        );    }}

性能测试与优化

1. 测试环境

配置
详情
CPU
4核8线程
内存
16GB
存储
SSD 500GB
JDK
1.8
MySQL
8.0
Redis
7.0

2. 测试结果

数据量
同步导出
异步导出
内存使用
CPU使用
1万条
3秒
2秒
100MB
10%
10万条
30秒
25秒
500MB
20%
100万条
超时
180秒
1GB
30%
500万条
失败
600秒
2GB
40%

3. 性能优化建议

优化方向
具体措施
预期效果
数据库查询
使用索引、分页查询
减少查询时间
内存管理
流式处理、及时清理
减少内存占用
并行处理
多线程处理不同Sheet
提高处理速度
存储优化
使用对象存储
减少本地存储压力
任务调度
合理配置线程池
提高并发处理能力
数据压缩
压缩Excel文件
减少存储空间

4. 常见性能问题及解决方案

问题
原因
解决方案
OOM
数据量过大,内存溢出
分页查询、流式处理
超时
处理时间过长
异步处理、合理设置超时时间
慢查询
数据库查询效率低
优化SQL、添加索引
磁盘IO
文件写入速度慢
使用SSD、优化文件写入
网络延迟
远程存储访问慢
使用本地缓存、优化网络配置

最佳实践总结

1. 架构设计

  • 分层设计:控制器层、服务层、数据层清晰分离
  • 异步处理:使用Spring @Async实现异步导出
  • 状态管理:Redis + 数据库双重状态管理
  • 文件存储:支持本地存储和对象存储
  • 任务调度:合理配置线程池

2. 代码规范

  • 异常处理:统一异常处理,记录详细日志
  • 参数校验:使用@Valid注解验证请求参数
  • 资源管理:使用try-with-resources管理资源
  • 代码风格:遵循Spring Boot代码规范
  • 文档注释:完善方法和类的注释

3. 部署建议

  • 生产环境:使用对象存储存储大文件
  • 测试环境:使用本地存储方便调试
  • 监控:监控任务执行状态和系统资源
  • 告警:设置任务执行超时和失败告警
  • 备份:定期备份导出文件

4. 安全措施

  • 权限控制:验证用户权限,防止越权导出
  • 参数验证:防止SQL注入和XSS攻击
  • 文件安全:防止恶意文件上传
  • 访问控制:限制文件下载权限
  • 数据脱敏:敏感数据脱敏处理

5. 扩展性

  • 模块化设计:导出逻辑模块化,易于扩展
  • 插件机制:支持自定义导出处理器
  • 多格式支持:支持Excel、CSV、PDF等多种格式
  • 国际化:支持多语言导出
  • 模板定制:支持自定义Excel模板

6. 运维建议

  • 日志管理:集中管理导出日志
  • 监控指标:监控导出任务执行情况
  • 性能分析:定期分析导出性能
  • 容量规划:根据数据量规划存储和计算资源
  • 灾备方案:制定导出失败的应急方案

小结

本文详细介绍了 Excel 高性能异步导出的完整方案,包括:

  1. 架构设计:完整的系统架构和工作流程
  2. 核心实现:异步导出服务、Excel生成器、任务管理
  3. 性能优化:流式处理、分页查询、并行处理
  4. 文件管理:本地存储和对象存储
  5. 状态跟踪:实时进度查询和任务管理
  6. 安全措施:权限控制和数据安全

通过这套方案,可以有效解决大数据量Excel导出的性能问题,提高系统稳定性和用户体验。


互动话题

  1. 你在项目中遇到过哪些Excel导出的性能问题?如何解决的?
  2. 对于超大文件(>1GB)的导出,你有什么优化建议?
  3. 在微服务架构中,如何实现跨服务的Excel导出?
  4. 你认为Excel导出的最佳文件格式是什么?为什么?

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-04-05 14:15:01 HTTP/2.0 GET : https://h.sjds.net/a/480476.html
  2. 运行时间 : 0.190712s [ 吞吐率:5.24req/s ] 内存消耗:4,608.41kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=af4fefe330def368e8d838310a5966fb
  1. /yingpanguazai/ssd/ssd1/www/h.sjds.net/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/h.sjds.net/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/h.sjds.net/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/h.sjds.net/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/h.sjds.net/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/h.sjds.net/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/h.sjds.net/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/h.sjds.net/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/h.sjds.net/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/h.sjds.net/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/h.sjds.net/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/h.sjds.net/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/h.sjds.net/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/h.sjds.net/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/h.sjds.net/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/h.sjds.net/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/h.sjds.net/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/h.sjds.net/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/h.sjds.net/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/h.sjds.net/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/h.sjds.net/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/h.sjds.net/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/h.sjds.net/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/h.sjds.net/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/h.sjds.net/runtime/temp/ad153693ed39fba6d1bda2fe72512cde.php ( 12.06 KB )
  140. /yingpanguazai/ssd/ssd1/www/h.sjds.net/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.001216s ] mysql:host=127.0.0.1;port=3306;dbname=h_sjds;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001599s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000710s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000654s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001370s ]
  6. SELECT * FROM `set` [ RunTime:0.000533s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001644s ]
  8. SELECT * FROM `article` WHERE `id` = 480476 LIMIT 1 [ RunTime:0.001281s ]
  9. UPDATE `article` SET `lasttime` = 1775369701 WHERE `id` = 480476 [ RunTime:0.003470s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 65 LIMIT 1 [ RunTime:0.000633s ]
  11. SELECT * FROM `article` WHERE `id` < 480476 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000975s ]
  12. SELECT * FROM `article` WHERE `id` > 480476 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.002805s ]
  13. SELECT * FROM `article` WHERE `id` < 480476 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001806s ]
  14. SELECT * FROM `article` WHERE `id` < 480476 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.003338s ]
  15. SELECT * FROM `article` WHERE `id` < 480476 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.003803s ]
0.194484s