我有以下场景:客户端正在将文件上传到服务器。该文件通过 Hibernate 存储到 Oracle 数据库中。直到最近,对文件进行建模的实体还具有byte[]
用于对 Blob 进行建模的数组属性。我们将其更改为 an 是InputStream
因为我们不想将整个文件放在服务器的 RAM 中。在不久的将来,我们预计会有更多用户上传更大的文件,因此 RAM 确实是一个问题。
我做了一些性能测试,到目前为止的结果表明,与插入字节数组相比,流式传输大约慢了 1.8 倍。以下是为 10 次迭代插入 100MB 文件的一些结果:
Method Mean Duration [ms] SD [ms]
---------------------------------------
Byte Array 10733.0 264.0
Streaming 18089.0 913.0
客户端和服务器之间的通信是使用 Spring RMI 完成的,而二进制数据本身的流式传输是使用RMIIO完成的。由于使用了 RMIIO,我们必须编写自己的 Blobtype,从客户端解包二进制文件并将其推送到 blob 的二进制流中。您可以在下面看到此代码。
到目前为止我做过的事情:
- 为了缩小问题所在,我在服务方法中解包 RMIIO 流并将其放入字节数组中,然后将其保存到数据库中。这样做,它产生的性能与一直使用字节数组相同。所以性能消耗不是来自 RMIIO 包,而是来自服务器和数据库之间的某个地方。
- 更改缓冲区大小对性能的影响很小
任何人都可以分享一些关于为什么插入流与插入字节数组相比如此慢的见解吗?
我的配置:
- 春天 3.1.1
- RMIIO 2.0.0
- 休眠 3.6.10
- 甲骨文 11.2.0.1.0
- 用于 JDBC 10.2.0.5.0 的 Oracle 驱动程序
带有字节数组的旧实体(插入速度更快):
@Entity
@Table(name = "MYBINARYOBJECT")
public class MyBinaryObject {
@Id
private Long id;
@Lob
private byte[] bytes;
}
具有输入流的新实体(插入速度较慢):
@Entity
@Table(name = "MYBINARYSTREAMOBJECT")
public class MyBinaryStreamObject {
@Id
private Long id;
@Type(type = "MyBlobType")
private RemoteInputStream bytes;
}
我们的自定义 Blob 类型:
public class MyBlobType extends AbstractLobType {
@Override
protected Object nullSafeGetInternal(ResultSet rs, String[] names, Object owner, LobHandler lobHandler)
throws SQLException, IOException, HibernateException {
RemoteInputStreamServer server = new GZIPRemoteInputStream(lobHandler.getBlobAsBinaryStream(rs, names[0]));
return server.export();
}
@Override
protected void nullSafeSetInternal(PreparedStatement ps, int index, Object value, LobCreator lobCreator)
throws SQLException, IOException, HibernateException {
BufferedInputStream stream = null;
try {
((RemoteInputStream) value).available();
stream = new BufferedInputStream(
RemoteInputStreamClient.wrap((RemoteInputStream) value, RemoteRetry.NEVER), 16384);
lobCreator.setBlobAsBinaryStream(ps, index, stream, -1);
} catch (IllegalArgumentException e) {
throw new MyStreamingException();
}
}
}