Designing a Bounded FIFO Export Queue for Large MySQL Data Exports in Java Spring
To prevent performance degradation during large MySQL data exports, this article presents a Java Spring implementation of a bounded FIFO export queue, detailing the ExportQueue class, abstract export handling with EasyExcel, concrete service and controller code, and test results demonstrating queue limits and concurrency considerations.
This article addresses the performance impact of exporting large volumes of MySQL data by introducing a bounded FIFO queue that serializes export tasks, limits concurrent operations, and integrates with EasyExcel for efficient file generation.
Business Requirements
The system must handle full‑data exports that involve heavy MySQL I/O and file stream operations, which can strain server resources when multiple users export simultaneously. A fixed‑size queue is proposed to serialize export requests and record export metadata.
Domain Model
ExportQueue : Maintains a fixed‑size queue, provides methods to add users, retrieve the next user, and blocks when the queue is full.
UserInfo : Represents the user who initiates an export.
Export : Defines the export method, supports asynchronous execution, and allows users to download generated files.
ExportQueue Implementation
package com.example.system.config;
import com.example.system.api.domain.ExportUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.LinkedList;
@Slf4j
@Component
public class ExportQueue {
private final int MAX_CAPACITY = 10; // queue max size
private LinkedList
queue;
public ExportQueue(LinkedList
queue) {
this.queue = new LinkedList<>();
}
/**
* Add a user to the queue
*/
public synchronized LinkedList
add(ExportUser sysUser) {
while (queue.size() >= MAX_CAPACITY) {
try {
log.info("当前排队人已满,请等待");
wait();
} catch (InterruptedException e) {
e.getMessage();
}
}
queue.add(sysUser);
log.info("目前导出队列排队人数:" + queue.size());
notifyAll();
return queue;
}
/**
* Get the next user from the queue
*/
public synchronized ExportUser getNextSysUser() {
while (queue.isEmpty()) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
ExportUser sysUser = queue.remove();
notifyAll(); // wake up waiting threads
return sysUser;
}
}AbstractExport (EasyExcel Integration)
package com.example.system.config;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.PageUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.example.system.api.domain.ExportUser;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;
@Slf4j
public abstract class AbstractExport
{
public abstract void export(ExportUser sysUser) throws InterruptedException;
/**
* Export data to Excel
*/
public void export(HttpServletResponse response, int pageSize, T t, Class
k, String fileName) throws Exception {
ExcelWriter writer = null;
try {
writer = getExcelWriter(response, fileName);
int total = this.countExport(t);
int loopCount = PageUtil.totalPage(total, pageSize);
BeanUtil.setProperty(t, "pageSize", pageSize);
for (int i = 0; i < loopCount; i++) {
BeanUtil.setProperty(t, "pageNum", PageUtil.getStart(i + 1, pageSize));
List
kList = this.getExportDetail(t);
WriteSheet writeSheet = EasyExcel.writerSheet(fileName).head(k).build();
writer.write(kList, writeSheet);
}
} catch (Exception e) {
String msg = "导出" + fileName + "异常";
log.error(msg, e);
throw new Exception(msg + e);
} finally {
if (writer != null) {
writer.finish();
}
}
}
public ExcelWriter getExcelWriter(HttpServletResponse response, String fileName) throws IOException {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
String fileNameUtf = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileNameUtf + ".xlsx");
return EasyExcel.write(response.getOutputStream()).build();
}
public abstract void complexFillWithTable(T t, String fileName, HttpServletResponse response);
public abstract int countExport(T t);
public abstract List
getExportDetail(T t);
}ExportImpl (Concrete Service)
package com.example.system.service.impl;
import com.alibaba.excel.ExcelWriter;
import com.example.system.api.domain.ExportUser;
import com.example.system.config.AbstractExport;
import com.example.system.config.ExportQueue;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
@Service
@Slf4j
public class ExportImpl extends AbstractExport {
@Autowired
private ExportQueue exportQueue;
@Override
public void export(ExportUser sysUser) throws InterruptedException {
log.info("导出文件方法执行~~~~~~~~~");
LinkedList
queue = exportQueue.add(sysUser);
log.info("导出队列:" + queue);
Thread.sleep(20000); // simulate export processing time
ExportUser nextSysUser = exportQueue.getNextSysUser();
log.info("移除后获取下一个排队的用户: " + nextSysUser.getUserName());
}
@Override
public void export(HttpServletResponse response, int pageSize, Object o, Class k, String fileName) throws Exception {
super.export(response, pageSize, o, k, fileName);
}
@Override
public ExcelWriter getExcelWriter(HttpServletResponse response, String fileName) throws IOException {
return super.getExcelWriter(response, fileName);
}
@Override
public void complexFillWithTable(Object o, String fileName, HttpServletResponse response) {
// not implemented
}
@Override
public int countExport(Object o) {
return 0;
}
@Override
public List getExportDetail(Object o) {
return null;
}
}Test Controller
package com.example.system.controller;
import com.example.system.api.domain.ExportUser;
import com.example.system.service.impl.ExportImpl;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/export")
@Slf4j
public class ExportController {
@Autowired
private ExportImpl export;
@PostMapping("/exportFile")
public void exportFile() {
new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
Thread thread1 = Thread.currentThread();
ExportUser sysUser = new ExportUser();
sysUser.setUserName(thread1.getName());
export.export(sysUser);
}
}).start();
}
}Test Results
The queue size is limited to 10. When the limit is reached, additional export requests are blocked until a slot becomes free. Logs and screenshots show that the first request finishes after ~20 seconds, the next user is then dequeued and processed, confirming FIFO behavior.
Conclusion
While the presented implementation demonstrates a workable FIFO export queue, it does not yet cover file schema design, OSS storage, download endpoints, or high‑concurrency edge cases. Alternative approaches such as Redis‑based queues are also viable.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.