簡體   English   中英

MySQL:如何優化這個簡單的GROUP BY + ORDER BY查詢?

[英]MySQL: How to optimize this simple GROUP BY+ORDER BY query?

我有一個mysql表:

CREATE TABLE IF NOT EXISTS `test` (
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`SenderId` int(10) unsigned NOT NULL,
`ReceiverId` int(10) unsigned NOT NULL,
`DateSent` datetime NOT NULL,
`Notified` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`Id`),
KEY `ReceiverId_SenderId` (`ReceiverId`,`SenderId`),
KEY `SenderId` (`SenderId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

該表中填充有10.000個隨機行,以使用以下過程進行測試:

DELIMITER //
CREATE DEFINER=`root`@`localhost` PROCEDURE `FillTest`(IN `cnt` INT)
BEGIN
DECLARE i INT DEFAULT 1;

DECLARE intSenderId INT;
DECLARE intReceiverId INT;
DECLARE dtDateSent DATE;

DECLARE blnNotified INT;

WHILE (i<=cnt) DO
SET intSenderId = FLOOR(1 + (RAND() * 50));
SET intReceiverId = FLOOR(51 + (RAND() * 50));
SET dtDateSent = str_to_date(concat(floor(1 + rand() * (12-1)),'-',floor(1 + rand() * (28 -1)),'-','2008'),'%m-%d-%Y');

SET blnNotified = FLOOR(1 + (RAND() * 2))-1;

INSERT INTO test (SenderId, ReceiverId, DateSent, Notified)
VALUES(intSenderId,intReceiverId,dtDateSent, blnNotified);

SET i=i+1;
END WHILE;

END//
DELIMITER ;
CALL `FillTest`(10000);

問題:

我需要編寫一個查詢,該查詢將按“ SenderId,ReceiverId”分組,並返回每個組的前100個最高ID按ID升序排列

我使用了GROUP BY,ORDER BY和MAX(Id),但查詢速度太慢,因此我提出了以下查詢:

SELECT SQL_NO_CACHE t1.*
FROM test t1
LEFT JOIN test t2 ON (t1.ReceiverId = t2.ReceiverId AND t1.SenderId = t2.SenderId AND     t1.Id < t2.Id)
WHERE t2.Id IS NULL
ORDER BY t1.Id ASC
LIMIT 100;

上面的查詢返回正確的數據,但是當test表的行數超過150.000時,它將變得太慢。 在150.000行上,上述查詢需要7秒才能完成。 我期望test表具有500.000 – 1M行,並且查詢需要在不到3秒的時間內返回正確的數據。 如果無法在不到3秒的時間內獲取正確的數據,那么我需要使用最快的查詢來獲取數據。

那么,如何優化上述查詢以使其運行更快?

此查詢可能會變慢的原因:

  • 很多數據。 很多可能會退回。 它返回每個SenderId / ReceiverId組合的最后一條記錄。
  • 數據的划分(許多發送者/接收者組合,或它們的相對較少,但具有多個“版本”。
  • 整個結果集必須按MySQL排序,因為您需要前100條記錄(按ID排序)。

這些使得在不重新構造數據的情況下很難優化此查詢。 可以嘗試的一些建議:
-您可以嘗試使用NOT EXISTS ,盡管我懷疑這是否有幫助。

SELECT SQL_NO_CACHE t1.*
FROM test t1
WHERE NOT EXISTS
    (SELECT 'x' 
    FROM test t2 
    WHERE t1.ReceiverId = t2.ReceiverId AND t1.SenderId = t2.SenderId AND t1.Id < t2.Id)
ORDER BY t1.Id ASC
LIMIT 100;


-您可以嘗試在ReceiverId,SenderId和Id上使用適當的索引。 嘗試在三列上創建組合索引。 嘗試兩個版本,一個版本的ID是第一列,另一個版本的ID是最后一列。

稍作數據庫修改: -您可以將SenderId / ReceiverId的組合保存在單獨的表中,並且LastId指向所需的記錄。 -您可以為每條記錄保存一個'PreviousId',對於每個發件人/收件人的最后一條記錄,將其保留為NULL。 您只需要查詢previousId為null的記錄。

暫無
暫無

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

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