1

我们正在两个数据库表之间进行更新查询,而且速度非常慢。如:执行查询需要 30天。

一个表lab.list 包含大约940,000 条记录,另一个表mind.list 大约3,700,000(370 万)条当满足两个BETWEEN 条件时更新设置一个字段。这是查询:

UPDATE lab.list L , mind.list M SET L.locId = M.locId  WHERE L.longip BETWEEN M.startIpNum AND M.endIpNum AND L.date BETWEEN "20100301" AND "20100401" AND L.locId = 0

就像现在一样,查询执行时大约每 8 秒更新 1 次。

我们还在同一个数据库中使用 mind.list 表进行了尝试,但这对于查询时间无关紧要。

UPDATE lab.list L, lab.mind M  SET L.locId = M.locId  WHERE  longip BETWEEN M.startIpNum AND M.endIpNum AND date BETWEEN "20100301" AND "20100401" AND L.locId = 0;

有没有办法加快这个查询?基本上恕我直言,它应该创建两个数据库子集:mind.list.longip BETWEEN M.startIpNum AND M.endIpNum lab.list.date BETWEEN "20100301" AND "20100401"

然后更新这些子集的值。在某个地方,我认为我犯了一个错误,但在哪里?也许有更快的查询可能?

我们尝试了 log_slow_queries,但这表明它确实检查了数百万行,可能一直上升到 3331 gigarows。

技术资料:

  • 服务器版本:5.5.22-0ubuntu1-log (Ubuntu)
  • lab.list 在 locId、longip、date 上有索引
  • lab.mind 在 locId、startIpNum 和 M.endIpNum 上有索引
  • 硬件:2x xeon 3.4 GHz、4GB RAM、128 GB SSD(所以这应该不是问题!)
4

2 回答 2

1

我首先会尝试按 startIpNum、endIpNum、locId 的顺序对头脑进行索引。locId 不用于 SELECTing from mind,即使它用于更新。

出于同样的原因,我会在 locId、date 和 longip(第一次分块中未使用它,它应该在 date 上运行)这个顺序上为 lab 建立索引。

那么 startIpNum 和 endIpNum 分配了什么样的数据类型呢?对于 IPv4,最好转换为 INTEGER 并使用 INET_ATON 和 INET_NTOA 进行用户 I/O。我假设你已经这样做了。

要运行更新,您可以尝试使用临时表对 M 数据库进行分段。那是:

* select all records of lab in the given range of dates with locId = 0 into a temporary table TABLE1.
* run an analysis on TABLE1 grouping IP addresses by their first N bits (using AND with a suitable mask: 0x80000000, 0xC0000000, ... 0xF8000000... and so on, until you find  that you have divided into a "suitable" number of IP "families". These will, by and large, match with startIpNum (but that's not strictly necessary).
* say that you have divided in 1000 families of IP.
* For each family:
*    select those IPs from TABLE1 to TABLE3.
*    select the IPs matching that family from mind to TABLE2.
*    run the update of the matching records between TABLE3 and TABLE2. This should take place in about one hundred thousandth of the time of the big query.
*    copy-update TABLE3 into lab, discard TABLE3 and TABLE2.
* Repeat with next "family".

这不是很理想,但如果稍微改进的索引没有帮助,我真的看不到那么多选择。

于 2012-07-01T22:28:08.013 回答
0

最后,查询太大或太麻烦,mysql无法填写。即使在索引之后。在高端 Sybase 服务器上用相同的数据测试相同的查询也需要 3 个小时。

所以我们放弃了在数据库服务器上做这一切的想法,回到脚本语言。

我们在python中做了以下事情:

  1. 加载 370 万条记录中的 100000 条记录,并循环遍历行
  2. 对于每一行,设置 locId 并填写其余列

所有这些更新加起来大约需要 5 分钟,这是一个巨大的改进!

结论:

跳出数据库框框思考!

于 2012-07-10T11:29:52.950 回答