优化mysql insert,具有多个值(),(),();

ulydmbyx  于 2021-06-21  发布在  Mysql
关注(0)|答案(1)|浏览(248)

我正在努力提高我的java应用程序的性能,现在我将重点放在一个端点上,这个端点必须向mysql中插入大量数据。
我将普通jdbc与mariadb java客户端驱动程序结合使用:

try (PreparedStatement stmt = connection.prepareStatement(
            "INSERT INTO data (" +
                    "fId, valueDate, value, modifiedDate" +
                    ") VALUES (?,?,?,?)") {
    for (DataPoint dp : datapoints) {
        stmt.setLong(1, fId);
        stmt.setDate(2, new java.sql.Date(dp.getDate().getTime()));
        stmt.setDouble(3, dp.getValue());
        stmt.setDate(4, new java.sql.Date(modifiedDate.getTime()));
        stmt.addBatch();
    }        
    int[] results = statement.executeBatch();
}

从转储文件中填充新数据库,我知道 max_allowed_packet 很重要,我把它设为536870912字节。
在https://dev.mysql.com/doc/refman/5.7/en/insert-optimization.html 它指出:
如果要同时从同一客户机插入多行,请使用具有多个值列表的insert语句一次插入多行。这比使用单独的单行insert语句快得多(在某些情况下快很多倍)。如果要向非空表中添加数据,可以调整bulk\u insert\u buffer\u size变量,使数据插入更快。参见第5.1.7节“服务器系统变量”。
在我的dbs上,这个设置为8mb
我也读过 key_buffer_size (当前设置为16mb)。
我担心最后两个可能不够。我可以对这个算法的json输入做一些粗略的计算,因为它看起来像这样:

[{"actualizationDate":null,"data":[{"date":"1999-12-31","value":0},
{"date":"2000-01-07","value":0},{"date":"2000-01-14","value":3144},
{"date":"2000-01-21","value":358},{"date":"2000-01-28","value":1049},
{"date":"2000-02-04","value":-231},{"date":"2000-02-11","value":-2367},
{"date":"2000-02-18","value":-2651},{"date":"2000-02-25","value":-
393},{"date":"2000-03-03","value":1725},{"date":"2000-03-10","value":-
896},{"date":"2000-03-17","value":2210},{"date":"2000-03-24","value":1782},

它看起来像是为 bulk_insert_buffer_size 很容易被超过,如果不是的话 key_buffer_size 也。
但是mysql文档只提到 MyISAM 引擎表,我现在正在使用 InnoDB table。
我可以设置一些测试,但它会很好地知道这将如何打破或降级,如果在所有。
[编辑]我有 --rewriteBatchedStatements=true . 实际上,这是我的连接字符串:

jdbc:p6spy:mysql://myhost.com:3306/mydb\
    ?verifyServerCertificate=true\
    &useSSL=true\
    &requireSSL=true\
    &cachePrepStmts=true\
    &cacheResultSetMetadata=true\
    &cacheServerConfiguration=true\
    &elideSetAutoCommits=true\
    &maintainTimeStats=false\
    &prepStmtCacheSize=250\
    &prepStmtCacheSqlLimit=2048\
    &rewriteBatchedStatements=true\
    &useLocalSessionState=true\
    &useLocalTransactionState=true\
    &useServerPrepStmts=true

(来自https://github.com/brettwooldridge/hikaricp/wiki/mysql-configuration )

oiopk7p5

oiopk7p51#

另一种方法是不时地执行批处理。这允许您减小批处理的大小,并让您将精力集中在更重要的问题上。

int batchSize = 0;

for (DataPoint dp : datapoints) {
    stmt.setLong(1, fId);
    stmt.setDate(2, new java.sql.Date(dp.getDate().getTime()));
    stmt.setDouble(3, dp.getValue());
    stmt.setDate(4, new java.sql.Date(modifiedDate.getTime()));
    stmt.addBatch();

    //When limit reach, execute and reset the counter
    if(batchSize++ >= BATCH_LIMIT){
        statement.executeBatch();

        batchSize = 0;
    }
}        

// To execute the remaining items
if(batchSize > 0){
    statement.executeBatch();
}

我通常使用基于dao实现的常量或参数来提高动态性,但是一批10000行是一个很好的开始。

private static final int BATCH_LIMIT = 10_000;

请注意,执行后不必清除批处理。即使没有在 Statement.executeBatch 文档,这在jdbc规范4.3中
14批更新
14.1批量更新说明
14.1.2成功执行
调用方法executebatch将关闭调用语句对象的当前结果集(如果其中一个结果集已打开)。
一旦executebatch返回,语句的批处理将重置为空。
结果的管理有点复杂,但是如果需要,仍然可以连接结果。这可以在任何时候进行分析,因为 ResultSet 不再需要了。

相关问题