带有 BLOB 列的 MySQL 5.7 批量插入

Posted

技术标签:

【中文标题】带有 BLOB 列的 MySQL 5.7 批量插入【英文标题】:MySQL 5.7 bulk insert with BLOB column 【发布时间】:2019-03-18 00:22:15 【问题描述】:

我正在尝试使用

mysql 进行批量插入
INSERT INTO TABLE (a, b, c) VALUES (?, ?, ?), (?, ?, ?)

我有常规登录,并且看到这在大多数情况下都非常有效。但是,当表有 BLOB 列时,它就不能正常工作了。

我正在尝试插入 20 条记录。

没有 BLOB,我在通用日志中看到同一查询中的所有 20 条记录,同一查询中插入了 20 条记录。

使用 BLOB,我在常规日志中只看到每个查询 2 条记录,总共需要 10 个查询。

这是 MySQL、JDBC 驱动程序的问题,还是我遗漏了其他东西。我更喜欢使用 BLOB,因为我在 protobufs 中有数据。

这是一个示例表...

CREATE TABLE my_table (
  id CHAR(36) NOT NULL,
  name VARCHAR(256) NOT NULL,
  data BLOB NOT NULL,
  PRIMARY KEY (id)
);

然后,在代码中创建批量插入...

val ps = conn.prepareStatement(
  "INSERT INTO my_table(id, name, data) VALUES (?, ?, ?)")
records.grouped(1000).foreach  group =>
  group.foreach  r =>
    ps.setString(1, UUID.randomUUID.toString)
    ps.setString(2, r.name)
    ps.setBlob(3, new MariaDbBlob(r.data))
    ps.addBatch()
  
  ps.executeBatch()

如果您运行它并检查常规日志,您将看到...

"2018-10-12T18:37:55.714825Z 4 查询 INSERT INTO my_table(id, name, fqdn, data) VALUES ('b4955537-2450-48c4-9953-e27f3a0fc583', '17-apply-test', _二进制' 17-apply-test\"AAAA(?2Pending8???,J$b4955537-2450-48c4-9953-e27f3a0fc583

1:2:3:4:5:6:7:8Rsystem'), ('480e470c-6d85-4bbc-b718-21d9e80ac7f7', '18-apply-test', _binary' 18-apply-test\"AAAA(?2Pending8?????,J$480e470c-6d85-4bbc-b718-21d9e80ac7f7

1:2:3:4:5:6:7:8Rsystem') 2018-10-12T18:37:55.715489Z 4 查询 INSERT INTO my_table(id、name、data)值('7571a651-0e0b-4e78-bff0-1394070735ce'、'19-apply-test'、_binary' 19-apply-test\"AAAA(?2Pending8???,J$7571a651-0e0b-4e78-bff0-1394070735ce

1:2:3:4:5:6:7:8Rsystem'), ('f77ebe28-73d2-4f6b-8fd5-284f0ec2c3f0', '20-apply-test', _binary' 20-apply-test\"AAAA(?2Pending8???,J$f77ebe28-73d2-4f6b-8fd5-284f0ec2c3f0

如您所见,每个 INSERT INTO 中只有 2 条记录。

现在,如果您从架构中删除 data 字段并插入并重新运行,您将看到以下输出(10 条记录)...

"2018-10-12T19:04:24.406567Z 4 查询 INSERT INTO my_table(id, name) VALUES ('d323d21e-25ac-40d4-8cff-7ad12f83b8c0', '1-apply-test'), ('f20e37f2 -35a4-41e9-8458-de405a44f4d9', '2-apply-test'), ('498f4e96-4bf1-4d69-a6cb-f0e61575ebb4', '3-apply-test'), ('8bf7925d-8f01-494f-8f9f -c5b8c742beae', '4-apply-test'), ('5ea663e7-d9bc-4c9f-a9a2-edbedf3e5415', '5-apply-test'), ('48f535c8-44e6-4f10-9af9-1562081538e5', '6 -apply-test'), ('fbf2661f-3a23-4317-ab1f-96978b39fffe', '7-apply-test'), ('3d781e25-3f30-48fd-b22b-91f0db8ba401', '8-apply-test') , ('55ffa950-c941-44dc-a233-ebecfd4413cf', '9-apply-test'), ('6edc6e25-6e70-42b9-8473-6ab68d065d44', '10-apply-test')"

所有 10 条记录都在同一个查询中

【问题讨论】:

请发minimal reproducible example。 您应该尝试 JDBC 的批处理语法,它允许您使用静态插入语句,您可以以迭代方式重用该语句。 This thread 有更多信息,还有关于优化参数的信息。 @MarkRotteveel 添加了详细信息 @MickMnemonic 我发布了批处理语法,我认为它是正确的。也重写了BatchedStatements。后一个示例演示了在不使用 BLOB 时批量插入按预期工作 您使用的是 MySQL 还是使用 MariaDB?您使用的是哪个 JDBC 驱动程序(+ 版本)? 【参考方案1】:

我一直在修补,直到找到解决方法...

val ps = conn.prepareStatement(
  "INSERT INTO my_table(id, name, data) VALUES (?, ?, ?)")
records.grouped(1000).foreach  group =>
  group.foreach  r =>
  ps.setString(1, UUID.randomUUID.toString)
  ps.setString(2, r.name)
  //ps.setBlob(3, new MariaDbBlob(r.data))
  ps.setBytes(r.data)
  ps.addBatch()

ps.executeBatch()

使用PreparedStatement.setBytes 而不是使用MariaDbBlob 似乎可以解决问题

【讨论】:

以上是关于带有 BLOB 列的 MySQL 5.7 批量插入的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis 批量插入包含 BLOB 类型的数据到 Oracle 异常问题分析

Java:如何使用用于 Sql Server 的 java jdbc 执行带有标识列的批量插入

带有自定义绑定的 JOOQ 批量插入

带有快速数组参数的节点mysql批量插入

在 Debian Stretch 上运行时,mysql 5.7 load_file 不起作用

带有字符编码的 Python MySQL 批量插入错误