Java 在BLOB上操作需要太多时间

Java 在BLOB上操作需要太多时间,java,jvm,blob,Java,Jvm,Blob,我必须阅读一个只包含文本的BLOB列。它以前工作效率很高(在3分钟内读取100kblobs),但在不同的环境中(尽管使用相同的硬件),它需要花费大量的时间 这是我的密码:- while (rs.next()) { is = rs.getBinaryStream(3); while ((len = is.read(buffer)) != -1) { baos.write(buffer, 0, len); } is.close();

我必须阅读一个只包含文本的
BLOB
列。它以前工作效率很高(在3分钟内读取100k
blob
s),但在不同的环境中(尽管使用相同的硬件),它需要花费大量的时间

这是我的密码:-

    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
    将整个数据传输到
    字节[]
    数组中。请注意,数据甚至在内存中存在两次,一次在
    ByteArrayOutputStream
    自己的缓冲区中,然后在
    baos.toByteArray()创建并返回的副本中
  • 然后,通过
    新字符串(blobByte)
    ,将整个数组转换为一个巨大的
    字符串,其中包含整个数据的第三次复制(包括字符集转换)
  • split(“\\\\”)
    将运行整个
    字符串
    ,为分隔符之间的每个序列创建子字符串,这意味着将整个数据再次复制到子字符串中(减去分隔符字符)。到那时,内存中有四个完整数据的副本,具体取决于源的缓冲,可能是五次。此外,将创建并填充一个包含对所有这些子字符串的引用的数组
并非所有的复制操作都可以避免。但我们可以避免将整个数据存储在内存中:

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
}
但是单独处理元素,而不将所有元素都保存在内存中,是首选的方法


另一种可能的优化方法是避免多个(内部)的
模式。通过自己执行一次(\\\\\\”
调用,并将准备好的
模式
而不是
“\\\\\\\\”
字符串传递给
useDelimiter
方法来编译(\\\\\\\\\\”


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


或者您首先使用CLOB。

那么您是说完全相同的代码在不同的环境中表现不同?您是否检查了这些环境之间的区别?此外,我还建议使用jconsole的cpu采样来获得花费时间的代码部分的低开销“概要”(虽然不如正确的概要准确,但应该足以让您知道在哪里查找问题)。blob中的数据描述可能会有所帮助。似乎是由管道字符分隔的文本字段。每个水滴的平均长度是多少?每个字段有多少个字段?您可以随意更改数据库模式吗?
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
}