Rust:我们如何 运行 MySQL 上带有大量参数的 DML?

Rust: How do we run a DML on MySQL with lots of parameters?

我想批量插入很多条记录。有些字段是 varchars,所以我想将该数据作为动态参数传递。我知道我可以使用 exec_batch(),但这在性能方面不是一个好的解决方案,因为它似乎在单独的调用中将每一行发送到数据库。所以我基本上想要的是通过构建更大的 INSERT INTO 语句来批量插入记录,每个语句将插入(比如说)1000 条记录。但这意味着每个插入都有数千个动态参数。

exec_drop()这样的方法可以接受元组,但最多12个元素。那么如何传递数千个 different/mixed 类型(整数、字符串等)的值呢?我猜我应该以某种方式使用所谓的“特征对象”的向量?

也许答案就在 docs 中,但我对 Rust 还是个新手,不太明白如何实现。

我不是 Rust 程序员,但无论应用程序语言如何,性能考虑因素应该是相似的。

对于这种情况,我只准备一个查询以插入一行,并为该行的列设置参数。然后 运行 一个循环,使用不同的参数值执行准备好的查询。这将分析 SQL 的开销排除在循环之外,因为这在准备步骤中仅完成一次。执行准备好的查询不会重新解析语句。

我希望这正是 exec_batch() 所做的。但它可能在循环中每次都重新准备 INSERT 语句。那将是一种幼稚的方法,但我不知道 Rust 包是否幼稚。

如果这不够高效,您应该考虑使用 LOAD DATA [LOCAL] INFILE 加载大量数据。这通常比任何 INSERT 语句快几倍,即使是一组 1000 个元组也是如此。

您可能会喜欢我的演示文稿 Load Data Fast!,我在其中比较了不同数据导入解决方案的相对性能。

好的,我找到方法了。只需创建一个 Vec<Value> 并通过 Value::from(...) 将参数推送到其中,如下所示:

let mut params = Vec::new();
for r in recs {
    params.push(Value::from(&r.someString));
    params.push(Value::from(&r.someInt));
    params.push(Value::from(&r.someFloat));
    ...
}

这样我们就可以轻松传递数千个不同类型的参数。

但是,请注意,似乎仍然存在与 Rust 的 MySQL 库相关的潜在问题,该库始终准备语句,如 here 所述。尽管如果只将不同的值作为动态参数传递,那么将只有 1 个准备好的语句,这样至少应该避免不必要的准备和缓存的开销。


上一个答案(部分解)

我想出了一个部分解决方案。它远非完美,并非在所有情况下都有效,但我会 post 在这里,以防它对读者有所帮助。而且还是希望有人能想出更好的解决办法

在大多数情况下,真正需要转义的只是字符串。大多数其他类型都可以作为静态参数安全地插入。所以我们基本上可以生成这样的查询:

INSERT INTO my_table(id, dt, name, notes, some_num)
VALUES
  (1, '2021-04-15 21:55:27', ?, ?, 6871.34),
  (2, '2021-04-15 22:33:44', ?, ?, NULL),
  (3, '2021-04-15 21:55:27', ?, ?, 55.1),
  (4, NULL, ?, ?, 55.1);

并使用特定类型的向量传递动态参数,例如 Vec<&Option<String>>,它也可以为我们处理 NULL 值。