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
值。
我想批量插入很多条记录。有些字段是 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
值。