Backend Development 11 min read

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.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
Designing a Bounded FIFO Export Queue for Large MySQL Data Exports in Java Spring

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.

JavaconcurrencySpringEasyExcelQueueExport
Java Architect Essentials
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.