繁体   English   中英

如何使用 MySQL C++ 连接器提高 MySQL 插入性能?

[英]How can I improve MySQL Insert Performance using MySQL C++ Connector?

我正在向 MySQL 数据库插入大量记录,并且正在尝试实现不错的 INSERT 性能。 我正在使用 MySQL 8.0 和 MySQL 连接器 C++ 8.0。

为了确定插入数据的最快方式,我构建了一个小型测试程序,它简单地将 10000 条记录插入到一个表中。 如果有帮助,这是表结构:

CREATE TABLE IF NOT EXISTS Parent (
id BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY,
xxuint1 INTEGER UNSIGNED,
xxuint2 INTEGER UNSIGNED,
xxuint3 INTEGER UNSIGNED,
xxuint4 INTEGER UNSIGNED)

我创建了一个包含值的结构,并创建了一个包含 10,000 个随机数的数组 (tblParent[10000])。 这个数组的填充是在插入之前完成的,所以我只能测量插入性能。 下面的 function 是我的基本插入 function:

void InsertData(sql::Connection* con)
{
    sql::PreparedStatement* pstmt = NULL;

    try {
        std::string sql = "INSERT INTO Parent("
            "xxuint1, xxuint2, xxuint3, xxuint4"
            ") VALUES (?,?,?,?);";

        pstmt = con->prepareStatement(sql);
        for (size_t i = 0; i < NUM_PARENTS; ++i) {
            pstmt->setUInt(1, tblParent[i].uint1);
            pstmt->setUInt(2, tblParent[i].uint2);
            pstmt->setUInt(3, tblParent[i].uint3);
            pstmt->setUInt(4, tblParent[i].uint4);
            pstmt->execute();
        }
    } catch(sql::SQLException &e) {
        std::cout << "SQLException: " << e.what() << std::endl;
    }

    delete pstmt;
}

通常,当插入许多记录时,您可以通过使用多个值列表来获得更好的性能:

INSERT INTO MyTable (col1, col2, col3) VALUES (?, ?, ?), (?, ?, ?), ... number_of_records

而不是一次插入一条记录。 对于每个记录数:

INSERT INTO MyTable (col1, col2, col3) VALUES (?, ?, ?)

我假设上面的代码将在幕后使用多值列表方法,但根据我的性能测量,我不相信它是。 这是我得到的:
具有 10,000 条记录的 InsertData 代码:
~300 条记录/秒。
用“START TRANSACTION”和“COMMIT”包围 InsertData:
~8000 条记录/秒

如果我重写插入数据,以便将数组中的数据作为字符串直接插入 sql,例如

std::string sql = "INSERT INTO Parent("
            "xxuint1, xxuint2, xxint3, xxbigint4"
            ") VALUES (";
for (size_t i = 0; i < NUM_PARENTS; ++i) {
    sql += to_string(tblParent[i].uint1) + ", ";
    sql += to_string(tblParent[i].uint2) + ", ";
    sql += to_string(tblParent[i].uint3) + ", ";
    sql += to_string(tblParent[i].uint4) + "); ";
}

我得到与上述类似的性能。

当我明确开始使用多个值列表时,性能得到了提高。 我调整了我的 sql 以包含“VALUES (?, ?, ?), (?, ?, ?), ...”,这将性能提高到约 14,000 条记录/秒。 但最好的时机是将我的数据转换为字符串,然后使用多个值列表将该数据直接插入 sql。 我这样做的速度高达约 40,000 条记录/秒。

然而,虽然速度很好,但我不认为将我的数据转换为文本并将其插入 sql 是一种理想的方法。 如何优化插入速度并仍然使用 pstmt->setUint() 方法?

几年前我做了一个演示,比较了不同插入方法的开销: https://www.slideshare.net/billkarwin/load-data-fast

和你一样,我发现在 VALUES 子句中包含多个元组的一条语句中插入多行更好。 您的第一个代码示例本身不会这样做,您必须编写带有多个元组的 INSERT 语句,就像您的第二个代码示例一样。

避免每行的完整事务(即自动提交)有很大帮助。 这就是您在循环之前开始事务时所做的。 在破坏二进制日志之前,每个事务可以插入的字节数有一个实际限制,因此如果您有大量插入,请尝试分批进行,例如不超过 10k 行。 为了安全起见,我可能会选择每批 1000 行。 这至少是交易开销的 1/1000。

如果您可以减少表上的索引和插入触发器的数量,那将有所帮助。 插入一行的成本大约与它需要更新的索引数量成正比(由于更改缓冲区等原因会有一些变化)。 触发器会增加开销,因为它可能会运行其他 DML,例如插入日志表,这意味着更多的索引写入。

一些 MySQL 服务器调整选项可以帮助减少一点开销,但以降低数据持久性为代价。

通过切换到 LOAD DATA INFILE(专为批量数据加载而设计),所有这些优化都相形见绌。 通过这种方式,您可以获得一个数量级的改进。 但是由于索引写入,每行仍然存在开销,并且事务大小仍然存在实际限制。

为您的 my.cnf 或 my.ini [mysqld] 部分考虑的建议

innodb_change_buffer_max_size=50  # from 25 (percent) set aside in buffer pool
innodb_change_buffering=none  # from all - most likely -
innodb_write_io_threads=64  # for max capacity

提高每秒插入率。

参考以前的答案。 dba.stackexchange.com 问题 5666 请参阅 Rolando 的 2011 年 9 月 12 日详细信息 dba.stackexchange.com 问题 196715 查找 Rolando 的变更缓冲和与这些变量的许多方面相关的注意事项

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM