国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

mysql JDBC的三種查詢(普通、流式、游標(biāo))

這篇具有很好參考價(jià)值的文章主要介紹了mysql JDBC的三種查詢(普通、流式、游標(biāo))。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

使用JDBC向mysql發(fā)送查詢時(shí),有三種方式:

  • 常規(guī)查詢:JDBC驅(qū)動(dòng)會(huì)阻塞的一次性讀取全部查詢的數(shù)據(jù)到 JVM 內(nèi)存中,或者分頁讀取
  • 流式查詢:每次執(zhí)行rs.next時(shí)會(huì)判斷數(shù)據(jù)是否需要從mysql服務(wù)器獲取,如果需要觸發(fā)讀取一批數(shù)據(jù)(可能n行)加載到 JVM 內(nèi)存進(jìn)行業(yè)務(wù)處理
  • 游標(biāo)查詢:通過 fetchSize 參數(shù),控制每次從mysql服務(wù)器一次讀取多少行數(shù)據(jù)。

1、常規(guī)查詢

public static void normalQuery() throws SQLException {
    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3307/test?useSSL=false", "root", "123456");
    PreparedStatement statement = connection.prepareStatement(sql);
    //statement.setFetchSize(100); //不起作用
    ResultSet resultSet = statement.executeQuery();
    
    while(resultSet.next()){
        System.out.println(resultSet.getString(2));
    }
    resultSet.close();
    statement.close();
    connection.close();
}

1)說明:

  1. 第四行設(shè)置featchSize不起作用。
  2. 第五行statement.executeQuery()執(zhí)行查詢會(huì)阻塞,因?yàn)樾枰鹊剿袛?shù)據(jù)返回并放到內(nèi)存中;接下來每次執(zhí)行resultSet.next()方法會(huì)從內(nèi)存中獲取數(shù)據(jù)。

2)將jvm內(nèi)存設(shè)置較?。?Xms16m -Xmx16m),對于大數(shù)據(jù)的查詢會(huì)產(chǎn)生OOM:

數(shù)據(jù)庫流式查詢,java,mysql,jvm,java

為了避免OOM,通常我們會(huì)使用分頁查詢,或者下面的兩種方式。

2、流式查詢

public static void streamQuery() throws Exception { 
    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3307/test?useSSL=false", "root", "123456");
    PreparedStatement statement = connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);    
    statement.setFetchSize(Integer.MIN_VALUE); 
    //或者通過 com.mysql.jdbc.StatementImpl
    ((StatementImpl) statement).enableStreamingResults();
    
    ResultSet rs = statement.executeQuery();
    while (rs.next()) {
        System.out.println(rs.getString(2));
    }
    rs.close();
    statement.close();
    connection.close();
}

2.1)流式查詢的條件:

隨著大數(shù)據(jù)的到來,對于百萬、千萬的數(shù)據(jù)使用流式查詢可以有效避免OOM。在執(zhí)行statement.executeQuery()時(shí)不會(huì)從TCP響應(yīng)流中讀取完所有數(shù)據(jù),當(dāng)下面執(zhí)行rs.next()時(shí)會(huì)按照需要從TCP響應(yīng)流中讀取部分?jǐn)?shù)據(jù)。

  1. 創(chuàng)建Statement的時(shí)候需要制定ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY
  2. 設(shè)置fetchSize位Integer.MIN_VALUE

或者通過com.mysql.jdbc.StatementImpl的enableStreamingResults()方法設(shè)置。二者是一致的??磎ysql的jdbc(com.mysql.jdbc.StatementImpl)源碼:

數(shù)據(jù)庫流式查詢,java,mysql,jvm,java

2.2)流式查詢原理:

1)基本概念

我們要知道jdbc客戶端和mysql服務(wù)器之間是通過TCP建立的通信,使用mysql協(xié)議進(jìn)行傳輸數(shù)據(jù)。首先聲明一個(gè)概念:在三次握手建立了TCP連接后,就可以在這個(gè)通道上進(jìn)行通信了,直到關(guān)閉該連接。

在 TCP 中發(fā)送端和接收端**可以是客戶端/服務(wù)端,也可以是服務(wù)器/客戶端**,通信的雙方在任意時(shí)刻既可以是接收數(shù)據(jù)也可以是發(fā)送數(shù)據(jù)(全雙工)。在通信中,收發(fā)雙方都不保持記錄的邊界,所以需要按照一定的協(xié)議進(jìn)行表示。在mysql中會(huì)按照mysql協(xié)議來進(jìn)行交互。

有了上面的概念,我們重新來定義這兩種查詢:

在執(zhí)行st.executeQuery()時(shí),jdbc驅(qū)動(dòng)會(huì)通過connection對象和mysql服務(wù)器建立TCP連接,同時(shí)在這個(gè)鏈接通道中發(fā)送sql命令,并接受返回。二者的區(qū)別是:

  1. 普通查詢:也叫批量查詢,jdbc客戶端會(huì)阻塞的一次性從TCP通道中讀取完mysql服務(wù)的返回?cái)?shù)據(jù);
  2. 流式查詢:分批的從TCP通道中讀取mysql服務(wù)返回的數(shù)據(jù),每次讀取的數(shù)據(jù)量并不是一行(通常是一個(gè)package大小),jdbc客戶端在調(diào)用rs.next()方法時(shí)會(huì)根據(jù)需要從TCP流通道中讀取部分?jǐn)?shù)據(jù)。(并不是每次讀區(qū)一行數(shù)據(jù),網(wǎng)上說的幾乎都是錯(cuò)的?。?/li>

2)源碼查看:

從statement.executeQuery()方法跟進(jìn)去,主要的調(diào)用連如下:

protected ResultSetInternalMethods executeInternal(int maxRowsToRetrieve, Buffer sendPacket, boolean createStreamingResultSet, boolean queryIsSelectOnly,
            Field[] metadataFromCache, boolean isBatch) throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            MySQLConnection locallyScopedConnection = this.connection;
            rs = locallyScopedConnection.execSQL(this, null, maxRowsToRetrieve, sendPacket, this.resultSetType, this.resultSetConcurrency,
                            createStreamingResultSet, this.currentCatalog, metadataFromCache, isBatch);
            return rs;
        }
public ResultSetInternalMethods execSQL(StatementImpl callingStatement, String sql, int maxRows, Buffer packet, int resultSetType, int resultSetConcurrency,
            boolean streamResults, String catalog, Field[] cachedMetadata, boolean isBatch) throws SQLException {
        synchronized (getConnectionMutex()) {
            return this.io.sqlQueryDirect(callingStatement, null, null, packet, maxRows, resultSetType, resultSetConcurrency, streamResults, catalog,
                        cachedMetadata);
        }
}
final ResultSetInternalMethods sqlQueryDirect(StatementImpl callingStatement, String query, String characterEncoding, Buffer queryPacket, int maxRows,
            int resultSetType, int resultSetConcurrency, boolean streamResults, String catalog, Field[] cachedMetadata) throws Exception {
        Buffer resultPacket = sendCommand(MysqlDefs.QUERY, null, queryPacket, false, null, 0);
        ResultSetInternalMethods rs = readAllResults(callingStatement, maxRows, resultSetType, resultSetConcurrency, streamResults, catalog, resultPacket,
                    false, -1L, cachedMetadata);
        return rs;
}
ResultSetImpl readAllResults(StatementImpl callingStatement, int maxRows, int resultSetType, int resultSetConcurrency, boolean streamResults,
            String catalog, Buffer resultPacket, boolean isBinaryEncoded, long preSentColumnCount, Field[] metadataFromCache) throws SQLException {
        ResultSetImpl topLevelResultSet = readResultsForQueryOrUpdate(callingStatement, maxRows, resultSetType, resultSetConcurrency, streamResults, catalog,
                resultPacket, isBinaryEncoded, preSentColumnCount, metadataFromCache);
        return topLevelResultSet;
}
protected final ResultSetImpl readResultsForQueryOrUpdate(StatementImpl callingStatement, int maxRows, int resultSetType, int resultSetConcurrency,
            boolean streamResults, String catalog, Buffer resultPacket, boolean isBinaryEncoded, long preSentColumnCount, Field[] metadataFromCache) throws SQLException {
            com.mysql.jdbc.ResultSetImpl results = getResultSet(callingStatement, columnCount, maxRows, resultSetType, resultSetConcurrency, streamResults,
                    catalog, isBinaryEncoded, metadataFromCache);
            return results;
        }
}
protected ResultSetImpl getResultSet(StatementImpl callingStatement, long columnCount, int maxRows, int resultSetType, int resultSetConcurrency,
            boolean streamResults, String catalog, boolean isBinaryEncoded, Field[] metadataFromCache) throws SQLException {
        Buffer packet; // The packet from the server
        RowData rowData = null;
        if (!streamResults) {
            rowData = readSingleRowSet(columnCount, maxRows, resultSetConcurrency, isBinaryEncoded, (metadataFromCache == null) ? fields : metadataFromCache);
        } else {
            rowData = new RowDataDynamic(this, (int) columnCount, (metadataFromCache == null) ? fields : metadataFromCache, isBinaryEncoded);
            this.streamingData = rowData;
        }
        ResultSetImpl rs = buildResultSetWithRows(callingStatement, catalog, (metadataFromCache == null) ? fields : metadataFromCache, rowData, resultSetType,
                resultSetConcurrency, isBinaryEncoded);
        return rs;
}

說明:

  1. sqlQueryDirect()方法中的sendCommand會(huì)通過io發(fā)送sql命令請求到mysql服務(wù)器,并獲取返回流mysqlOutput
  2. getResultSet()方法會(huì)判斷是否是流式查詢還是批量查詢。MySQL驅(qū)動(dòng)會(huì)根據(jù)不同的參數(shù)設(shè)置選擇對應(yīng)的ResultSet實(shí)現(xiàn)類,分別對應(yīng)三種查詢方式:
  • RowDataStatic 靜態(tài)結(jié)果集,默認(rèn)的查詢方式,普通查詢
  • RowDataDynamic 動(dòng)態(tài)結(jié)果集,流式查詢
  • RowDataCursor 游標(biāo)結(jié)果集,服務(wù)器端基于游標(biāo)查詢

看上述代碼(41行),對于批量查詢:readSingleRowSet方法會(huì)循環(huán)掉用nextRow方法獲取所有數(shù)據(jù),然后放到j(luò)vm內(nèi)存的rows中:

數(shù)據(jù)庫流式查詢,java,mysql,jvm,java

對于流式查詢:直接創(chuàng)建RowDataDynamic對象返回。后面在掉用rs.next()獲取數(shù)據(jù)時(shí)會(huì)根據(jù)需要從mysqlOutput流中讀取數(shù)據(jù)。

2.3)流式查詢的坑:

public static void streamQuery2() throws Exception { 
    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3307/test?useSSL=false", "root", "123456");
    //statement1
    PreparedStatement statement = connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);    
    statement.setFetchSize(Integer.MIN_VALUE); 
    ResultSet rs = statement.executeQuery();
    if (rs.next()) {
        System.out.println(rs.getString(2));
    }
    //statement2
    PreparedStatement statement2 = connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);    
    statement2.setFetchSize(Integer.MIN_VALUE); 
    ResultSet rs2 = statement2.executeQuery();
    if (rs2.next()) {
        System.out.println(rs2.getString(2));
    }
//      rs.close();
//      statement.close();
//      connection.close();
}

執(zhí)行結(jié)果:

test1
java.sql.SQLException: Streaming result set com.mysql.jdbc.RowDataDynamic@45c8e616 is still active. No statements may be issued when any streaming result sets are open and in use on a given connection. Ensure that you have called .close() on any active streaming result sets before attempting more queries.
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:869)
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:865)
	at com.mysql.jdbc.MysqlIO.checkForOutstandingStreamingData(MysqlIO.java:3217)
	at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2453)
	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2683)
	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2482)
	at com.mysql.jdbc.StatementImpl.executeSimpleNonQuery(StatementImpl.java:1465)
	at com.mysql.jdbc.StatementImpl.setupStreamingTimeout(StatementImpl.java:726)
	at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:1939)
	at com.tencent.clue_disp_api.MysqlTest.streamQuery2(MysqlTest.java:79)
	at com.tencent.clue_disp_api.MysqlTest.main(MysqlTest.java:25)

MySQL Connector/J 5.1 Developer Guide中原文:

There are some caveats with this approach. You must read all of the rows in the result set (or close it) before you can issue any other queries on the connection, or an exception will be thrown. 也就是說當(dāng)通過流式查詢獲取一個(gè)ResultSet后,通過next迭代出所有元素之前或者調(diào)用close關(guān)閉它之前,不能使用同一個(gè)數(shù)據(jù)庫連接去發(fā)起另外一個(gè)查詢,否者拋出異常(第一次調(diào)用的正常,第二次的拋出異常)。

2.4)抓包驗(yàn)證:

數(shù)據(jù)庫流式查詢,java,mysql,jvm,java

查看3307 > 62169的包可以發(fā)現(xiàn),ack都是1324,證明都是針對當(dāng)時(shí)sql請求的返回?cái)?shù)據(jù)。

數(shù)據(jù)庫流式查詢,java,mysql,jvm,java

3、游標(biāo)查詢

public static void cursorQuery() throws Exception {
    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3307/test?useSSL=false&useCursorFetch=true", "root", "123456");
    ((JDBC4Connection) connection).setUseCursorFetch(true); //com.mysql.jdbc.JDBC4Connection
    Statement statement = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);    
    statement.setFetchSize(2);    
    ResultSet rs = statement.executeQuery(sql);    
    while (rs.next()) {
        System.out.println(rs.getString(2));
        Thread.sleep(5000);
    }
    
    rs.close();
    statement.close();
    connection.close();
}

1)說明:

  • 在連接參數(shù)中需要拼接useCursorFetch=true;
  • 創(chuàng)建Statement時(shí)需要設(shè)置ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY
  • 設(shè)置fetchSize控制每一次獲取多少條數(shù)據(jù)

2)抓包驗(yàn)證:

通過wireshark抓包,可以看到每執(zhí)行一次rs.next() 就會(huì)向mysql服務(wù)發(fā)送一個(gè)請求,同時(shí)mysql服務(wù)返回兩條數(shù)據(jù):

數(shù)據(jù)庫流式查詢,java,mysql,jvm,java

3)游標(biāo)查詢需要注意的點(diǎn):

由于MySQL方不知道客戶端什么時(shí)候?qū)?shù)據(jù)消費(fèi)完,而自身的對應(yīng)表可能會(huì)有DML寫入操作,此時(shí)MySQL需要建立一個(gè)臨時(shí)空間來存放需要拿走的數(shù)據(jù)。因此對于當(dāng)你啟用useCursorFetch讀取大表的時(shí)候會(huì)看到MySQL上的幾個(gè)現(xiàn)象:文章來源地址http://www.zghlxwxcb.cn/news/detail-717292.html

  1. IOPS飆升 (IOPS (Input/Output Per Second):磁盤每秒的讀寫次數(shù))
  2. 磁盤空間飆升
  3. 客戶端JDBC發(fā)起SQL后,長時(shí)間等待SQL響應(yīng)數(shù)據(jù),這段時(shí)間就是服務(wù)端在準(zhǔn)備數(shù)據(jù)
  4. 在數(shù)據(jù)準(zhǔn)備完成后,開始傳輸數(shù)據(jù)的階段,網(wǎng)絡(luò)響應(yīng)開始飆升,IOPS由“讀寫”轉(zhuǎn)變?yōu)椤白x取”。
  5. CPU和內(nèi)存會(huì)有一定比例的上升

到了這里,關(guān)于mysql JDBC的三種查詢(普通、流式、游標(biāo))的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • MySQL大數(shù)據(jù)表處理的三種方案,查詢效率嘎嘎高

    MySQL大數(shù)據(jù)表處理的三種方案,查詢效率嘎嘎高

    場景 當(dāng)我們業(yè)務(wù)數(shù)據(jù)庫表中的數(shù)據(jù)越來越多,如果你也和我遇到了以下類似場景,那讓我們一起來解決這個(gè)問題 數(shù)據(jù)的插入,查詢時(shí)長較長 后續(xù)業(yè)務(wù)需求的擴(kuò)展 在表中新增字段 影響較大 表中的數(shù)據(jù)并不是所有的都為有效數(shù)據(jù) 需求只查詢時(shí)間區(qū)間內(nèi)的 評估表數(shù)據(jù)體量 我們可

    2024年02月13日
    瀏覽(20)
  • mysql查詢結(jié)果命令行方式導(dǎo)出/輸出/寫入到文件的三種方法

    mysql查詢結(jié)果命令行方式導(dǎo)出/輸出/寫入到文件的三種方法

    直接執(zhí)行命令: 在目錄/tmp/下會(huì)產(chǎn)生文件test.xls 遇到的問題: 可能原因:mysql沒有向/data/下寫的權(quán)限 查詢都自動(dòng)寫入文件: 跳出mysql命令行

    2024年02月11日
    瀏覽(26)
  • Linux更改普通用戶密碼的三種方法

    Linux更改普通用戶密碼的三種方法

    Linux服務(wù)器使用root管理員用戶創(chuàng)建完成普通用戶之后,為了后續(xù)使用該用戶能夠登錄服務(wù)器(/etc/passwd中每個(gè)用戶的shell類型為/bin/bash的可用來登錄linux服務(wù)器),我們需要為普通用戶設(shè)置登錄密碼。 useradd用戶創(chuàng)建: https://blog.csdn.net/z19861216/article/details/130613814 Linux下,使用root管

    2024年02月15日
    瀏覽(24)
  • JDBC連接Oracle的三種URL格式

    使用jdbc連接oracle時(shí)url有三種格式 格式一: Oracle JDBC Thin using an?SID 這種格式是最簡單也是用得最多的。 你的oracle的sid可以通過一下指令獲得: 格式二: Oracle JDBC Thin using a ServiceName 注意這里的格式,@后面有//, port后面:換成了/,這種格式是Oracle 推薦的格式,因?yàn)閷τ诩簛碚f,

    2024年02月16日
    瀏覽(20)
  • linux中賦予普通用戶root權(quán)限的三種方式

    linux中賦予普通用戶root權(quán)限的三種方式

    在成功拿下一個(gè)服務(wù)器并提權(quán)成為root用戶以后,為了減少root用戶使用時(shí)間從而減少被網(wǎng)站管理員發(fā)現(xiàn)的概率,就需要新建一個(gè)普通用戶,并賦予其完全的管理員權(quán)限,有以下三種常見的方式 1. 直接修改/etc/passwd文件 /etc/passwd文件里的第三列是UID,這個(gè)值為0的話就表示這個(gè)用

    2024年01月21日
    瀏覽(36)
  • 解決數(shù)據(jù)庫查詢時(shí)間過長導(dǎo)致com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure

    大數(shù)據(jù)量下數(shù)據(jù)庫查詢中斷,拋出異常,異常信息見附錄1。 使用springboot項(xiàng)目測試分庫分表,使用sharding-jdbc插件,2000w數(shù)據(jù)量查詢總數(shù)count(*)查詢,查詢失敗,經(jīng)過排查,排除了sharding-jdbc插件的問題,還原原始的mybatis-plus查詢方式依然報(bào)錯(cuò),經(jīng)過查閱網(wǎng)上的相關(guān)文章,嘗試修

    2024年02月11日
    瀏覽(449)
  • ES中的三種查詢

    Es有三種查詢方式,不知道你平時(shí)工作中用到的是哪種呢? 一、from+Size 1、深度分頁或者size特別大的時(shí)候,會(huì)出現(xiàn)deep pagination問題.并且因?yàn)镋s自身的保護(hù)機(jī)制(max_result_window是10000),如果查出來的數(shù)據(jù)量大于10000的就會(huì)報(bào)錯(cuò). 2、該查詢的實(shí)際原理類似于mysql中的limit,比如查詢第10001條數(shù)

    2023年04月09日
    瀏覽(20)
  • Mybatis的三種映射關(guān)系以及聯(lián)表查詢

    目錄 一、概念 二、一對一 1、配置generatorConfig.xml 2、Vo包的編寫 3、xml的sql編寫 4、編寫對應(yīng)接口及實(shí)現(xiàn)類 5、測試 三、一對多 1、Vo包類的編寫 2、xml的sql編寫 3、編寫對應(yīng)接口及實(shí)現(xiàn)類 4、測試 四、多對多 1、Vo類 2、xml的sql配置 3、接口及接口實(shí)現(xiàn)類 4、測試 1、MyBatis中表之間

    2024年02月10日
    瀏覽(25)
  • Mybatis-puls——條件查詢的三種格式+條件查詢null判定+查詢投影

    Mybatis-puls——條件查詢的三種格式+條件查詢null判定+查詢投影

    在mybatis_plus的封裝中的WrapperT接口參數(shù)就是用于封裝查詢條件? ?在測試類中啟動(dòng)如上一個(gè)簡單的查詢,然后控制臺運(yùn)行會(huì)輸出一大堆無關(guān)日志,這里先把這些日志關(guān)閉 先新建一個(gè)XML配置文件? ?然后變成如下,這里configuration標(biāo)簽里面什么都沒有配置就是取消所有日志文件了

    2024年01月18日
    瀏覽(26)
  • 數(shù)據(jù)庫批量插入數(shù)據(jù)的三種方法

    測試環(huán)境:SpringBoot項(xiàng)目+MybatisPlus框架+MySQL數(shù)據(jù)庫+Lombok 方法一: for循環(huán)插入(單條) (總耗時(shí):n分鐘,每次都要獲取連接Connection、釋放連接和關(guān)閉資源等操作,比較耗時(shí),這里就沒測了) 方法二: 批量插入saveBatch (4~7秒,這里用到了MybatisPLus的saveBatch批量插入方法,實(shí)際

    2024年02月14日
    瀏覽(23)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包