优化查询

时间:2017-02-09 18:41:12

标签: mysql sql indexing query-performance

如何让我的响应时间更快,大约平均响应时间为0.2秒(我的项目表中有8039条记录,跟踪表中有81条记录)

查询

 SELECT a.name, b.cnt  FROM `items` a  LEFT JOIN 
(SELECT guid, COUNT(*) cnt FROM tracking WHERE
date > UNIX_TIMESTAMP(NOW() - INTERVAL 1 day ) GROUP BY guid)  b ON
a.`id` = b.guid WHERE a.`type` = 'streaming' AND a.`state` = 1 
ORDER BY b.cnt DESC LIMIT 15 OFFSET 75

跟踪表格结构

CREATE TABLE `tracking` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`guid` int(11) DEFAULT NULL,
`ip` int(11) NOT NULL,
`date` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `i1` (`ip`,`guid`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4303 DEFAULT CHARSET=latin1;

项目表格结构

CREATE TABLE `items` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`guid` int(11) DEFAULT NULL,
`type` varchar(255) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`embed` varchar(255) DEFAULT NULL,
`url` varchar(255) DEFAULT NULL,
`description` text,
`tags` varchar(255) DEFAULT NULL,
`date` int(11) DEFAULT NULL,
`vote_val_total` float DEFAULT '0',
`vote_total` float(11,0) DEFAULT '0',
`rate` float DEFAULT '0',
`icon` text CHARACTER SET ascii,
`state` int(11) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9258 DEFAULT CHARSET=latin1;

2 个答案:

答案 0 :(得分:2)

您的查询,如所写,并没有多大意义。它会在两个表中生成所有可能的行组合,然后对它们进行分组。

你可能想要这个:

    SELECT a.*, b.cnt 
      FROM `items` a  
 LEFT JOIN (
              SELECT guid, COUNT(*) cnt 
                FROM tracking 
               WHERE `date` > UNIX_TIMESTAMP(NOW() - INTERVAL 1 day) 
            GROUP BY guid
           )  b ON a.guid = b.guid
  ORDER BY b.cnt DESC

此查询中的高容量数据来自相对较大的跟踪表。因此,您应该使用列(date, guid)为其添加复合索引。这将允许您的查询按date随机访问索引,然后扫描guid值。

 ALTER TABLE tracking ADD INDEX guid_summary (`date`, guid);

我想你会看到一个不错的性能提升。

专业提示:请勿使用SELECT *。而是在结果集中提供所需列的列表。例如,

SELECT a.guid, a.name, a.description, b.cnt  

为什么这很重要?

首先,它使您的软件更具弹性,可以防止将来向您的表添加列。

其次,它告诉MySQL服务器只吊索你想要的信息。这可以非常显着地提高性能,特别是当你的桌子变大时。

答案 1 :(得分:0)

由于tracking的行数明显少于items,因此我将提出以下建议。

SELECT  i.name, c.cnt
    FROM  
    (
        SELECT  guid, COUNT(*) cnt
            FROM  tracking
            WHERE  date > UNIX_TIMESTAMP(NOW() - INTERVAL 1 day )
            GROUP BY  guid 
    ) AS c
    JOIN  items AS i  ON i.id = c.guid
    WHERE  i.type = 'streaming'
      AND  i.state = 1;
    ORDER BY  c.cnt DESC
    LIMIT  15 OFFSET 75

它将无法显示cnt为0的任何项目。(您的版本会显示计数为NULL的项目。)

所需的综合指数:

items: The PRIMARY KEY(id) is sufficient.
tracking: INDEX(date, guid) -- "covering"

其他问题:

  • 如果ip是IP地址,则需要INT UNSIGNED。但这仅涵盖IPv4,而不是IPv6。
  • 似乎date不仅仅是" date",而是真正的约会+时间。请重命名以避免混淆。
  • float(11,0) - 不要将FLOAT用于整数。不要在(m,n)FLOAT上使用DOUBLEINT UNSIGNED在这里更有意义。

OFFSET在性能方面很顽皮 - 它必须扫描跳过的记录。但是,在您的查询中,没有办法避免收集所有可能的行,对它们进行排序,步进超过75,并且最终只能提供15行。 (并且,不超过81,它不会是一个完整的15。)

您使用的是哪个版本? LEFT JOIN ( SELECT ... )的优化已发生重大变化。请为正在讨论的每个查询提供EXPLAIN SELECT