第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號(hào)安全,請(qǐng)及時(shí)綁定郵箱和手機(jī)立即綁定
已解決430363個(gè)問(wèn)題,去搜搜看,總會(huì)有你想問(wèn)的

為什么按位置讀取 JDBC 結(jié)果集比按名稱快多少,速度有多快?

為什么按位置讀取 JDBC 結(jié)果集比按名稱快多少,速度有多快?

RISEBY 2022-09-22 13:51:10
在宣布休眠 6 時(shí),休眠團(tuán)隊(duì)聲稱,通過(guò)在 JDBC ResultSet 中從按名稱讀取切換到按位置讀取,他們獲得了性能優(yōu)勢(shì)。高負(fù)載性能測(cè)試表明,Hibernate 按名稱從 ResultSet 讀取值的方法是其在吞吐量中擴(kuò)展的最大限制因素。這是否意味著他們正在將調(diào)用從 getString(字符串列標(biāo)簽)更改為 getString(int 列索引)?為什么這樣更快?就像接口一樣,性能提升不取決于實(shí)現(xiàn)它的JDBC驅(qū)動(dòng)程序嗎?ResultSet收益有多大?
查看完整描述

2 回答

?
慕無(wú)忌1623718

TA貢獻(xiàn)1744條經(jīng)驗(yàn) 獲得超4個(gè)贊

作為 JDBC 驅(qū)動(dòng)程序維護(hù)者(我承認(rèn),我進(jìn)行了一些不一定適用于所有 JDBC 驅(qū)動(dòng)程序的全面概括),行值通常存儲(chǔ)在數(shù)組或列表中,因?yàn)檫@最自然地與從數(shù)據(jù)庫(kù)服務(wù)器接收數(shù)據(jù)的方式相匹配。


因此,按索引檢索值將是最簡(jiǎn)單的。它可能像這樣的東西一樣簡(jiǎn)單(忽略實(shí)現(xiàn)JDBC驅(qū)動(dòng)程序的一些更令人討厭的細(xì)節(jié)):


public Object getObject(int index) throws SQLException {

    checkValidRow();

    checkValidIndex(index);

    return currentRow[index - 1];

}

這幾乎是最快的。


另一方面,按列名查找需要更多的工作。列名需要處理為不區(qū)分大小寫(xiě)的,無(wú)論使用小寫(xiě)還是大寫(xiě)進(jìn)行規(guī)范化,或者使用不區(qū)分大小寫(xiě)的查找,這都會(huì)產(chǎn)生額外的成本。TreeMap


一個(gè)簡(jiǎn)單的實(shí)現(xiàn)可能是這樣的:


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;

}

根據(jù)數(shù)據(jù)庫(kù)的 API 和可用的語(yǔ)句元數(shù)據(jù),可能需要額外的處理來(lái)確定查詢的實(shí)際列標(biāo)簽。根據(jù)開(kāi)銷,這可能僅在實(shí)際需要時(shí)才確定(按名稱訪問(wèn)列標(biāo)簽時(shí),或檢索結(jié)果集元數(shù)據(jù)時(shí))。換句話說(shuō),成本可能相當(dāng)高。createOrGetIndexMap()


但是,即使該成本可以忽略不計(jì)(例如,從數(shù)據(jù)庫(kù)服務(wù)器準(zhǔn)備元數(shù)據(jù)的語(yǔ)句包括列標(biāo)簽),將列標(biāo)簽映射到索引然后按索引檢索的開(kāi)銷顯然高于直接按索引檢索的開(kāi)銷。


驅(qū)動(dòng)程序甚至可以每次都循環(huán)訪問(wèn)結(jié)果集元數(shù)據(jù),并使用標(biāo)簽匹配的第一個(gè);這可能比為具有少量列的結(jié)果集構(gòu)建和訪問(wèn)哈希映射更便宜,但成本仍然高于通過(guò)索引直接訪問(wèn)。


正如我所說(shuō),這是一個(gè)全面的概括,但是如果這(按名稱查找索引,然后按索引檢索)不是它在大多數(shù)JDBC驅(qū)動(dòng)程序中的工作方式,我會(huì)感到驚訝,這意味著我希望按索引查找通常會(huì)更快。


快速瀏覽一下許多驅(qū)動(dòng)程序,情況如下:

  • 火鳥(niǎo)(杰伯德,披露:我維護(hù)這個(gè)司機(jī))

  • MySQL (MySQL Connector/J)

  • PostgreSQL

  • 神諭

  • 斷續(xù)器

  • SQL Server (Microsoft JDBC Driver for SQL Server)

我不知道JDBC驅(qū)動(dòng)程序按列名檢索的成本會(huì)相等,甚至更便宜。


查看完整回答
反對(duì) 回復(fù) 2022-09-22
?
互換的青春

TA貢獻(xiàn)1797條經(jīng)驗(yàn) 獲得超6個(gè)贊

在制作 jOOQ 的早期,我考慮了這兩個(gè)選項(xiàng),即按索引或名稱訪問(wèn) JDBC 值。出于以下原因,我選擇按索引訪問(wèn)內(nèi)容:ResultSet

數(shù)據(jù)庫(kù)管理系統(tǒng)支持

并非所有 JDBC 驅(qū)動(dòng)程序?qū)嶋H上都支持按名稱訪問(wèn)列。我忘記了哪些沒(méi)有,如果它們?nèi)匀粵](méi)有,因?yàn)槲以?3年內(nèi)再也沒(méi)有接觸過(guò)JDBC API的那一部分。但有些人沒(méi)有,這對(duì)我來(lái)說(shuō)已經(jīng)是一個(gè)節(jié)目的阻礙。

名稱的語(yǔ)義

此外,在那些支持列名的列名中,列名有不同的語(yǔ)義,主要是兩個(gè),JDBC調(diào)用:

關(guān)于上述兩個(gè)的實(shí)現(xiàn)有很多歧義,盡管我認(rèn)為意圖非常明確:

  • 列名應(yīng)該產(chǎn)生列的名稱,而不管別名如何,例如 如果投影表達(dá)式是TITLEBOOK.TITLE AS X

  • 列標(biāo)簽應(yīng)該生成列的標(biāo)簽(或別名),如果沒(méi)有可用的別名,則生成名稱,例如 如果投影表達(dá)式是XBOOK.TITLE AS X

因此,名稱/標(biāo)簽的這種模糊性已經(jīng)非常令人困惑和擔(dān)憂。一般來(lái)說(shuō),ORM似乎不應(yīng)該依賴它,盡管在Hibernate的情況下,人們可以爭(zhēng)辯說(shuō)休眠控制著大多數(shù)SQL的生成,至少是為獲取實(shí)體而生成的SQL。但是,如果用戶編寫(xiě) HQL 或本機(jī) SQL 查詢,我將不愿意依賴名稱/標(biāo)簽 - 至少不要先在 中查找內(nèi)容。ResultSetMetaData

歧義

在SQL中,在頂層有不明確的列名是完全可以的,例如:

SELECT id, id, not_the_id AS id
FROM book

這是完全有效的 SQL。不能將此查詢嵌套為派生表,因?yàn)椴辉试S出現(xiàn)多義詞,但在頂級(jí)中可以?,F(xiàn)在,您將如何處理頂層的重復(fù)標(biāo)簽?您無(wú)法確定在按名稱訪問(wèn)事物時(shí)會(huì)得到哪一個(gè)。前兩個(gè)可能相同,但第三個(gè)非常不同。SELECTID

清楚地區(qū)分列的唯一方法是按索引,索引是唯一的:, , 。123

性能

我當(dāng)時(shí)也嘗試過(guò)表演。我不再有基準(zhǔn)測(cè)試結(jié)果,但很容易快速編寫(xiě)另一個(gè)基準(zhǔn)測(cè)試。在下面的基準(zhǔn)測(cè)試中,我正在對(duì) H2 內(nèi)存中實(shí)例運(yùn)行一個(gè)簡(jiǎn)單的查詢,并使用訪問(wèn)內(nèi)容:ResultSet

  • 按索引

  • 按名稱

結(jié)果令人震驚:

Benchmark                            Mode  Cnt        Score       Error  Units

JDBCResultSetBenchmark.indexAccess  thrpt    7  1130734.076 ±  9035.404  ops/s

JDBCResultSetBenchmark.nameAccess   thrpt    7   600540.553 ± 13217.954  ops/s

盡管基準(zhǔn)測(cè)試在每次調(diào)用時(shí)運(yùn)行整個(gè)查詢,但按索引訪問(wèn)的速度幾乎是其兩倍!你可以看看H2的代碼,它是開(kāi)源的。它執(zhí)行以下操作(版本 2.1.212):


private int getColumnIndex(String columnLabel) {

    checkClosed();

    if (columnLabel == null) {

        throw DbException.getInvalidValueException("columnLabel", null);

    }

    if (columnCount >= 3) {

        // use a hash table if more than 2 columns

        if (columnLabelMap == null) {

            HashMap<String, Integer> map = new HashMap<>();

            // [ ... ]


            columnLabelMap = map;

            if (preparedStatement != null) {

                preparedStatement.setCachedColumnLabelMap(columnLabelMap);

            }

        }

        Integer index = columnLabelMap.get(StringUtils.toUpperEnglish(columnLabel));

        if (index == null) {

            throw DbException.get(ErrorCode.COLUMN_NOT_FOUND_1, columnLabel);

        }

        return index + 1;

    }

    // [ ... ]

所以,有一個(gè)帶有上寫(xiě)字母的哈希圖,每次查找也執(zhí)行上寫(xiě)。至少,它將映射緩存在預(yù)準(zhǔn)備語(yǔ)句中,因此:

  • 您可以在每行上重復(fù)使用它

  • 您可以在語(yǔ)句的多次執(zhí)行中重用它(至少這是我解釋代碼的方式)

因此,對(duì)于非常大的結(jié)果集,它可能不再那么重要,但對(duì)于較小的結(jié)果集,它肯定很重要。

關(guān)于管理權(quán)的結(jié)論

像休眠或jOOQ這樣的ORM可以控制大量的SQL和結(jié)果集。它確切地知道哪個(gè)列在什么位置,這項(xiàng)工作在生成SQL查詢時(shí)已經(jīng)完成。因此,當(dāng)結(jié)果集從數(shù)據(jù)庫(kù)服務(wù)器返回時(shí),絕對(duì)沒(méi)有理由進(jìn)一步依賴列名。每個(gè)值都將位于預(yù)期位置。

在休眠中,使用列名一定是一些歷史性的事情。這可能也是為什么他們?cè)?jīng)生成這些不那么可讀的列別名,以確保每個(gè)別名都是不明確的。

這似乎是一個(gè)明顯的改進(jìn),無(wú)論在現(xiàn)實(shí)世界(非基準(zhǔn))查詢中的實(shí)際收益如何。即使改進(jìn)只有 2%,也是值得的,因?yàn)樗鼤?huì)影響每個(gè)基于 Hibernate 的應(yīng)用程序執(zhí)行的每個(gè)查詢。

下面的基準(zhǔn)代碼,用于復(fù)制

package org.jooq.test.benchmarks.local;


import java.io.*;

import java.sql.*;

import java.util.Properties;


import org.openjdk.jmh.annotations.*;

import org.openjdk.jmh.infra.*;


@Fork(value = 1)

@Warmup(iterations = 3, time = 3)

@Measurement(iterations = 7, time = 3)

public class JDBCResultSetBenchmark {


    @State(Scope.Benchmark)

    public static class BenchmarkState {


        Connection connection;


        @Setup(Level.Trial)

        public void setup() throws Exception {

            try (InputStream is = BenchmarkState.class.getResourceAsStream("/config.properties")) {

                Properties p = new Properties();

                p.load(is);

                connection = DriverManager.getConnection(

                    p.getProperty("db.url"),

                    p.getProperty("db.username"),

                    p.getProperty("db.password")

                );

            }

        }


        @TearDown(Level.Trial)

        public void teardown() throws Exception {

            connection.close();

        }

    }


    @FunctionalInterface

    interface ThrowingConsumer<T> {

        void accept(T t) throws SQLException;

    }


    private void run(BenchmarkState state, ThrowingConsumer<ResultSet> c) throws SQLException {

        try (Statement s = state.connection.createStatement();

            ResultSet rs = s.executeQuery("select c as c1, c as c2, c as c3, c as c4 from system_range(1, 10) as t(c);")) {

            c.accept(rs);

        }

    }


    @Benchmark

    public void indexAccess(Blackhole blackhole, BenchmarkState state) throws SQLException {

        run(state, rs -> {

            while (rs.next()) {

                blackhole.consume(rs.getInt(1));

                blackhole.consume(rs.getInt(2));

                blackhole.consume(rs.getInt(3));

                blackhole.consume(rs.getInt(4));

            }

        });

    }


    @Benchmark

    public void nameAccess(Blackhole blackhole, BenchmarkState state) throws SQLException {

        run(state, rs -> {

            while (rs.next()) {

                blackhole.consume(rs.getInt("C1"));

                blackhole.consume(rs.getInt("C2"));

                blackhole.consume(rs.getInt("C3"));

                blackhole.consume(rs.getInt("C4"));

            }

        });

    }

}


查看完整回答
反對(duì) 回復(fù) 2022-09-22
  • 2 回答
  • 0 關(guān)注
  • 103 瀏覽
慕課專欄
更多

添加回答

舉報(bào)

0/150
提交
取消
微信客服

購(gòu)課補(bǔ)貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動(dòng)學(xué)習(xí)伙伴

公眾號(hào)

掃描二維碼
關(guān)注慕課網(wǎng)微信公眾號(hào)