提高mySQL Query在更大的数据库上的速度

时间:2013-04-15 18:55:01

标签: mysql performance

我正在尝试优化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

现在我想从表数据中获取前50个条目,这些条目没有条件dbID = 4,按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 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)会导致条目重复多次。

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

3 个答案:

答案 0 :(得分:0)

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

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

答案 1 :(得分: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条记录。

答案 2 :(得分: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

有没有办法改进这个查询?