最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Mysql中JDBC的三種查詢(普通、流式、游標)詳解

 更新時間:2023年08月04日 09:53:31   作者:趕路人兒  
這篇文章主要介紹了Mysql中JDBC的三種查詢(普通、流式、游標)詳解,JDBC(Java DataBase Connectivity:java數(shù)據(jù)庫連接)是一種用于執(zhí)行SQL語句的Java API,可以為多種關系型數(shù)據(jù)庫提供統(tǒng)一訪問,它是由一組用Java語言編寫的類和接口組成的,需要的朋友可以參考下

JDBC查詢

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

  1. 常規(guī)查詢:JDBC驅動會阻塞的一次性讀取全部查詢的數(shù)據(jù)到 JVM 內存中,或者分頁讀取
  2. 流式查詢:每次執(zhí)行rs.next時會判斷數(shù)據(jù)是否需要從mysql服務器獲取,如果需要觸發(fā)讀取一批數(shù)據(jù)(可能n行)加載到 JVM 內存進行業(yè)務處理
  3. 游標查詢:通過 fetchSize 參數(shù),控制每次從mysql服務器一次讀取多少行數(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)說明:

第四行設置featchSize不起作用。第五行statement.executeQuery()執(zhí)行查詢會阻塞,因為需要等到所有數(shù)據(jù)返回并放到內存中;接下來每次執(zhí)行resultSet.next()方法會從內存中獲取數(shù)據(jù)。

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

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

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()時不會從TCP響應流中讀取完所有數(shù)據(jù),當下面執(zhí)行rs.next()時會按照需要從TCP響應流中讀取部分數(shù)據(jù)。

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

或者通過com.mysql.jdbc.StatementImpl的enableStreamingResults()方法設置。

二者是一致的。看mysql的jdbc(com.mysql.jdbc.StatementImpl)

源碼:

2.2 流式查詢原理:

1)基本概念

我們要知道jdbc客戶端和mysql服務器之間是通過TCP建立的通信,使用mysql協(xié)議進行傳輸數(shù)據(jù)。

首先聲明一個概念:在三次握手建立了TCP連接后,就可以在這個通道上進行通信了,直到關閉該連接。

在 TCP 中發(fā)送端和接收端**可以是客戶端/服務端,也可以是服務器/客戶端**,通信的雙方在任意時刻既可以是接收數(shù)據(jù)也可以是發(fā)送數(shù)據(jù)(全雙工)。

在通信中,收發(fā)雙方都不保持記錄的邊界,所以需要按照一定的協(xié)議進行表示。在mysql中會按照mysql協(xié)議來進行交互。

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

在執(zhí)行st.executeQuery()時,jdbc驅動會通過connection對象和mysql服務器建立TCP連接,同時在這個鏈接通道中發(fā)送sql命令,并接受返回。

二者的區(qū)別是:

  • 普通查詢:也叫批量查詢,jdbc客戶端會阻塞的一次性從TCP通道中讀取完mysql服務的返回數(shù)據(jù);
  • 流式查詢:分批的從TCP通道中讀取mysql服務返回的數(shù)據(jù),每次讀取的數(shù)據(jù)量并不是一行(通常是一個package大小),jdbc客戶端在調用rs.next()方法時會根據(jù)需要從TCP流通道中讀取部分數(shù)據(jù)。(并不是每次讀區(qū)一行數(shù)據(jù),網(wǎng)上說的幾乎都是錯的!)

2)源碼查看:

從statement.executeQuery()方法跟進去,主要的調用連如下:

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會通過io發(fā)送sql命令請求到mysql服務器,并獲取返回流mysqlOutput
  2. getResultSet()方法會判斷是否是流式查詢還是批量查詢。MySQL驅動會根據(jù)不同的參數(shù)設置選擇對應的ResultSet實現(xiàn)類,分別對應三種查詢方式:
    1. RowDataStatic 靜態(tài)結果集,默認的查詢方式,普通查詢
    2. RowDataDynamic 動態(tài)結果集,流式查詢
    3. RowDataCursor 游標結果集,服務器端基于游標查詢

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

對于流式查詢:直接創(chuàng)建RowDataDynamic對象返回。后面在掉用rs.next()獲取數(shù)據(jù)時會根據(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í)行結果:

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. 也就是說當通過流式查詢獲取一個ResultSet后,通過next迭代出所有元素之前或者調用close關閉它之前,不能使用同一個數(shù)據(jù)庫連接去發(fā)起另外一個查詢,否者拋出異常(第一次調用的正常,第二次的拋出異常)。

2.4 抓包驗證:

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

3、游標查詢

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時需要設置ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY
  • 設置fetchSize控制每一次獲取多少條數(shù)據(jù)

2)抓包驗證:

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

3)游標查詢需要注意的點:

由于MySQL方不知道客戶端什么時候將數(shù)據(jù)消費完,而自身的對應表可能會有DML寫入操作,此時MySQL需要建立一個臨時空間來存放需要拿走的數(shù)據(jù)。

因此對于當你啟用useCursorFetch讀取大表的時候會看到MySQL上的幾個現(xiàn)象:

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

到此這篇關于Mysql中JDBC的三種查詢(普通、流式、游標)詳解的文章就介紹到這了,更多相關JDBC的三種查詢內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 詳解 MySQL的FreeList機制

    詳解 MySQL的FreeList機制

    這篇文章主要介紹了MySQL的FreeList機制的相關資料,幫助大家更好的理解和使用MySQL 數(shù)據(jù)庫,感興趣的朋友可以了解下
    2020-11-11
  • MySQL 的 20+ 條最佳實踐

    MySQL 的 20+ 條最佳實踐

    數(shù)據(jù)庫操作是當今 Web 應用程序中的主要瓶頸。 不僅是 DBA(數(shù)據(jù)庫管理員)需要為各種性能問題操心,程序員為做出準確的結構化表,優(yōu)化查詢性能和編寫更優(yōu)代碼,也要費盡心思。 在本文中,我列出了一些針對程序員的 MySQL 優(yōu)化技術
    2016-12-12
  • 利用MySQL統(tǒng)計一列中不同值的數(shù)量方法示例

    利用MySQL統(tǒng)計一列中不同值的數(shù)量方法示例

    這篇文章主要給大家介紹了利用MySQL統(tǒng)計一列中不同值的數(shù)量的幾種解決方法,每種方法都給了詳細的示例代碼供大家參考學習,相信對大家具有一定的參考價值,需要的朋友們下面跟隨小編一起來看看吧。
    2017-04-04
  • 探究MySQL優(yōu)化器對索引和JOIN順序的選擇

    探究MySQL優(yōu)化器對索引和JOIN順序的選擇

    這篇文章主要介紹了探究MySQL優(yōu)化器對索引和JOIN順序的選擇,包括在優(yōu)化器做出錯誤判斷時的選擇情況,需要的朋友可以參考下
    2015-05-05
  • mysql自動定時備份數(shù)據(jù)庫的最佳方法(windows服務器)

    mysql自動定時備份數(shù)據(jù)庫的最佳方法(windows服務器)

    網(wǎng)上有很多關于window下Mysql自動備份的方法,可是真的能用的也沒有幾個,有些說的還非常的復雜,難以操作,這里腳本之家小編為大家分享與整理了幾個軟件方便大家使用
    2016-11-11
  • MySql用DATE_FORMAT截取DateTime字段的日期值

    MySql用DATE_FORMAT截取DateTime字段的日期值

    MySql截取DateTime字段的日期值可以使用DATE_FORMAT來格式化,使用方法如下
    2014-08-08
  • Idea 如何導入Mysql8.0驅動jar包

    Idea 如何導入Mysql8.0驅動jar包

    IDEA中的庫(Libraries)就是用來存放外部jar包,我們的項目或模塊需要某些jar包時,可以從這里把包導入到模塊依賴(Dependencies)中,本文給大家介紹Idea 如何導入Mysql8.0驅動jar包,感興趣的朋友一起看看吧
    2023-12-12
  • Mysql 根據(jù)一個表數(shù)據(jù)更新另一個表的某些字段(sql語句)

    Mysql 根據(jù)一個表數(shù)據(jù)更新另一個表的某些字段(sql語句)

    這篇文章主要介紹了Mysql 根據(jù)一個表數(shù)據(jù)更新另一個表的某些字段,本文給出了sql語句,感興趣的朋友可以跟隨腳本之家小編一起學習吧
    2018-05-05
  • Mysql之BufferPool中chunk的使用及說明

    Mysql之BufferPool中chunk的使用及說明

    InnoDB通過將BufferPool劃分為若干個chunk來優(yōu)化內存管理,避免了每次調整大小時的耗時操作,每個chunk代表一片連續(xù)的內存空間,包含緩沖頁和控制塊,BufferPool有2個實例,每個實例包含2個chunk,通過innodb_buffer_pool_chunk_size可以指定chunk的大小
    2025-11-11
  • 新手把mysql裝進docker中碰到的各種問題

    新手把mysql裝進docker中碰到的各種問題

    這篇文章主要給大家介紹了新手第一次把mysql裝進docker中可能碰到的各種問題,文中通過示例代碼介紹的非常詳細,對大家學習或者使用mysql具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-06-06

最新評論

平谷区| 成安县| 满洲里市| 易门县| 剑阁县| 新安县| 石城县| 日土县| 连江县| 朝阳区| 永城市| 屏边| 东海县| 荣昌县| 盐城市| 新郑市| 尼勒克县| 滁州市| 金阳县| 永顺县| 留坝县| 建昌县| 正镶白旗| 洪江市| 福建省| 靖州| 海盐县| 阿勒泰市| 宜川县| 鲁山县| 连云港市| 鄂伦春自治旗| 永平县| 响水县| 吐鲁番市| 刚察县| 八宿县| 建水县| 南丹县| 根河市| 手机|