MySQL 8.0.15 大坝慢选查询
Posted
技术标签:
【中文标题】MySQL 8.0.15 大坝慢选查询【英文标题】:MySQL 8.0.15 Dam slow select query 【发布时间】:2019-08-21 08:43:27 【问题描述】:查询的主旨很简单,找出外键列上的最后一个条目。 我可以说的伪代码是
select vehicleid , last_journey_point , last_journey_time from journeyTable.
这是我的 SQL 语句
-- loconumber 是一个索引列 -- Journeyserla 是一个自动编号的主键 int(11) -- 表 locojourney 包含 400,000 条记录
-- 下面的代码块在 19 秒内执行
with LocomotiveLastRun AS(
-- this block of code runs in 0.016 sec
SELECT locojourney.loconumber , MAX(locojourney.journeyserla) as lastrunid
FROM locojourney GROUP BY loconumber)
SELECT locojourney.CurrentCombiners , locojourney.JourneySerla ,
locojourney.From_RunPoint , locojourney.NEXT_RunPoint
FROM LocomotiveLastRun FORCE INDEX(lastrunid)
JOIN locojourney FORCE INDEX(PRIMARY) ON x.lastrunid = locojourney.journeyserla
WHERE locojourney.ishoc = 'n'
EXPLAIN 命令显示一个派生表,它不使用索引,使用 where 并键入 ALL
这是表定义:
-- SHOW CREATE TABLE locojourney
CREATE TABLE `locojourney` (
`trainID` smallint(5) NOT NULL,
`LocoNumber` varchar(5) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`CurrentLocoBase` varchar(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
`CurrentDuedate` date DEFAULT NULL,
`LocoConsist` varchar(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`CurrentLocoDomain` varchar(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
`DomainChange` varchar(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`FEDR` enum('N','Y') CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT 'N',
`LADR` enum('N','Y') CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT 'N',
`ISBANKER` enum('N','Y') CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT 'N',
`TrainName` varchar(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`WithOutLoad` enum('N','Y') CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT 'N',
`runRoute` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`From_RunPoint` varchar(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`From_RunTime` datetime NOT NULL,
`NEXT_RunPoint` varchar(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`NEXT_RunTime` datetime NOT NULL,
`Affects_Outage` enum('N','Y') CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT 'N',
`Affects_Mileage` enum('N','Y') CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT 'N',
`GroundDistance` double(5,2) DEFAULT '0.00',
`SHGallowance` int(11) DEFAULT '0',
`Outage` double(5,4) DEFAULT '0.0000',
`UnderServiceType` enum('FHT','CHG','DEP','MIX','DETN') CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT 'FHT',
`SubServiceHead` varchar(25) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT 'RUN',
`IShoc` enum('N','Y') CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT 'N',
`CurrentCombiners` varchar(28) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
`RunSetSerla` varchar(25) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
`JourneySerla` int(11) NOT NULL AUTO_INCREMENT,
`NominationSerla` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
`Traction` enum('DSL','AC') CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT 'DSL',
`Trainload` smallint(4) NOT NULL DEFAULT '0',
`LeadAssist` enum('Y','N') CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT 'N',
`DEO` varchar(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
`DEOtime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`JourneySerla`),
KEY `trainID` (`trainID`) USING BTREE,
KEY `routesection_idx` (`runRoute`) USING BTREE,
KEY `loconumber_idx` (`LocoNumber`) USING BTREE,
KEY `runsetserla_idx` (`RunSetSerla`) USING BTREE,
KEY `subservicehead_idx` (`SubServiceHead`) USING BTREE,
CONSTRAINT `locojourney_ibfk_1` FOREIGN KEY (`SubServiceHead`) REFERENCES `ineffective` (`IneffectiveHead`) ON UPDATE CASCADE,
CONSTRAINT `locojourney_ibfk_3` FOREIGN KEY (`runRoute`) REFERENCES `routesections` (`Sectionname`) ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT `loconumber_fk` FOREIGN KEY (`LocoNumber`) REFERENCES `lococontainer` (`LocoNumber`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=345719 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
【问题讨论】:
您没有向我们提供足够的信息来帮助您。请阅读这个。 meta.***.com/a/271056 请特别注意查询性能部分。那么请edit你的问题。 添加适当的数据样本和预期结果 我担心查询的性能不佳,mysql 说 mysql 每秒能够查询 1400 万条记录,但在我的情况下它只有 40 万条记录。 需要正确的索引才能使任何 SQL 系统在超过几千行的表上运行良好。为了建议正确的索引,我们需要更多地了解您的情况。FORCE INDEX()
几乎从来没有帮助过,当然在 CTE 上也没有帮助,因为 CTE 本身就缺乏索引。
JOIN 在主键列上执行,实际上应该没有任何异常
【参考方案1】:
with LocomotiveLastRun AS(
-- this block of code runs in 0.016 sec
SELECT locojourney.loconumber , MAX(locojourney.journeyserla) as lastrunid
FROM locojourney
GROUP BY loconumber)
为什么这个 CTE 子查询很快?因为您的表已经在(loconumber, journeyserla)
上有一个索引。 (InnoDb 自动将主键附加到每个索引。)该查询可以通过对该索引的松散索引扫描来满足,而且速度很快。
现在是您的主要查询:
-
摆脱
FORCE INDEX()
。除非你有至少十年的 SQL 经验或者你已经阅读了 MySQL 中 InnoDB 索引的源代码,否则不要梦想使用它。值得注意的是,它在 CTE 上完全没用,因为 CTE 没有索引。
为清楚起见,将主(详细)表放在首位,将 CTE 放在第二位。
为清楚起见,将 JOIN 重铸为 WHERE...IN...
这三个建议给了我们这个:
WITH LocomotiveLastRun AS (...)
SELECT locojourney.CurrentCombiners , locojourney.JourneySerla ,
locojourney.From_RunPoint , locojourney.NEXT_RunPoint
FROM locojourney
WHERE journeyserla IN (SELECT lastrunid FROM LocomotiveLastRun)
AND locojourney.ishoc = 'n'
现在,很明显什么索引可以帮助这个查询。
(ishoc)
上的索引会有所帮助。 (它实际上是一个索引,因为 InnoDB 在 (ishoc, journeyserla)
上,所以它对 WHERE
条件都有帮助。)查询计划器使用 BTREE 随机访问来查找具有 ishoc
值 'n'
的第一个索引行,然后扫描主键的值以将它们与IN
子句匹配。
代替该索引,覆盖查询的复合索引将提供更多帮助。这样的覆盖索引特别有用,因为表的每一行都很大,有很多列。该索引提到了WHERE
子句中的列以及您要选择的列,如下所示:
(ishoc, journeyserla, CurrentCombiners, From_RunPoint, NEXT_RunPoint)
查询计划器可以完全从索引中满足您的查询,从而节省磁盘读取时间来满足查询。如果你经常使用你的查询,这个索引是个好主意。但是,它确实会消耗磁盘空间并稍微减慢INSERT
和UPDATE
的操作速度。
阅读https://use-the-index-luke.com/
【讨论】:
要求不超过 1 秒,因为这是一个几乎实时的应用程序,所以这个查询在 30 秒内运行了几乎 1 次 我可以对这种情况下的桌子设计提出建议吗,我的意思是专业人士在现实世界的应用程序中会做什么 使用IN ( SELECT ...)
通常效率很低。您编写查询的方式意味着遍历所有 400K 行,根据派生表中的 1217 检查每一行。我会预计这是低效的。
"不要梦想使用 FORCE INDEX ..." -- 是的,这是一场噩梦!
如果n
是ishoc
的共同点,则不会使用INDEX(ishoc)
。可以使用以 ishoc 开头的复合索引。【参考方案2】:
试试这个:
SELECT lj.CurrentCombiners , lj.JourneySerla , lj.From_RunPoint , lj.NEXT_RunPoint
FROM ( SELECT MAX(journeyserla) as lastrunid
FROM locojourney
GROUP BY loconumber
) AS llr
JOIN locojourney AS lj ON llr.lastrunid = lj.journeyserla
WHERE lj.ishoc = 'n'
(计时并提供EXPLAIN
)
【讨论】:
以上是关于MySQL 8.0.15 大坝慢选查询的主要内容,如果未能解决你的问题,请参考以下文章