3

我正在尝试优化 SQL 查询以希望提高其执行速度。

我有以下两个表:

CREATE TABLE IF NOT EXISTS `data` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `entry` varchar(255) NOT NULL,
  `numDB` int(11) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `entry` (`entry`),
  UNIQUE KEY `entry_numDB` (`entry`,`numDB`),
  UNIQUE KEY `entry_numDB_id` (`id`,`entry`,`numDB`),
  KEY `numDB` (`numDB`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1;

CREATE TABLE IF NOT EXISTS `details` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `dataID` bigint(20) NOT NULL,
  `dbID` int(11) NOT NULL,
  <removed - unimportant>
  PRIMARY KEY (`id`),
  KEY `dataID` (`dataID`),
  KEY `dbID` (`dbID`),
  KEY `dataID_dbID` (`dataID`,`dbID`),
  <removed - unimportant>
) ENGINE=InnoDB  DEFAULT CHARSET=latin1;

一个条目(例如,'abc')以“id = 1; entry = abc, numDB = 2”的形式存储在表数据中,并且(至少)有两个详细条目“id = 1, dataID = 1, dbID = 4 " 和 "id = 2, dataID = 1, dbID = 17",但是相同的 dataID 和 dbID 可以出现多次,例如,"id = 3, dataID = 1, dbID = 17", "id = 4, dataID = 1,dbID = 17"。

表数据中的
总条目数:45.245.438 表详细信息中的总条目数:126.608.661

现在我想从不具有条件 dbID = 4 的表数据中获取前 50 个条目,按 data.numDB 排序。结果查询是:

SELECT DISTINCT(data.entry), data.numDB FROM blacklists.data data INNER JOIN blacklists.details details ON details.dbID NOT IN (4) AND data.id = details.dataID ORDER BY data.numDB DESC LIMIT 50

这(至少)需要 10 分钟的处理时间(我在 10 分钟后停止了它)。

这是 EXPLAIN 的输出:

EXPLAIN SELECT DISTINCT(data.entry), data.numDB FROM blacklists.data data INNER JOIN blacklists.details details ON details.dbID NOT IN (4) AND data.id = details.dataID ORDER BY data.numDB DESC LIMIT 50

id  select_type  table    type   possible_keys            key         key_len  ref                rows      Extra
1   SIMPLE       data     index  PRIMARY,entry_numDB_id   entry_numDB 261      NULL               45166874  Using index; Using temporary; Using filesort
1   SIMPLE       details  ref    dataID,dbID,dataID_dbID  dataID      8        blacklists.data.id  1        Using where; Distinct

不使用 DISTINCT(或 GROUP BY)会导致条目被重复多次。

有没有办法改进这个查询?我已经阅读了许多帮助页面和其他问题及其答案,但无法为这些表格找到解决方案。

4

3 回答 3

0

加入细节有点搞砸你。您并没有真正使用 dbID != 4 过滤掉太多内容,因此它仍然必须扫描大部分表“数据”。这是一个子查询可以帮助您的领域。而不是查询所有表“数据”以获取有效的最后 50 行 + 使用 dbID == 4 连接表“详细信息”的任何行,可能手动限制到最后几千行,或者任何近似值一个 dbID == 4

我认为通过这样编写查询,您会看到很大的性能提升:

SELECT  DISTINCT(data.entry), 
        data.numDB 
FROM
    (
        SELECT x.entry, x.numDB, x.numDB
        FROM blacklists.data x
        ORDER BY x.numDB DESC LIMIT 2000
    ) data
INNER JOIN blacklists.details details 
    ON details.dbID NOT IN (4) 
    AND data.id = details.dataID 
ORDER BY data.numDB DESC LIMIT 50

向上或向下更改子选择中的限制以满足您的需要。较小的值会更快地运行此查询,但可能无法得出您想要的 50 条记录,较大的值会运行得更慢,但会给您更好的机会获得所需的 50 条记录。

于 2013-04-15T22:06:09.603 回答
0

我想知道您是否对以下查询也有建议:

详细信息表与上面相同,我还有

CREATE TABLE IF NOT EXISTS `ips` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `dataID` bigint(20) NOT NULL,
  `ip` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `dataID_ip` (`dataID`,`ip`),
  KEY `dataID` (`dataID`),
  KEY `ip` (`ip`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1;

我现在想从该表中获取不在 dbID = 4 中的所有 IP,按其计数(IP)排序:

我要做的是:

SELECT ip.id, ip.ip, count(ip.ip)
FROM
    (
        SELECT x.id, x.ip, x.dataID
        FROM ips x
        GROUP BY x.ip ORDER BY COUNT(x.ip) DESC LIMIT 1000
    ) ip
INNER JOIN details details 
    ON details.dbID NOT IN (4) 
    AND ip.dataID = details.dataID 
GROUP BY ip.ip ORDER BY COUNT(ip.ip) DESC LIMIT 50

但是内部 SELECT 需要全表扫描。

1 PRIMARY <derived2> ALL       NULL    NULL    NULL    NULL    1000     Using temporary; Using filesort
1 PRIMARY details    ref       dataID,dbID,dataID_dbID,dataID_active,dbID_active_...     dataID     8     ip.dataID     1     Using where
2 DERIVED x          index     NULL    ip     4     NULL    8960260     Using temporary; Using filesort

有什么办法也可以改进这个查询吗?

于 2013-04-16T11:33:53.553 回答
0

首先,我将更dbID NOT IN (4)改为dbID <> 4. MySQL 可能会正确优化它,但我想确定一下。

其次,我会考虑对数据进行一些反规范化,在其中放置一个data代表所需条件的字段,允许您进行单表查询(这将更快地工作)。该字段既可以在应用程序中维护,也可以通过触发器维护。

于 2013-04-15T21:15:20.567 回答