Announcing Hibernate 6,Hibernate团队声称通过从 按名称读取到按位置读取在JDBC ResultSet中,它们获得了性能上的好处。
高负载性能测试表明,Hibernate的方法 按名称从ResultSet中读取值是其最大限制因素 扩展吞吐量。
这是否意味着他们将通话从getString(String columnLabel)
更改为getString(int columnIndex)
?
为什么这么快?
ResultSet
是一个接口,性能提升不取决于实现该接口的JDBC驱动程序吗?
收益有多大?
答案 0 :(得分:12)
作为JDBC驱动程序维护者(我承认,做了一些概括性的归纳,不一定适用于所有JDBC驱动程序),行值通常将存储在数组或列表中,因为最自然地匹配数据的方式从数据库服务器收到。
因此,按索引检索值将是最简单的。它可能像这样简单(忽略实现JDBC驱动程序的一些更原始的细节):
public Object getObject(int index) throws SQLException {
checkValidRow();
checkValidIndex(index);
return currentRow[index - 1];
}
这差不多快了。
另一方面,按列名称查找则需要更多工作。列名需要区分大小写,无论您使用小写还是大写进行规范化,还是使用TreeMap
使用不区分大小写的查找,都将产生额外的开销。
一个简单的实现可能类似于:
public Object getObject(String columnLabel) throws SQLException {
return getObject(getIndexByLabel(columnLabel));
}
private int getIndexByLabel(String columnLabel) {
Map<String, Integer> indexMap = createOrGetIndexMap();
Integer columnIndex = indexMap.get(columnLabel.toLowerCase());
if (columnIndex == null) {
throw new SQLException("Column label " + columnLabel + " does not exist in the result set");
}
return columnIndex;
}
private Map<String, Integer> createOrGetIndexMap() throws SQLException {
if (this.indexMap != null) {
return this.indexMap;
}
ResultSetMetaData rsmd = getMetaData();
Map<String, Integer> map = new HashMap<>(rsmd.getColumnCount());
// reverse loop to ensure first occurrence of a column label is retained
for (int idx = rsmd.getColumnCount(); idx > 0; idx--) {
String label = rsmd.getColumnLabel(idx).toLowerCase();
map.put(label, idx);
}
return this.indexMap = map;
}
取决于数据库的API和可用的语句元数据,可能需要其他处理才能确定查询的实际列标签。根据成本,这可能仅在实际需要时才确定(当按名称访问列标签时,或在检索结果集元数据时)。换句话说,createOrGetIndexMap()
的成本可能会很高。
但是,即使该费用可以忽略不计(例如,语句从数据库服务器准备元数据包括列标签),将列标签映射到索引然后按索引检索的开销显然也比直接按索引检索的开销高。 / p>
驱动程序甚至每次都可以遍历结果集元数据,并使用标签匹配的第一个;这可能比为具有少量列的结果集构建和访问哈希映射要便宜,但是成本仍然高于按索引直接访问。
正如我所说,这是一个笼统的概括,但是如果这种方法(按名称查找索引,然后按索引检索)不是大多数JDBC驱动程序的工作方式,我会感到惊讶,这意味着我期望按索引查找通常会更快。
快速浏览一些驱动程序,情况就是这样的:
我不知道JDBC驱动程序,按列名进行检索的代价是相等的,甚至更便宜。