[英]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秒的時間內獲取正確的數據,那么我需要使用最快的查詢來獲取數據。
那么,如何優化上述查詢以使其運行更快?
此查詢可能會變慢的原因:
這些使得在不重新構造數據的情況下很難優化此查詢。 可以嘗試的一些建議:
-您可以嘗試使用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.