spring 如何从Oracle数据库中分块读取大Blob以避免Java堆空间OutOfMemoryError?

nfg76nw0  于 2023-11-16  发布在  Spring
关注(0)|答案(1)|浏览(111)

我正在处理一个Java Spring服务,它从Oracle数据库中读取一个BLOB类型的字段。这个BLOB字段包含一个zip文件,我的服务应该在响应体中将这个文件返回给客户端。
我实现了以下代码:

public void datasetDownload(String id, HttpServletResponse response) throws SQLException, IOException {
    try (Connection connection = dataSource.getConnection()) {
        String sql = "SELECT CONTENT FROM MY_TABLE_NAME WHERE ID = ?";
        try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
            preparedStatement.setString(1, id);
            try (ResultSet resultSet = preparedStatement.executeQuery()) {
                if (resultSet.next()) {
                    Blob blob = resultSet.getBlob("CONTENT");

                    try (InputStream inputStream = blob.getBinaryStream();
                         OutputStream outputStream = response.getOutputStream()) {
                        byte[] buffer = new byte[1024];

                        int bytesRead;
                        while ((bytesRead = inputStream.read(buffer)) != -1) {
                            outputStream.write(buffer, 0, bytesRead);
                        }
                    }

                    deleteRecord(id, connection);
                }
            }
        }
    }
}

字符串
但是,当我调用此服务时,我遇到以下异常:
java.lang.OutOfMemoryError:Java堆空间
我怀疑这个问题是由于从Oracle数据库阅读整个blob到内存中引起的。为了解决这个问题,我想我需要以较小的块读取blob,以避免耗尽Java堆空间,但我不知道如何做到这一点。
谢谢您的帮助!
编辑:我尝试添加一些日志:

public void datasetDownload(String id, HttpServletResponse response) throws SQLException, IOException {
    try (Connection connection = dataSource.getConnection()) {
        String sql = "SELECT CONTENT FROM MY_TABLE_NAME WHERE ID = ?";
        try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
            preparedStatement.setString(1, id);
            try (ResultSet resultSet = preparedStatement.executeQuery()) {
                if (resultSet.next()) {
                    log.info("Start downloading the BLOB");
                    log.info("Total memory available: " + (int) (Runtime.getRuntime().totalMemory() / (1024 * 1024)) + " MB");
                    log.info("Max memory available: " + (int) (Runtime.getRuntime().maxMemory() / (1024 * 1024)) + " MB");

                    try (InputStream inputStream = resultSet.getBinaryStream("CONTENT");
                         OutputStream outputStream = response.getOutputStream()) {
                        byte[] buffer = new byte[1024 * 1024];

                        int bytesRead;

                        while ((bytesRead = inputStream.read(buffer)) != -1) {
                            outputStream.write(buffer, 0, bytesRead);

                            log.info("Memory available: " + (int) (Runtime.getRuntime().freeMemory() / (1024 * 1024)) + " MB");
                        }
                    }

                    deleteRecord(id, connection);
                    log.info("BLOB download completed");
                }
            }
        }
    }
}


发生的事情是,我可以看到最后一个日志“BLOB下载完成”,然后我得到OutOfMemoryError。
此外,最后的可用内存日志是:可用内存:570 MB
编辑:实际上我找到了一个解决方案,我用ZipOutputStream压缩了blob,机器没有内存不足。但我还是不知道问题出在哪里

nxowjjhe

nxowjjhe1#

错误发生在哪一行?使用resultSet.getBinaryStream(),您可以保存一些行,它应该使用更少的内存,因为它不会首先加载blob。

相关问题