Java 为什么按位置读取JDBC结果集比按名称读取快,速度有多快?

Java 为什么按位置读取JDBC结果集比按名称读取快,速度有多快?,java,performance,hibernate,jdbc,hibernate-6.x,Java,Performance,Hibernate,Jdbc,Hibernate 6.x,Hibernate团队声称,通过从 在JDBC中,按名称读取到按位置读取,这样可以获得性能优势 高负载性能测试表明Hibernate的 按名称从ResultSet读取值是其最大的限制因素 在缩放中通过放置 这是否意味着他们正在将电话从转接到 为什么这样更快 由于ResultSet是一个接口,性能的提高不取决于实现它的JDBC驱动程序吗 收益有多大?作为JDBC驱动程序维护者(我承认,做了一些不一定适用于所有JDBC驱动程序的全面概括),行值通常存储在数组或列表中,因为这与从数据库服务器接收数据的

Hibernate团队声称,通过从 在JDBC中,按名称读取到按位置读取,这样可以获得性能优势

高负载性能测试表明Hibernate的 按名称从ResultSet读取值是其最大的限制因素 在缩放中通过放置

这是否意味着他们正在将电话从转接到

为什么这样更快

由于
ResultSet
是一个接口,性能的提高不取决于实现它的JDBC驱动程序吗


收益有多大?

作为JDBC驱动程序维护者(我承认,做了一些不一定适用于所有JDBC驱动程序的全面概括),行值通常存储在数组或列表中,因为这与从数据库服务器接收数据的方式最自然地匹配

因此,通过索引检索值将是最简单的。它可能很简单,比如(忽略实现JDBC驱动程序的一些更糟糕的细节):

这是最快的速度了

另一方面,按列名查找要做更多的工作。列名需要不区分大小写,无论是使用小写或大写进行规范化,还是使用
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;
}
public对象getObject(String-columnLabel)抛出SQLException{
返回getObject(getIndexByLabel(columnLabel));
}
私有int getIndexByLabel(字符串列标签){
Map indexMap=createOrGetIndexMap();
整型columnIndex=indexMap.get(columnLabel.toLowerCase());
if(columnIndex==null){
抛出新的SQLException(“结果集中不存在列标签“+columnLabel+”);
}
返回列索引;
}
私有映射createOrGetIndexMap()引发SQLException{
如果(this.indexMap!=null){
返回此.indexMap;
}
ResultSetMetaData rsmd=getMetaData();
Map Map=newhashmap(rsmd.getColumnCount());
//反向循环,以确保保留第一次出现的列标签
对于(int idx=rsmd.getColumnCount();idx>0;idx--){
String label=rsmd.getColumnLabel(idx.toLowerCase();
地图放置(标签,idx);
}
返回this.indexMap=map;
}
根据数据库的API和可用的语句元数据,可能需要额外的处理来确定查询的实际列标签。根据成本的不同,这可能仅在实际需要时确定(按名称访问列标签时,或检索结果集元数据时)。换句话说,
createOrGetIndexMap()
的成本可能相当高

但是,即使成本可以忽略不计(例如,从数据库服务器准备元数据的语句包含列标签),将列标签映射到索引然后按索引检索的开销也明显高于直接按索引检索的开销

驱动程序甚至可以每次循环遍历结果集元数据,并使用标签匹配的第一个元数据;这可能比为具有少量列的结果集构建和访问哈希映射要便宜,但成本仍然高于通过索引直接访问

正如我所说的,这是一个全面的概括,但是如果这(按名称查找索引,然后按索引检索)不是大多数JDBC驱动程序的工作方式,我会感到惊讶,这意味着我希望按索引查找通常会更快

快速查看一些驱动程序,如下所示:

  • Firebird(Jaybird,披露:我维护这个驱动程序)
  • MySQL(MySQL连接器/J)
  • PostgreSQL
  • 神谕
  • HSQLDB
  • SQL Server(用于SQL Server的Microsoft JDBC驱动程序)

我不知道JDBC驱动程序中按列名检索的成本相当,甚至更便宜。

很好的解释。我想知道是否使用请求的列名作为映射中的键,而不是使用小写的列名预先进行映射(这样就可以在每次调用getXxx()方法时延迟地构造映射,添加一个条目)对于大的结果集不会更快:它可以避免在结果集的每一行重复使用相同的键的小写。@jbnize不可能。事实上,这就是我们要做的,但我不想把这里的东西弄得乱七八糟:)
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;
}