【问题标题】:MySQL - just adding ORDER BY an indexed field adds 5 minutes for just 52 records. Where to start?MySQL - 只需添加 ORDER BY 索引字段即可为 52 条记录添加 5 分钟。从哪儿开始?
【发布时间】:2020-05-05 17:56:15
【问题描述】:

编辑 2:现在我们已经优化了数据库并缩小了 MySQL - Why is phpMyAdmin extremely slow with this query that is super fast in php/mysqli?

编辑 1:有两种解决方案对我们有帮助。一个在数据库级别(配置),一个在查询级别。我当然只能接受一个作为最佳答案,但如果您有类似的问题,请同时查看。

我们的数据库多年来一直运行良好。但是,现在,我们有一个我不明白的问题。是 mysql/InnoDB 配置问题吗?而且我们目前没有人进行系统维护(我是程序员)。

TitelDaggegevens 表只有几 Gigs 大小,大约有 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 条记录。在索引字段上。

不知道发生了什么。解释对我没有帮助。我优化/重新创建了表,检查了它,然后重新启动了服务器。一切都无济于事。我绝对不是配置 MySQL 或 InnoDB 的专家,所以我不知道从哪里开始搜索。

我只是希望也许有人能认识到这一点并可以为我指明正确的方向。

SHOW TABLE STATUS WHERE Name = 'TitelDaggegevens' 给我:

我知道这是一个非常模糊的问题,但我无法更具体地确定它。我为慢查询启用了日志记录,但表 slow_log 保持为空。我迷路了。

感谢您提供在哪里寻找的任何想法。

这可能对了解它的人有所帮助,但对我来说不是真的,phpmyadmins'顾问':

在 cmets 中,要求 EXPLAIN 输出做出反应:

1) 没有ORDER BYORDER BY datum(在 WHERE 中并且有索引):

2) 使用ORDER BY 加上除datum 以外的任何字段(是否已编入索引,因此对于快速和慢速查询都一样)。

表结构:

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 

【问题讨论】:

  • 请提供 EXPLAIN 输出
  • 你说得对,反正我应该有。所以我就这么做了。我希望它有所帮助。
  • 考虑学习如何使用此 URL 中的覆盖索引 - blog.toadworld.com/2017/04/06/…
  • 请发布 EXPLAIN 查询的 TEXT 结果、整个慢查询和 SHOW CREATE TABLE (for-each-table);进行分析。查看配置文件、网络配置文件以获取联系信息和免费下载的实用程序脚本以帮助进行性能调整。请发布我们的 findfragtables.sql 的第一两页输出以供分析。 MySQL 的版本?
  • "ORDER BY 另一个索引字段,查询时间增加了 4-6 分钟" -- 这仍然是 52 行输出?让我们看看那个查询和SHOW CREATE TABLE

标签: mysql phpmyadmin innodb


【解决方案1】:
  1. 让它完全处理WHERE

    INDEX(fondskosten, Datum)
    

注意:= 首先是范围。

  1. 获取*。注意:如果您不需要大的TEXTBLOB 列,请拼出SELECT 列表以便避免它们。它们可能被“非记录”存储,因此需要更长的时间来获取。

  2. 一个可选的ORDER BY。如果它在Datum 上,则无需额外的努力。如果它在 any 其他列上,则将进行排序。但是 52 行会很快(毫秒)。

注意事项:

  • 如果你没有fondskosten IS NULL 或者你有一些其他的测试,那么所有的赌注都被取消了。我们必须重新设计最佳综合指数。
  • USE/FORCE INDEX -- 将其用作最后的手段。
  • 在需要讨论查询时始终提供SHOW CREATE TABLE
  • Advisor 有一些好东西,但没有任何“太大”的线索,它是无用的。
  • 怀疑所有其他讨论都没有意识到给定Datum 范围的行数远远超过 52 行。那就是fondskosten IS NULL 确实是问题和解决方案的一部分。

【讨论】:

  • 这些查询实际上是在phpmyadmin中生成的。所以我们几乎无法控制它们!不过还有其他情况。
  • 我们在同一时间找到了相同的索引。此外,一些参数更改也有很大帮助。谢谢你的解释。我会把这个放在脑海里。
  • 我们还对数据库配置进行了专业的研究,这有助于解决所有其他问题。
  • 我们现在唯一不明白的问题是,对类似表的类似查询,如发布的表,仅在 phpmyadmin 中运行缓慢。进程状态一直是“正在发送数据...”很长一段时间。在 php 或直接在 mysql 中,一切都运行得非常顺利。即使没有你的索引。您的 index dus 对 phpmyadmin 有帮助。有什么想法吗?
  • 增加混乱:没有索引,仅在 phpmyadmin 中:使用
【解决方案2】:

对于在类似情况下搜索调整的人来说,这些是专家对数据库所做的调整,大大加快了它的速度(请注意,这是针对具有 100 个表和许多非常复杂和大型查询的数据库,有时会加入超过 15 个表,但不是超大量的记录。数据库只有 37 GB。

[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

(第二个查询系列更轻巧,侵权更少,但仍然可以通过服务器重新计算查询策略来帮助加快某些查询。)

【讨论】:

  • OPTIMIZE TABLE 在大多数情况下对 InnoDB 表无用。在OPTIMIZE 之后运行ANALYZE TABLE 完全没用,因为OPTIMIZE 执行ANALYZE。优化的一个用例是错误DELETE。但是有更好的方法来做到这一点。让我们在另一个问答中讨论那个
  • 你有多少内存?我看到buffer_pool只有2G。
  • 这是 EAV 架构吗?而且是15路自加入?还是其他情况?
  • 这不是记录如何返回;这是感动了多少。这提供了该信息:mysql.rjweb.org/doc.php/index_cookbook_mysql#handler_counts
  • DATA_FREE/DATA_LENGTH 很奇怪——对于innodb_file_per_table=OFF,它说“有空间进行优化”,只是它忽略了Index_length。对于ON,它可能表示也可能不表示任何内容。
【解决方案3】:

看起来 ORDER BY 使用了 3 种不同的优化计划

  1. ORDER BY id - 额外:Using index condition; Using where; Using filesort。 MySQL 使用filesort 来解析ORDER BY。但是行已经排序。因此,需要 15 秒。
  2. ORDER BY Datum 或其他非索引字段 - 额外:Using index condition; Using where。 MySQL 使用Datum 索引来解析ORDER BY。这需要几秒钟。
  3. 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

如果您在SHOW GLOBAL STATUS 输出中看到每秒有很多Sort_merge_passes,您可以考虑增加sort_buffer_size 的值以加快ORDER BYGROUP BY 操作,这些操作无法通过查询优化或改进的索引来改进。 在 Linux 上,有 256KB 和 2MB 的阈值,较大的值可能会显着减慢内存分配,因此您应该考虑保持在其中一个值以下。

【讨论】:

  • 谢谢!我在问题中添加了 EXPLAIN,事实证明 ORDER BY 和 non-ORDER BY/ordering 在 WHERE 中的字段上存在差异,但是对于无延迟和延迟 6 分钟的结果是相同的......跨度>
  • 我之前尝试过增加sort_buffer_size,可惜没有效果。
  • 我试过你的查询。如果您 ORDER BY 任何领域,USE INDEX FOR ORDER BY (datum) 会让一切进展顺利。基本上:告诉 MySQL 忽略所有“适当的”索引会使其运行得更快。 (ORDER BY isbn 使用任何以 isbn 作为第一个字段的索引会很慢,但作为第二个字段或没有它,它会很快。)
  • 所以毫不奇怪:SELECT id FROM TitelDaggegevens WHERE id IN (SELECT id FROM TitelDaggegevens WHERE datum > '20200331' AND datum < 20200501 AND fondskosten IS NULL) ORDER BY isbn 超级快...
  • 我发现 Sergey Petrania 的文章 How MySQL executes ORDER BY 解释了它的工作原理。
猜你喜欢
  • 2013-05-23
  • 1970-01-01
  • 1970-01-01
  • 2017-05-31
  • 1970-01-01
  • 2021-06-23
  • 1970-01-01
  • 2011-11-16
  • 2013-04-30
相关资源
最近更新 更多