Java 中 MySQL Insert 语句的性能:批处理模式准备语句与具有多个值的单个插入

2024-01-12

我正在设计一个MySQL该数据库每秒需要在各种 InnoDB 表中处理大约 600 行插入。我当前的实现使用非批处理准备好的语句。然而,写信给MySQL数据库瓶颈和我的队列大小随着时间的推移而增加。

实现是用Java编写的,我不知道版本。它用MySQL's Java连接器 http://www.mysql.com/downloads/connector/j/。我需要考虑切换到JDBC明天。我假设这是两个不同的连接器包。

我已阅读有关该问题的以下主题:

  • 优化 MySQL 插入以处理数据流 https://stackoverflow.com/questions/1774142/optimizing-mysql-inserts-to-handle-a-data-stream
  • MyISAM 与 InnoDB https://stackoverflow.com/questions/20148/myisam-versus-innodb
  • 将二进制数据插入MySQL(没有PreparedStatement) https://stackoverflow.com/questions/10139521/inserting-binary-data-into-mysql-without-preparedstatements

以及来自 mysql 站点:

  • http://dev.mysql.com/doc/refman/5.0/en/insert-speed.html http://dev.mysql.com/doc/refman/5.0/en/insert-speed.html

我的问题是:

  • 有没有人对在批处理模式下使用带有准备好的语句的 INSERT 与使用单个语句的性能差异有建议或经验INSERT具有多个 VALUE 的语句。

  • 两者之间的性能差异是什么MySQLJava 连接器对比JDBC。我应该使用其中之一吗?

  • 这些表用于存档目的,并且将看到约 90% 的写入和约 10% 的读取(可能甚至更少)。我正在使用InnoDB。这是相对于 MyISAM 的正确选择吗?

预先感谢您的帮助。


JDBC 只是提供标准接口的数据库访问的 Java SE 标准,因此您并不真正受限于特定的 JDBC 实现。 MySQL Java 连接器 (Connector/J) 是仅用于 MySQL 数据库的 JDBC 接口的实现。出于经验,我参与了一个使用MySQL使用大量数据的项目,对于可以生成的数据,我们最喜欢MyISAM:它允许在丢失事务时实现更高的性能,但总的来说,MyISAM更快,但InnoDB更可靠。

大约一年前,我也想知道 INSERT 语句的性能,并在我的代码架中找到了以下旧的测试代码(抱歉,它有点复杂,有点超出了您的问题范围)。下面的代码包含 4 种插入测试数据的方法的示例:

  • single INSERTs;
  • batched INSERTs;
  • 手动批量 INSERT(永远不要使用它 - 它很危险);
  • 最后准备好的散装 INSERT).

It uses TestNG http://testng.org/doc/index.html作为跑步者,并使用一些自定义代码遗留,例如:

  • the runWithConnection()方法 - 确保在执行回调后连接被关闭或放回连接池(但下面的代码使用了不可靠的语句关闭策略 - 即使没有try/finally减少代码);
  • IUnsafeIn<T, E extends Throwable>- 接受单个参数但可能抛出 E 类型异常的方法的自定义回调接口,例如:void handle(T argument) throws E;.
package test;

import test.IUnsafeIn;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import static java.lang.String.format;
import static java.lang.String.valueOf;
import static java.lang.System.currentTimeMillis;

import core.SqlBaseTest;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

public final class InsertVsBatchInsertTest extends SqlBaseTest {

    private static final int ITERATION_COUNT = 3000;

    private static final String CREATE_TABLE_QUERY = "CREATE TABLE IF NOT EXISTS ttt1 (c1 INTEGER, c2 FLOAT, c3 VARCHAR(5)) ENGINE = InnoDB";
    private static final String DROP_TABLE_QUERY = "DROP TABLE ttt1";
    private static final String CLEAR_TABLE_QUERY = "DELETE FROM ttt1";

    private static void withinTimer(String name, Runnable runnable) {
        final long start = currentTimeMillis();
        runnable.run();
        logStdOutF("%20s: %d ms", name, currentTimeMillis() - start);
    }

    @BeforeSuite
    public void createTable() {
        runWithConnection(new IUnsafeIn<Connection, SQLException>() {
            @Override
            public void handle(Connection connection) throws SQLException {
                final PreparedStatement statement = connection.prepareStatement(CREATE_TABLE_QUERY);
                statement.execute();
                statement.close();
            }
        });
    }

    @AfterSuite
    public void dropTable() {
        runWithConnection(new IUnsafeIn<Connection, SQLException>() {
            @Override
            public void handle(Connection connection) throws SQLException {
                final PreparedStatement statement = connection.prepareStatement(DROP_TABLE_QUERY);
                statement.execute();
                statement.close();
            }
        });
    }

    @BeforeTest
    public void clearTestTable() {
        runWithConnection(new IUnsafeIn<Connection, SQLException>() {
            @Override
            public void handle(Connection connection) throws SQLException {
                final PreparedStatement statement = connection.prepareStatement(CLEAR_TABLE_QUERY);
                statement.execute();
                statement.close();
            }
        });
    }

    @Test
    public void run1SingleInserts() {
        withinTimer("Single inserts", new Runnable() {
            @Override
            public void run() {
                runWithConnection(new IUnsafeIn<Connection, SQLException>() {
                    @Override
                    public void handle(Connection connection) throws SQLException {
                        for ( int i = 0; i < ITERATION_COUNT; i++ ) {
                            final PreparedStatement statement = connection.prepareStatement("INSERT INTO ttt1 (c1, c2, c3) VALUES (?, ?, ?)");
                            statement.setInt(1, i);
                            statement.setFloat(2, i);
                            statement.setString(3, valueOf(i));
                            statement.execute();
                            statement.close();
                        }
                    }
                });
            }
        });
    }

    @Test
    public void run2BatchInsert() {
        withinTimer("Batch insert", new Runnable() {
            @Override
            public void run() {
                runWithConnection(new IUnsafeIn<Connection, SQLException>() {
                    @Override
                    public void handle(Connection connection) throws SQLException {
                        final PreparedStatement statement = connection.prepareStatement("INSERT INTO ttt1 (c1, c2, c3) VALUES (?, ?, ?)");
                        for ( int i = 0; i < ITERATION_COUNT; i++ ) {
                            statement.setInt(1, i);
                            statement.setFloat(2, i);
                            statement.setString(3, valueOf(i));
                            statement.addBatch();
                        }
                        statement.executeBatch();
                        statement.close();
                    }
                });
            }
        });
    }

    @Test
    public void run3DirtyBulkInsert() {
        withinTimer("Dirty bulk insert", new Runnable() {
            @Override
            public void run() {
                runWithConnection(new IUnsafeIn<Connection, SQLException>() {
                    @Override
                    public void handle(Connection connection) throws SQLException {
                        final StringBuilder builder = new StringBuilder("INSERT INTO ttt1 (c1, c2, c3) VALUES ");
                        for ( int i = 0; i < ITERATION_COUNT; i++ ) {
                            if ( i != 0 ) {
                                builder.append(",");
                            }
                            builder.append(format("(%s, %s, '%s')", i, i, i));
                        }
                        final String query = builder.toString();
                        final PreparedStatement statement = connection.prepareStatement(query);
                        statement.execute();
                        statement.close();
                    }
                });
            }
        });
    }

    @Test
    public void run4SafeBulkInsert() {
        withinTimer("Safe bulk insert", new Runnable() {
            @Override
            public void run() {
                runWithConnection(new IUnsafeIn<Connection, SQLException>() {
                    private String getInsertPlaceholders(int placeholderCount) {
                        final StringBuilder builder = new StringBuilder("(");
                        for ( int i = 0; i < placeholderCount; i++ ) {
                            if ( i != 0 ) {
                                builder.append(",");
                            }
                            builder.append("?");
                        }
                        return builder.append(")").toString();
                    }

                    @SuppressWarnings("AssignmentToForLoopParameter")
                    @Override
                    public void handle(Connection connection) throws SQLException {
                        final int columnCount = 3;
                        final StringBuilder builder = new StringBuilder("INSERT INTO ttt1 (c1, c2, c3) VALUES ");
                        final String placeholders = getInsertPlaceholders(columnCount);
                        for ( int i = 0; i < ITERATION_COUNT; i++ ) {
                            if ( i != 0 ) {
                                builder.append(",");
                            }
                            builder.append(placeholders);
                        }
                        final int maxParameterIndex = ITERATION_COUNT * columnCount;
                        final String query = builder.toString();
                        final PreparedStatement statement = connection.prepareStatement(query);
                        int valueIndex = 0;
                        for ( int parameterIndex = 1; parameterIndex <= maxParameterIndex; valueIndex++ ) {
                            statement.setObject(parameterIndex++, valueIndex);
                            statement.setObject(parameterIndex++, valueIndex);
                            statement.setObject(parameterIndex++, valueIndex);
                        }
                        statement.execute();
                        statement.close();
                    }
                });
            }
        });
    }

}

看看用@Test注解注释的方法:它们实际上执行的是INSERT声明。另请查看CREATE_TABLE_QUERY常量:在源代码中,它使用 InnoDB 在安装了 MySQL 5.5 (MySQL Connector/J 5.1.12) 的机器上产生以下结果:

InnoDB
Single inserts: 74148 ms
Batch insert: 84370 ms
Dirty bulk insert: 178 ms
Safe bulk insert: 118 ms

如果您更改CREATE_TABLE_QUERYInnoDB 到 MyISAM,您会看到性能显着提升:

MyISAM
Single inserts: 604 ms
Batch insert: 447 ms
Dirty bulk insert: 63 ms
Safe bulk insert: 26 ms

希望这可以帮助。

UPD:

对于第四种方式,您必须正确自定义max_allowed_packet in mysql.ini (the [mysqld]部分)足够大以支持真正大的数据包。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 中 MySQL Insert 语句的性能:批处理模式准备语句与具有多个值的单个插入 的相关文章

  • 使用 LinkedList 实现下一个和上一个按钮

    这可能是一个愚蠢的问题 但我很难思考清楚 我编写了一个使用 LinkedList 来移动加载的 MIDI 乐器的方法 我想制作一个下一个和一个上一个按钮 以便每次单击该按钮时都会遍历 LinkedList 如果我硬编码itr next or
  • 为 java 游戏创建交互式 GUI

    大家好 我正在创建一个类似于 java 中的 farmville 的游戏 我只是想知道如何实现用户通常单击以与游戏客户端交互的交互式对象 按钮 我不想使用 swing 库 通用 Windows 看起来像对象 我想为我的按钮导入自定义图像 并
  • 学说迁移后备

    我们正在使用原则迁移 当迁移包含多个操作并且其中一个操作失败时 通常会出现问题 例如 如果迁移添加了 5 个外键 其中第 5 个失败 而字段长度不同 则修复字段错误并重新生成迁移不会not修复整个问题 而现在出现一个与 4 个密钥已存在有关
  • 如何使用assertEquals 和 Epsilon 在 JUnit 中断言两个双精度数?

    不推荐使用双打的assertEquals 我发现应该使用带有Epsilon的形式 这是因为双打不可能100 严格 但无论如何我需要比较两个双打 预期结果和实际结果 但我不知道该怎么做 目前我的测试如下 Test public void te
  • 如何获取之前的URL?

    我需要调用我的网络应用程序的 URL 例如 如果有一个从 stackoverflow com 到我的网站 foo com 的链接 我需要 Web 应用程序 托管 bean 中的 stackoverflow 链接 感谢所有帮助 谢谢 并不总是
  • 在 Jar 文件中运行 ANT build.xml 文件

    我需要使用存储在 jar 文件中的 build xml 文件运行 ANT 构建 该 jar 文件在类路径中可用 是否可以在不分解 jar 文件并将 build xml 保存到本地目录的情况下做到这一点 如果是的话我该怎么办呢 Update
  • 在接口中使用默认方法是否违反接口隔离原则?

    我正在学习 SOLID 原则 ISP 指出 客户端不应被迫依赖于他们所使用的接口 不使用 在接口中使用默认方法是否违反了这个原则 我见过类似的问题 但我在这里发布了一个示例 以便更清楚地了解我的示例是否违反了 ISP 假设我有这个例子 pu
  • java.lang.IllegalStateException:应用程序 PagerAdapter 更改了适配器的内容,而没有调用 PagerAdapter#notifyDataSetChanged android

    我正在尝试使用静态类将值传递给视图 而不是使用意图 因为我必须传递大量数据 有时我会收到此错误 但无法找出主要原因是什么 Error java lang IllegalStateException The application s Pag
  • Java 集合的并集或交集

    建立并集或交集的最简单方法是什么Set在 Java 中 我见过这个简单问题的一些奇怪的解决方案 例如手动迭代这两个集合 最简单的单行解决方案是这样的 set1 addAll set2 Union set1 retainAll set2 In
  • 将流转换为 IntStream

    我有一种感觉 我在这里错过了一些东西 我发现自己做了以下事情 private static int getHighestValue Map
  • java.lang.IllegalStateException:提交响应后无法调用 sendRedirect()

    这两天我一直在尝试找出问题所在 我在这里读到我应该在代码中添加一个返回 我做到了 但我仍然得到 java lang IllegalStateException Cannot call sendRedirect after the respo
  • 在 android 中建立与 MySQL 的池连接

    我需要从我的 Android 应用程序访问 MySQL 数据库 现在所有的工作都通过 DriverManager getConnection url 等等 但我必须从多个线程访问数据库 所以我必须使用连接池 问题1 是 com mysql
  • Java ResultSet 如何检查是否有结果

    结果集 http java sun com j2se 1 4 2 docs api java sql ResultSet html没有 hasNext 方法 我想检查 resultSet 是否有任何值 这是正确的方法吗 if resultS
  • 快速将列的副本添加到 MySQL 表

    我需要一种快速的方法来复制表中的 DATETIME 列并为其指定一个新名称 我的表中有一个名为 myDate 的列 名为 myResults 我需要一个查询来在名为 newDate 的表中创建一个新列 该列的数据与 myDate 列完全相同
  • SQL 最近日期

    我需要在 php 中获取诸如 2010 04 27 之类的日期作为字符串 并在表中找到最近的 5 个日期 表中的日期保存为日期类型 您可以使用DATEDIFF http dev mysql com doc refman 5 1 en dat
  • Eclipse 选项卡宽度不变

    我浏览了一些与此相关的帖子 但它们似乎并不能帮助我解决我的问题 我有一个项目 其中 java 文件以 2 个空格的宽度缩进 我想将所有内容更改为 4 空格宽度 我尝试了 正确的缩进 选项 但当我将几行修改为 4 空格缩进时 它只是将所有内容
  • java.io.Serialized 在 C/C++ 中的等价物是什么?

    C C 的等价物是什么java io Serialized https docs oracle com javase 7 docs api java io Serializable html 有对序列化库的引用 用 C 序列化数据结构 ht
  • 如何使用mockito模拟构建器

    我有一个建造者 class Builder private String name private String address public Builder setName String name this name name retur
  • 双枢轴快速排序和快速排序有什么区别?

    我以前从未见过双枢轴快速排序 是快速排序的升级版吗 双枢轴快速排序和快速排序有什么区别 我在 Java 文档中找到了这个 排序算法是双枢轴快速排序 作者 弗拉基米尔 雅罗斯拉夫斯基 乔恩 本特利和约书亚 布洛赫 这个算法 在许多数据集上提供
  • 如何防止在Spring Boot单元测试中执行import.sql

    我的类路径中有一个 import sql 文件 其中包含一些 INSERT 语句 当使用 profile devel 运行我的应用程序时 它的数据被加载到 postgres 数据库中 到目前为止一切正常 当使用测试配置文件执行测试时 imp

随机推荐

  • ThreadStateException:尝试加入线程时线程尚未启动

    就在最近我面试的时候遇到了这样一个问题 methid 计算 执行的输出是什么 public void Calculate var threads Enumerable Range 0 50 Select x gt var thread ne
  • DataGridView 鼠标滚轮滚动停止工作

    我的应用程序中有一个 DataGridView 但无法使用鼠标滚轮滚动它 以前运行得很好 我不确定我做了什么导致了这个问题 因为我最近对代码进行了多次更改后才注意到它 我没有发布任何代码 因为有超过 2k 行 而且我不确定错误可能出在哪里
  • 如何阻止网站注册表中的一次性电子邮件地址? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我想知道可能的阻止方法一次性电子邮件地址 http en wikipedia org wiki Dis
  • 是否可以对包含 url 编码的 url 进行 urlencode?

    我有一个网站 使用 Facebook Twitter 美味分享链接 它们包含您希望共享的网站的 url 编码 url 问题是我想通过 php 重定向页面发送 facebook twitter delicious url 在编码的 url 中
  • 在 VBA 中导入 UTF-8 文件

    我想在表中导入一个管道分隔文件 但该文件采用 UTF 8 格式 具有动态结构 我曾尝试使用 TransfertText 和 FSO 但只有 ADODB Stream 似乎可以很好地处理这种编码 但它只读取全文 如何每行读取这样的文件行以在现
  • 具有抽象方法的接口

    我遇到了一些由同事编写的 PHP 代码 它没有用于任何用途 基本上它是一个包含抽象方法的接口 然后我说这很愚蠢 并展示了坐在我旁边的另一位同事 我们笑了 但随后开始互相询问这是否可能 如果可能的话是否真的有用 显然这是不可能的 参见下面的示
  • module-info.java 不适用于 Java 9 中的 lombok

    我在将项目迁移到Java 9 更新 Java 9 后 我尝试运行项目 但遇到编译器错误 Error 6 1 java package javax annotation is not visible package javax annotat
  • 在 Windows 8 上安装 Docker 卷不起作用

    Context 我想在 Windows 8 上运行 Docker Compose 应用程序 我在 Ubuntu 16 04 下制作了它 它运行得很好 此 Docker Compose 运行 nginx php fpm 这两个容器使用卷 Fi
  • Git 预提交钩子配置

    我正在关注办公室指导 https pre commit com创建钩子并将其添加到预提交检查过程 我需要创建 3 个文件 pre commit config yaml pre commit hooks yaml theCheckFile s
  • ioexception:在java中写入文件时客户端不拥有所需的权限

    我搜索了很多类似的问题 但无法解决我的问题 我试图在文件中写入一些内容 这给了我错误 My code try File f new File file name f createNewFile System out println Hell
  • 无法使用 Sigma.js 显示任何图表

    我想在网络界面上可视化大型网络图 经过几天的搜索 我决定使用 Sigma js 因为它看起来很简单并且兼容 HTML5 问题是我无法显示 Sigma js 网页上的任何图形示例 即使我使用作者在 Sigma js 主页上拥有的最少代码也是如
  • write 和 printf 哪个更快?

    进行以下测试后 for i 0 i lt 3000000 i printf Test string n for i 0 i lt 3000000 i write STDOUT FILENO Test string n strlen Test
  • 如何创建带空格的列名?

    谁能告诉我如何创建带有 全名 这样的空格的列 我已经尝试过类似以下的操作 但它不起作用 CREATE TABLE info Full Names varchar 20 Physical Address varchar 20 Moviesre
  • GCMIntentService 的 onRegistered() 从未调用过

    我正在尝试在 GCM 上注册我的应用程序 但我不知道为什么我的应用程序从未注册 GCMRegistrar register this SENDER ID 被调用 但我的 GCMIntentService 中的 onRegistered 从未
  • R 中的滚动连接 data.table

    我试图更多地了解滚动连接的工作方式 但有些困惑 我希望有人能为我澄清这一点 举一个具体的例子 dt1 lt data table id rep 1 5 10 t 1 50 val1 1 50 key id t dt2 lt data tab
  • 如何使用 PHP 制作 cookie [重复]

    这个问题在这里已经有答案了 可能的重复 如何为uuid设置cookie https stackoverflow com questions 5750658 how to set cookies for uuid 你好 我想知道如何用PHP制
  • 如何在 Flask 中将数据写入文本文件? [复制]

    这个问题在这里已经有答案了 我们的目标是将名为 inputed email 的变量写入名为 test3 txt 的文本文件中 因为这是在服务器上进行的 所以我们需要确保该 Python 脚本可以访问该目录和文本文件 app route me
  • Haskell / GHC:{-# SPECIALIZE #-} 导致“规则左侧过于复杂,无法脱糖”警告

    我有一段代码 它使用 monad 来抽象实际实现是在 ST 还是 IO 中运行 由于内联和缺少类型类函数调用开销 删除额外的抽象层并仅替换具体类型会带来巨大的加速 约 4 5 倍 我正在考虑通过使用专门的编译指示来获得一些性能 但我从编译器
  • XDocument.保存到特定目录?

    我是第一次使用这个 XML 类 找不到这条信息 我正在做 xmlDoc new XDocument new XDeclaration 1 0 utf 8 yes xmlDoc Add new XElement Images xmlDoc S
  • Java 中 MySQL Insert 语句的性能:批处理模式准备语句与具有多个值的单个插入

    我正在设计一个MySQL该数据库每秒需要在各种 InnoDB 表中处理大约 600 行插入 我当前的实现使用非批处理准备好的语句 然而 写信给MySQL数据库瓶颈和我的队列大小随着时间的推移而增加 实现是用Java编写的 我不知道版本 它用