千家信息网

MySQL JDBC Statement.executeBatch实践问题是怎样的

发表于:2025-12-04 作者:千家信息网编辑
千家信息网最后更新 2025年12月04日,这篇文章将为大家详细讲解有关MySQL JDBC Statement.executeBatch实践问题是怎样的,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的
千家信息网最后更新 2025年12月04日MySQL JDBC Statement.executeBatch实践问题是怎样的

这篇文章将为大家详细讲解有关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实践问题是怎样的就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

数据 问题 实践 数据库 语句 代码 内容 性能 文章 是在 更多 知识 篇文章 测试 驱动 不错 低下 原始 原因 情况 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 删除企业级地理数据库 控制系统软件开发服务方案报价 郑州网络技术大赛 信息化软件开发设施五星服务 弹性裸金属服务器概念 湖南企业软件开发排行榜 云服务器哪个安全速度快 流量转发服务器压力 drop表示从数据库中删除表 机战数据库闪退 国美通讯网络安全 数据库系统中包括选择题 软件开发财务分析怎么写 网络安全包括网络暴力吗 怎么进入证券交易所服务器 网络视频行业内容安全和网络安全存在的问题 管理员不允许所有服务器 网络技术专业碰到的问题 东莞盲盒软件开发程序 台州市互联网软件开发有限公司 网络安全工控网 爬虫爬取文章并保存数据库 服务器设置无法打开网页 广州定制化服务器 红亚网络安全技能竞赛 网络安全员属于公务员吗 民航网络安全稿件 北京悦互联网科技有限公司 区块链软件开发商 手机上搭建网站服务器
0