中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

MySQL JDBC Statement.executeBatch實踐問題是怎樣的

發布時間:2021-10-25 10:19:44 來源:億速云 閱讀:215 作者:柒染 欄目:大數據

這篇文章將為大家詳細講解有關MySQL JDBC Statement.executeBatch實踐問題是怎樣的,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。

MySQL JDBC Statement.executeBatch實踐(執行效率低)

現在很少使用原生jdbc去實現代碼, 最近在測試MySQL批處理數據遇到一個問題: 執行Statement.executeBatch時效率極其低下(插入10000條數據), 斷點調試時在此處停頓很長時間(908712ms)

數據版本:

select version();
// 5.7.17-11-V2.0R530D002-20190816-1203-log

數據庫表:

CREATE TABLE `t_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(64) DEFAULT NULL,
  `age` smallint(6) DEFAULT NULL,
  `salary` decimal(10,2) DEFAULT NULL,
  `max_size` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;

測試代碼如下:

public class BatchTest {
    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        batch("com.mysql.cj.jdbc.Driver",
                "jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false&allowMultiQueries=true",
                "root", "123456");
    }

    private static void batch(String driver, String url, String username, String password) throws ClassNotFoundException, SQLException {
        // jdbc driver: mysql-connector-java:8.0.21
        Class.forName(driver);
        Connection conn = DriverManager.getConnection(url, username, password);
        conn.setAutoCommit(false);
         PreparedStatement pstmt = conn.prepareStatement("select * from dual");
        for(int i = 1; i <= 10000; i++){
            String batchSql = "insert into t_user(user_name, age, salary, max_size) values (" +
                    "'name" + i + "'," + i + "," + i + "," + i +
                    ")";
            pstmt.addBatch(batchSql);
        }
        long start = System.currentTimeMillis();
        // 執行耗時很長
        int[] rs = pstmt.executeBatch();
        conn.commit();
        // total ======> 908712
        System.out.println("total ======> " + (System.currentTimeMillis() - start));

    }
}

原因

  1. addBatch() 是在PreparedStatement定義的, addBatch(String) 是在Statement定義的,

  2. MySQL JDBC驅動在默認情況下使用預編譯會無視executeBatch()語句, 把我們期望批量執行的一組sql語句拆散, 一條一條地發給MySQL數據庫, 直接造成較低的性能.

  3. batchHasPlainStatements=true, 即使用PreparedStatement時使用了Statement.addBatch(String), 不執行批處理操作, 一條一條地發給MySQL數據庫, 直接造成較低的性能.(確使用方式: PreparedStatement + addBatch(), Statement + addBatch(sql))

JDBC驅動源碼:

public class ClientPreparedStatement extends com.mysql.cj.jdbc.StatementImpl implements JdbcPreparedStatement {
    /**
     * Does the batch (if any) contain "plain" statements added by
     * Statement.addBatch(String)?
     * 
     * If so, we can't re-write it to use multi-value or multi-queries.
     */
    protected boolean batchHasPlainStatements = false;
    // ......
    protected long[] executeBatchInternal() throws SQLException {
            synchronized (checkClosed().getConnectionMutex()) {
    
                if (this.connection.isReadOnly()) {
                    throw new SQLException(Messages.getString("PreparedStatement.25") + Messages.getString("PreparedStatement.26"),
                            MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT);
                }
    
                if (this.query.getBatchedArgs() == null || this.query.getBatchedArgs().size() == 0) {
                    return new long[0];
                }
    
                // we timeout the entire batch, not individual statements
                int batchTimeout = getTimeoutInMillis();
                setTimeoutInMillis(0);
    
                resetCancelledState();
    
                try {
                    statementBegins();
    
                    clearWarnings();
                     // 1. batchHasPlainStatements 包含原始sql語句
                     // 2. rewriteBatchedStatements 使用批量
                    if (!this.batchHasPlainStatements && this.rewriteBatchedStatements.getValue()) {
    
                        if (((PreparedQuery<?>) this.query).getParseInfo().canRewriteAsMultiValueInsertAtSqlLevel()) {
                            return executeBatchedInserts(batchTimeout);
                        }
    
                        if (!this.batchHasPlainStatements && this.query.getBatchedArgs() != null
                                && this.query.getBatchedArgs().size() > 3 /* cost of option setting rt-wise */) {
                            return executePreparedBatchAsMultiStatement(batchTimeout);
                        }
                    }
    
                    return executeBatchSerially(batchTimeout);
                } finally {
                    this.query.getStatementExecuting().set(false);
    
                    clearBatch();
                }
            }
    }
    // ......
}

修正:

  1. 在連接url加上&rewriteBatchedStatements=true, 如: jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true

  2. 使用Statement.addBatch()替換Statement.addBatch(String)

public class BatchTest {
    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        batch("com.mysql.cj.jdbc.Driver",
                "jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true",
                "root", "123456");
    }

    private static void batch(String driver, String url, String username, String password) throws ClassNotFoundException, SQLException {
        Class.forName(driver);
        Connection conn = DriverManager.getConnection(url, username, password);
        conn.setAutoCommit(false);
        String pSql = "insert into t_user(user_name, age, salary, max_size) values (?, ?, ?, ?)";
        PreparedStatement pstmt =conn.prepareStatement(pSql);
        for(int i = 1; i <= 10000; i++){
            try {
                pstmt.setString(1, "name" + i);
                pstmt.setInt(2, i);
                pstmt.setInt(3, i);
                pstmt.setInt(4, i);
                pstmt.addBatch();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        long start = System.currentTimeMillis();
        int[] rs = pstmt.executeBatch();
        conn.commit();
        // total ======> 2598
        System.out.println("total ======> " + (System.currentTimeMillis() - start));
    }
}

關于MySQL JDBC Statement.executeBatch實踐問題是怎樣的就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

兴和县| 南溪县| 蓬溪县| 天水市| 沿河| 社旗县| 利川市| 汾阳市| 浏阳市| 景宁| 林甸县| 潼关县| 土默特左旗| 雅江县| 黄大仙区| 廊坊市| 惠安县| 双峰县| 温宿县| 兰坪| 旬邑县| 云安县| 抚州市| 鄂托克前旗| 乐清市| 崇文区| 黄龙县| 乐业县| 灵石县| 新丰县| 河间市| 宁陵县| 河北区| 宁波市| 五指山市| 长治县| 东阳市| 陆良县| 顺平县| 汝城县| 乌苏市|