java - 对 BLOB 进行操作需要太多时间

标签 java jvm blob

我必须读取仅包含文本的BLOB 列。之前它的工作效率非常高(在 3 分钟内读取 100k blob),但在不同的环境中,尽管使用相同的硬件,却花费了大量的时间。

这是我的代码:-

    while (rs.next()) {
    is = rs.getBinaryStream(3);

    while ((len = is.read(buffer)) != -1) {
        baos.write(buffer, 0, len);
    }
    is.close();
    blobByte = baos.toByteArray();
    baos.close();
    String blob = new String(blobByte);
    String msisdn = rs.getString(2);

    blobData = blob.split("\\|");
            //some operations
            }

我每隔 5 秒获取一次 jstack,发现应用程序总是在这一行:-

    blobData = blob.split("\\|");

有时是:-

    new String(blobByte);

我的java选项:-

     -ms10g -mx12g -XX:NewSize=1g -XX:MaxNewSize=1g

我的代码的某些部分是否未优化?或者有没有一种非常有效的方法来读取BLOB

最佳答案

您将获得 InputStream BLOB 能够避免将整个 BLOB 数据存储在内存中。但随后,你却做了完全相反的事情

  • 您使用 ByteArrayOutputStream将整个数据传输到 byte[]大批。请注意,数据甚至在内存中存在两次,一次位于 ByteArrayOutputStream 内。自己的缓冲区,然后在 baos.toByteArray() 创建并返回的副本中
  • 然后,将整个数组转换为可能巨大的 String通过new String(blobByte) ,承担整个数据的第3次复制(包括字符集转换)。
  • split("\\|")将运行整个 String ,为分隔符之间的每个序列创建子字符串,这意味着将整个数据再次复制到子字符串中(减去分隔符字符),届时,内存中将拥有整个数据的四个副本,具体取决于源的缓冲,它可能是五次。此外,还会创建并填充一个包含对所有这些子字符串的引用的数组

并非所有复制操作都可以避免。但我们可以避免将整个数据存储在内存中:

try(Scanner s = new Scanner(is).useDelimiter("\\|")) {
    while(s.hasNext()) {
        String next = s.next();
        System.out.println(next);// replace with actual processing
    }
}

当您能够单独处理项目而不保留对前一个项目的引用时,这些字符串可能会被垃圾收集,在最好的情况下会进行少量收集。

即使 String[]处理时需要包含所有元素的数组,这使得整个数据的一份副本(以单个字符串的形式)不可避免,您可以避免所有其他副本:

try(Scanner s = new Scanner(is).useDelimiter("\\|")) {
    List<String> list = new ArrayList<>();
    while(s.hasNext()) list.add(s.next());
    System.out.println(list);// replace with actual processing as List
    String[] array = list.toArray(new String[0]); // when an array really is required
}

从 Java 9 开始,您可以使用

try(Scanner s = new Scanner(is).useDelimiter("\\|")) {
    List<String> list = s.tokens().collect(Collectors.toList());
    System.out.println(list); // replace with actual processing as List
}

try(Scanner s = new Scanner(is).useDelimiter("\\|")) {
    String[] array = s.tokens().toArray(String[]::new);
    System.out.println(Arrays.toString(array)); // replace with actual processing
}

但是单独处理元素,而不将所有元素保存在内存中,是首选方法。

<小时/>

另一种可能的优化是避免多个(内部)Pattern.compile("\\|")通过自己执行一次并传递准备好的 Pattern 来调用而不是 "\\|"字符串到 useDelimiter方法。

<小时/>

请注意,所有这些示例都使用系统的默认字符集编码,就像您的原始代码一样。由于运行代码的环境的默认字符集不一定与数据库相同,因此您应该明确,即使用 new Scanner(is, charset) ,就像您应该使用 new String(blobByte, charset) 一样在你的原始代码中,而不是 new String(blobByte) .

或者您首先使用 CLOB。

关于java - 对 BLOB 进行操作需要太多时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57408541/

相关文章:

Java DNS 缓存查看器

javascript - 问题 - Base 64 PDF 字符串未在 IE 11 中呈现

angularjs - 使用 AngularJS 从服务器下载并保存文件

java - 如何在 64 位 JVM 上运行 TeamCity

java - Java 中的内部 block 如何访问应该超出范围的局部变量? (JVM 如何处理 Java 中的最终局部变量)

java - 从 blob 反序列化 java 对象

Java - 将图像转换为 Base64

java - 如何在 Java 中对 HashMap 进行排序

java - Type 局部变量text的值不使用

jvm - 为什么 JVM 被认为是如此出色的软件工程?