MySQL - 只需为索引字段添加 ORDER BY 即可为 52 条记录增加 5 分钟。从哪儿开始?
MySQL - just adding ORDER BY an indexed field adds 5 minutes for just 52 records. Where to start?
编辑 2:现在我们已经优化了数据库并在
中缩小了范围
编辑 1:有两种解决方案对我们有所帮助。一个在数据库级别(配置),一个在查询级别。我当然只能接受一个作为最佳答案,但是如果你有类似的问题,请看两个。
我们有一个多年来 运行 完美无缺的数据库。但是,现在,我们有一个我不明白的问题。是 mysql/InnoDB 配置问题吗?而且我们目前没有人负责系统维护(我是程序员)。
TitelDaggegevens 表有几个 Gig 大小,大约有 12,000,000 条记录,所以没什么特别的。
如果我们这样做:
SELECT *
FROM TitelDaggegevens
WHERE fondskosten IS NULL
AND (datum BETWEEN 20200401 AND 20200430)
它运行良好,在十分之几秒内。
结果:52条记录。
此外,如果我们添加 ORDER BY datum
或者如果我们按任何其他非索引字段排序:一切都很好,速度相同。
但是,如果我添加 ORDER BY id
(id 是主键),对于相同的 52 条记录,查询突然需要 15 秒。
并且当我 ORDER BY
另一个索引字段时,查询时间增加到 4-6 分钟 。用于订购 52 条记录。在索引字段上。
我不知道发生了什么。解释对我没有帮助。我optimized/recreated table,检查了它,然后重新启动了服务器。都无济于事。我绝对不是配置 MySQL 或 InnoDB 的专家,所以我不知道从哪里开始搜索。
我只是希望有人能认识到这一点,并能为我指明正确的方向。
SHOW TABLE STATUS WHERE Name = 'TitelDaggegevens'
给我:
我知道这是一个非常模糊的问题,但我无法更具体地确定它。我为慢速查询启用了日志记录,但 table slow_log 保持为空。我迷路了。
感谢您提供有关查找位置的任何想法。
这可能对了解它的人有帮助,但对我来说不是真的,phpmyadmins 'Advisor':
在评论和反应中要求 EXPLAIN 输出:
1) 没有 ORDER BY
和 ORDER BY datum
(在 WHERE 中并且有一个索引):
2) 使用 ORDER BY
加上除 datum
以外的任何字段(索引或不索引,因此对于快速查询和慢速查询都是一样的)。
table结构:
CREATE TABLE `TitelDaggegevens` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`isbn` decimal(13,0) NOT NULL,
`datum` date NOT NULL,
`volgendeDatum` date DEFAULT NULL,
`prijs` decimal(8,2) DEFAULT NULL,
`prijsExclLaag` decimal(8,2) DEFAULT NULL,
`prijsExclHoog` decimal(8,2) DEFAULT NULL,
`stadiumDienstverlening` char(2) COLLATE utf8mb4_unicode_520_ci DEFAULT NULL,
`stadiumLevenscyclus` char(1) COLLATE utf8mb4_unicode_520_ci DEFAULT NULL,
`gewicht` double(7,3) DEFAULT NULL,
`volume` double(7,3) DEFAULT NULL,
`24uurs` tinyint(1) DEFAULT NULL,
`UitgeverCode` varchar(4) COLLATE utf8mb4_unicode_520_ci DEFAULT NULL,
`imprintId` int(11) DEFAULT NULL,
`distributievormId` tinyint(4) DEFAULT NULL,
`boeksoort` char(1) COLLATE utf8mb4_unicode_520_ci DEFAULT NULL,
`publishingStatus` tinyint(4) DEFAULT NULL,
`productAvailability` tinyint(4) DEFAULT NULL,
`voorraadAlles` mediumint(8) unsigned DEFAULT NULL,
`voorraadBeschikbaar` mediumint(8) unsigned DEFAULT NULL,
`voorraadGeblokkeerdEigenaar` smallint(5) unsigned DEFAULT NULL,
`voorraadGeblokkeerdCB` smallint(5) unsigned DEFAULT NULL,
`voorraadGereserveerd` smallint(5) unsigned DEFAULT NULL,
`fondskosten` enum('depot leverbaar','depot onleverbaar','POD','BOV','eBoek','geen') COLLATE utf8mb4_unicode_520_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ISBN+datum` (`isbn`,`datum`) USING BTREE,
KEY `UitgeverCode` (`UitgeverCode`),
KEY `Imprint` (`imprintId`),
KEY `VolgendeDatum` (`volgendeDatum`),
KEY `Index op voorraad om maxima snel te vinden` (`isbn`,`voorraadAlles`) USING BTREE,
KEY `fondskosten` (`fondskosten`),
KEY `Datum+isbn+fondskosten` (`datum`,`isbn`,`fondskosten`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=16519430 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci
看起来 ORDER BY 使用了 3 个不同的优化计划
ORDER BY id
- 额外:Using index condition; Using where; Using filesort
。 MySQL 使用 filesort
解析 ORDER BY
。但是行已经排序了。因此,需要 15 秒。
ORDER BY Datum
或其他非索引字段 - 额外:Using index condition; Using where
。 MySQL 使用 Datum
索引来解析 ORDER BY
。需要几秒钟。
ORDER BY index_field
- 额外:Using index condition; Using where; Using filesort
。 MySQL 使用 filesort
解析 ORDER BY
。行未排序。需要几分钟。
这是我的建议。只有 EXPLAIN
才能知道发生了什么
Influencing ORDER BY Optimization
更新:
你能用每个 ORDER BY
个子句检查这个查询吗?
SELECT *
FROM TitelDaggegevens USE INDEX FOR ORDER BY (Datum)
WHERE fondskosten IS NULL
AND (Datum BETWEEN 20200401 AND 20200430)
您也可以尝试增加 sort_buffer_size
If you see many Sort_merge_passes
per second in SHOW GLOBAL STATUS
output, you can consider increasing the sort_buffer_size
value to speed up ORDER BY
or GROUP BY
operations that cannot be improved with query optimization or improved indexing.
On Linux, there are thresholds of 256KB and 2MB where larger values may significantly slow down memory allocation, so you should consider staying below one of those values.
用这个来完全处理 WHERE
:
INDEX(fondskosten, Datum)
注意:先是=
,再是范围。
获取 *
。注意:如果有大的 TEXT
或 BLOB
列是您不需要的,请拼写出 SELECT
列表以便您可以避免它们。它们可能已存储 "off-record",因此需要更长的时间来获取。
可选ORDER BY
。如果是在Datum
上,那就没有额外的努力了。如果它在 any 其他列上,则将进行排序。但是52行的排序会非常快(毫秒)。
备注:
- 如果您没有
fondskosten IS NULL
或您有其他测试,那么所有赌注都会被取消。我们必须重新设计最优综合指数。
USE/FORCE INDEX
-- 将其用作最后的手段。
- 在需要讨论查询时始终提供
SHOW CREATE TABLE
。
Advisor
有一些好东西,但不知道"too big"是什么,比较没用。
- 我怀疑 所有其他讨论都没有意识到给定的
Datum
范围有远远超过52 行。也就是说 fondskosten IS NULL
确实是问题和解决方案的一部分。
对于在类似情况下搜索调整的人来说,这些是专家对数据库所做的调整,大大加快了它的速度(请注意,这是针对具有 100 个表和许多非常复杂和大型查询的数据库,有时会加入超过15张表,但记录数不多。数据库只有37GB。
[mysqld]
innodb_buffer_pool_size=2G
innodb_buffer_pool_instances=4
innodb_flush_log_at_trx_commit=2
tmp_table_size=64M
max_heap_table_size=64M
join_buffer_size=4M
sort_buffer_size=8M
optimizer_search_depth=5
optimizer_search_depth 已减少,以最大限度地减少优化器复杂查询所需的时间。
重新启动服务器后,(定期)运行 所有作为 运行 宁此查询结果的查询:
SELECT CONCAT('OPTIMIZE TABLE `', TABLE_SCHEMA , '`.`', TABLE_NAME ,'`;') AS query
FROM INFORMATION_SCHEMA.TABLES
WHERE DATA_FREE/DATA_LENGTH > 2 AND DATA_LENGTH > 4*1024*1024
(如果您有大表,当服务器离线或使用率低时,第一个更好。它会重建并优化需要它的表。)
然后:
SELECT CONCAT('ANALYZE TABLE `', TABLE_SCHEMA , '`.`', TABLE_NAME ,'`;') AS query
FROM INFORMATION_SCHEMA.TABLES
WHERE DATA_FREE/DATA_LENGTH > 2 AND DATA_LENGTH > 1*1024*1024
(这第二个querie-series更轻量且侵权更少,但仍可能通过服务器重新计算查询策略来帮助加快某些查询。)
编辑 2:现在我们已经优化了数据库并在
编辑 1:有两种解决方案对我们有所帮助。一个在数据库级别(配置),一个在查询级别。我当然只能接受一个作为最佳答案,但是如果你有类似的问题,请看两个。
我们有一个多年来 运行 完美无缺的数据库。但是,现在,我们有一个我不明白的问题。是 mysql/InnoDB 配置问题吗?而且我们目前没有人负责系统维护(我是程序员)。
TitelDaggegevens 表有几个 Gig 大小,大约有 12,000,000 条记录,所以没什么特别的。
如果我们这样做:
SELECT *
FROM TitelDaggegevens
WHERE fondskosten IS NULL
AND (datum BETWEEN 20200401 AND 20200430)
它运行良好,在十分之几秒内。
结果:52条记录。
此外,如果我们添加 ORDER BY datum
或者如果我们按任何其他非索引字段排序:一切都很好,速度相同。
但是,如果我添加 ORDER BY id
(id 是主键),对于相同的 52 条记录,查询突然需要 15 秒。
并且当我 ORDER BY
另一个索引字段时,查询时间增加到 4-6 分钟 。用于订购 52 条记录。在索引字段上。
我不知道发生了什么。解释对我没有帮助。我optimized/recreated table,检查了它,然后重新启动了服务器。都无济于事。我绝对不是配置 MySQL 或 InnoDB 的专家,所以我不知道从哪里开始搜索。
我只是希望有人能认识到这一点,并能为我指明正确的方向。
SHOW TABLE STATUS WHERE Name = 'TitelDaggegevens'
给我:
我知道这是一个非常模糊的问题,但我无法更具体地确定它。我为慢速查询启用了日志记录,但 table slow_log 保持为空。我迷路了。
感谢您提供有关查找位置的任何想法。
这可能对了解它的人有帮助,但对我来说不是真的,phpmyadmins 'Advisor':
在评论和反应中要求 EXPLAIN 输出:
1) 没有 ORDER BY
和 ORDER BY datum
(在 WHERE 中并且有一个索引):
2) 使用 ORDER BY
加上除 datum
以外的任何字段(索引或不索引,因此对于快速查询和慢速查询都是一样的)。
table结构:
CREATE TABLE `TitelDaggegevens` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`isbn` decimal(13,0) NOT NULL,
`datum` date NOT NULL,
`volgendeDatum` date DEFAULT NULL,
`prijs` decimal(8,2) DEFAULT NULL,
`prijsExclLaag` decimal(8,2) DEFAULT NULL,
`prijsExclHoog` decimal(8,2) DEFAULT NULL,
`stadiumDienstverlening` char(2) COLLATE utf8mb4_unicode_520_ci DEFAULT NULL,
`stadiumLevenscyclus` char(1) COLLATE utf8mb4_unicode_520_ci DEFAULT NULL,
`gewicht` double(7,3) DEFAULT NULL,
`volume` double(7,3) DEFAULT NULL,
`24uurs` tinyint(1) DEFAULT NULL,
`UitgeverCode` varchar(4) COLLATE utf8mb4_unicode_520_ci DEFAULT NULL,
`imprintId` int(11) DEFAULT NULL,
`distributievormId` tinyint(4) DEFAULT NULL,
`boeksoort` char(1) COLLATE utf8mb4_unicode_520_ci DEFAULT NULL,
`publishingStatus` tinyint(4) DEFAULT NULL,
`productAvailability` tinyint(4) DEFAULT NULL,
`voorraadAlles` mediumint(8) unsigned DEFAULT NULL,
`voorraadBeschikbaar` mediumint(8) unsigned DEFAULT NULL,
`voorraadGeblokkeerdEigenaar` smallint(5) unsigned DEFAULT NULL,
`voorraadGeblokkeerdCB` smallint(5) unsigned DEFAULT NULL,
`voorraadGereserveerd` smallint(5) unsigned DEFAULT NULL,
`fondskosten` enum('depot leverbaar','depot onleverbaar','POD','BOV','eBoek','geen') COLLATE utf8mb4_unicode_520_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ISBN+datum` (`isbn`,`datum`) USING BTREE,
KEY `UitgeverCode` (`UitgeverCode`),
KEY `Imprint` (`imprintId`),
KEY `VolgendeDatum` (`volgendeDatum`),
KEY `Index op voorraad om maxima snel te vinden` (`isbn`,`voorraadAlles`) USING BTREE,
KEY `fondskosten` (`fondskosten`),
KEY `Datum+isbn+fondskosten` (`datum`,`isbn`,`fondskosten`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=16519430 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci
看起来 ORDER BY 使用了 3 个不同的优化计划
ORDER BY id
- 额外:Using index condition; Using where; Using filesort
。 MySQL 使用filesort
解析ORDER BY
。但是行已经排序了。因此,需要 15 秒。ORDER BY Datum
或其他非索引字段 - 额外:Using index condition; Using where
。 MySQL 使用Datum
索引来解析ORDER BY
。需要几秒钟。ORDER BY index_field
- 额外:Using index condition; Using where; Using filesort
。 MySQL 使用filesort
解析ORDER BY
。行未排序。需要几分钟。
这是我的建议。只有 EXPLAIN
才能知道发生了什么
Influencing ORDER BY Optimization
更新:
你能用每个 ORDER BY
个子句检查这个查询吗?
SELECT *
FROM TitelDaggegevens USE INDEX FOR ORDER BY (Datum)
WHERE fondskosten IS NULL
AND (Datum BETWEEN 20200401 AND 20200430)
您也可以尝试增加 sort_buffer_size
If you see many
Sort_merge_passes
per second inSHOW GLOBAL STATUS
output, you can consider increasing thesort_buffer_size
value to speed upORDER BY
orGROUP BY
operations that cannot be improved with query optimization or improved indexing. On Linux, there are thresholds of 256KB and 2MB where larger values may significantly slow down memory allocation, so you should consider staying below one of those values.
用这个来完全处理
WHERE
:INDEX(fondskosten, Datum)
注意:先是=
,再是范围。
获取
*
。注意:如果有大的TEXT
或BLOB
列是您不需要的,请拼写出SELECT
列表以便您可以避免它们。它们可能已存储 "off-record",因此需要更长的时间来获取。可选
ORDER BY
。如果是在Datum
上,那就没有额外的努力了。如果它在 any 其他列上,则将进行排序。但是52行的排序会非常快(毫秒)。
备注:
- 如果您没有
fondskosten IS NULL
或您有其他测试,那么所有赌注都会被取消。我们必须重新设计最优综合指数。 USE/FORCE INDEX
-- 将其用作最后的手段。- 在需要讨论查询时始终提供
SHOW CREATE TABLE
。 Advisor
有一些好东西,但不知道"too big"是什么,比较没用。- 我怀疑 所有其他讨论都没有意识到给定的
Datum
范围有远远超过52 行。也就是说fondskosten IS NULL
确实是问题和解决方案的一部分。
对于在类似情况下搜索调整的人来说,这些是专家对数据库所做的调整,大大加快了它的速度(请注意,这是针对具有 100 个表和许多非常复杂和大型查询的数据库,有时会加入超过15张表,但记录数不多。数据库只有37GB。
[mysqld]
innodb_buffer_pool_size=2G
innodb_buffer_pool_instances=4
innodb_flush_log_at_trx_commit=2
tmp_table_size=64M
max_heap_table_size=64M
join_buffer_size=4M
sort_buffer_size=8M
optimizer_search_depth=5
optimizer_search_depth 已减少,以最大限度地减少优化器复杂查询所需的时间。
重新启动服务器后,(定期)运行 所有作为 运行 宁此查询结果的查询:
SELECT CONCAT('OPTIMIZE TABLE `', TABLE_SCHEMA , '`.`', TABLE_NAME ,'`;') AS query
FROM INFORMATION_SCHEMA.TABLES
WHERE DATA_FREE/DATA_LENGTH > 2 AND DATA_LENGTH > 4*1024*1024
(如果您有大表,当服务器离线或使用率低时,第一个更好。它会重建并优化需要它的表。)
然后:
SELECT CONCAT('ANALYZE TABLE `', TABLE_SCHEMA , '`.`', TABLE_NAME ,'`;') AS query
FROM INFORMATION_SCHEMA.TABLES
WHERE DATA_FREE/DATA_LENGTH > 2 AND DATA_LENGTH > 1*1024*1024
(这第二个querie-series更轻量且侵权更少,但仍可能通过服务器重新计算查询策略来帮助加快某些查询。)