簡體   English   中英

優化查詢

[英]Optimize a query

如何繼續使響應時間更快,大約平均響應時間為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;

按照您的書面查詢,沒有多大意義。 它會在您的兩個表中產生所有可能的行組合,然后對其進行分組。

您可能想要這樣:

    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服務器僅傳遞所需的信息。 這可以極大地提高性能,尤其是當您的桌子很大時。

由於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不僅僅是一個“日期”,而且實際上是一個日期+時間。 請重命名以避免混淆。
  • float(11,0) -請勿對整數使用FLOAT 不要在FLOATDOUBLE上使用(m,n) INT UNSIGNED在這里更有意義。

就性能而言, OFFSET是頑皮的-它必須掃描跳過的記錄。 但是,在您的查詢中,無法避免收集所有可能的行,對其進行排序,超過75行,最后只能交付15行。 (而且,總數不超過81,則不會是完整的15。)

您使用什么版本? LEFT JOIN ( SELECT ... )的優化進行了重要更改。 請為討論中的每個查詢提供EXPLAIN SELECT

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM