【问题标题】:Tricky MySQL batch query棘手的 MySQL 批量查询
【发布时间】:2012-02-12 16:09:51
【问题描述】:

我有一个每天晚上都会更新的事件数据库。单个事件具有跨三个(或更多)表的信息。最近更新量导致我的 MySQL 引擎非常慢,以至于在插入新事件时我的其他查询被冻结。为了加快速度,我想进行一系列批处理查询,而不是单独执行每个查询,我觉得这是开销的很大一部分。

问题是因为数据分布在多个表中,这是我插入单个事件所要做的:

   (in Mysql) INSERT INTO 'locations' (...) VALUES (...) ON DUPLICATE KEY UPDATE ...
   (in php get the last inserted id into variable $locationID)
   (in Mysql) INSERT INTO 'event_info' (...) VALUES ($locationID, ...) ON DUPLICATE KEY UPDATE...
   (in php get the last inserted id into variable $eventID)
   (in Mysql) INSERT INTO 'event_times' (...) VALUES ($eventID, ...) ON DUPLICATE KEY UPDATE...

我不是在设计表格时寻求帮助,但正如您所见,插入单个事件至少需要三个插入 每个插入都取决于从前一个中获取 id。这就是为什么我不知道从哪里开始将其变成批处理请求的原因。将这个过程设计成批处理请求的任何帮助都会很棒,谢谢!

编辑:我之前可能已经有位置或事件信息,这就是为什么 ON DUPLICATE KEY UPDATE 部分在那里,如果它已经在数据库中,我会得到旧的 id。直到插入我才知道它是新数据还是数据库中已经存在。 (因为除非我有误解,否则我不能做预先分配 id 的事情,因为这每次都会假设一个新的 id。)

【问题讨论】:

  • 您使用的是什么数据库引擎?请记住,MyISAM 会为每次插入锁定表。
  • 这是 MyISAM,但其他​​被冻结的查询将转到一个完全不同的表,而不是这会影响的表,所以我认为这只是所有插入和索引更新的整体压力。

标签: php mysql performance batch-processing


【解决方案1】:

不要使用自动递增的列 - 在插入之前预先分配您的参考 ID。这样您就可以使用批量插入并删除依赖项。

更新:

  1. 从数据库中选择任何现有的 id(最好是对所有已知数据进行一次选择)。

  2. 丰富数据以使用任何已知的 id 插入。 (为每个项目计算一个键,该键与数据库中表的主键相对应,使用它来使用数据库中的 id 更新项目) - 您希望最终将数据拆分为您知道的项目数据库,因此具有已知的 id - 和数据库中不存在的数据,因此需要密钥分配。我假设您的表有一个主键,而不仅仅是 id - 否则数据库怎么会知道您已经在数据库中拥有数据。

  3. 将新 id 分配给任何没有 id 的记录。

  4. 批量替换数据库中的数据(用一条语句插入多行)。

【讨论】:

  • 有趣,我该怎么做?问题是该位置可能是我已经拥有的位置,并且在 ON DUPLICATED KEY UPDATE 部分发生之前我不知道它是否是新的......
  • 将您的 INSERT INTO 更改为 REPLACE INTO - 然后它会在您的主键匹配时自动更新。
  • 至于您的 id - 为您自己创建一种在 mysql 之外分配唯一引用的方法。
  • 谢谢 不知道那个功能。所以我应该将它们全部替换为 REPLACE INTO,然后将所有这些放入一个批次中?有什么办法可以先插入所有位置,然后插入所有信息,然后始终插入第三个?
  • 用批量插入替换插入最终应该是一个巨大的胜利,因为 mysql 在每个完整的插入语句之后更新索引。您还可以从减少 1 次交易超过 N 次交易的往返时间中受益。如果正确索引,预选不应该很大 - 理想情况下,您可以通过一个查询拉回所有必需的信息。
【解决方案2】:

不了解 MySQL 的详细信息,但任何自尊的 RDBMS 都有所谓的“序列”实体,这些实体旨在用作适用于表主键的唯一值的来源。

以下是使用序列解决问题的方法,这是我多次遇到的问题。使用伪代码:

  1. 开始交易
  2. 从某个序列中选择 3 个下一个值。同样,任何自尊的 RDBMS 都将保证“序列的下一个值”的每个查询都将返回唯一值,适合用作主键。三个选定的值将作为locationsevent_infoevent_times 中新记录的主键;
  3. 使用第一步中的值执行INSERT INTO locations
  4. 使用第一步中的值执行INSERT INTO event_info
  5. 使用第一步中的值执行INSERT INTO event_times
  6. COMMIT 交易,如果一切顺利的话。否则,ROLLBACK 交易。

在一次交易中完成所有INSERTs 至关重要。 如需进一步增强,您可以批量查询。

更新为了满足您对INSERTs 之前可能存在的数据的要求

如果您传入的更新始终包含整组数据:即位置、事件信息和事件时间:然后使用上述方法并删除表中记录的旧实例。这假设您可以SELECT 旧实例使用主键以外的一些数据(即称为域级主键的数据)。不要忘记在同一笔交易中制作DELETE

【讨论】:

  • 如何解决我刚才在 EDIT 中对我的原始问题提到的问题?
  • 谢谢,我对你的回答投了赞成票。几分钟前建议的另一个答案我使用 REPLACE INTO 它将为我执行删除和插入,但如果他没有先回答,我会接受你的回答。谢谢。
  • 再想一想,我可能不得不这样做,因为简单地替换会更改其他表已经拥有的 ID...我希望有一种方法而不必更改这些 ID。 ..你的方法也会让我删除其他表中的其他数据仍然可以指向的旧记录吗?
  • 使用事务加速批量更新。最终的磁盘写入会一直持续到 COMMIT。
  • @hackartist 你必须删除旧记录,对。我假设除了提到的三个表之外没有其他表。如果有其他表引用了locationsevent_infoevent_times 中的某些内容,您也需要更新它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-12
  • 1970-01-01
  • 2013-08-01
相关资源
最近更新 更多