MySQL - 为什么 phpMyAdmin 对于这个在 php/mysqli 中超快的查询非常慢?
MySQL - Why is phpMyAdmin extremely slow with this query that is super fast in php/mysqli?
编辑:另请参阅我的回答,主要区别在于 phpmyadmin 添加的 LIMIT
,但我仍然不不明白 phpmyadmin 还是比 mysqli 慢。
在我们的数据库(+web)服务器上,在 phpmyadmin 中进行查询与在 php 中进行查询时,我们在性能上存在 巨大的 差异(mysqli) 或直接在 mariadb 服务器上。 60 秒 vs < 0.01 秒!
这个查询功能很好:
SELECT * FROM `TitelDaggegevens`
WHERE `datum` > '2020-03-31' AND datum < '2020-05-02' AND `fondskosten` IS NULL
ORDER BY isbn;
但是,只有 在phpMyAdmin 中,当我们将2020-05-02
更改为2020-05-01
时,查询变得非常慢。
SHOW PROCESSLIST
表明查询主要是 Sending data
而 运行.
在 mysql.rjweb.org/doc.php/index_cookbook_mysql#handler_counts 之后,我做了以下查询系列:
FLUSH STATUS;
SELECT-query above with one of the two dates;
SHOW SESSION STATUS LIKE 'Handler%';
差异很吸引人。 (在所有情况下,我都省略了所有等于 0 的值)。并且随着时间的推移保持一致。
| how: | server/MySqli | phpMyAdmin
| date used in query: | 2020-05-02 | 2020-05-01 | 2020-05-02 | 2020-05-01
| records returned: | 6912 | 1 | 6912 | 1
| avg speed: | 0.27s | 0.00s | 0.52s | 60s (!)
| Variable_name | Value | Value | Value | Value
| Handler_icp_attempts | 213197 | 206286 | 213197 | 0
| Handler_icp_match | 6912 | 1 | 6912 | 0
| Handler_read_next | 6912 | 1 | 26651 | 11728896 (!)
| Handler_read_key | 1 | 1 | 151 | 4
| Handler_commit | 1 | 1 | 152 | 5
| Handler_read_first | 0 | 0 | 1 | 1
| Handler_read_rnd_next | 0 | 0 | 82 | 83
| Handler_read_rnd | 0 | 0 | 0 | 1
| Handler_tmp_write | 0 | 0 | 67 | 67
EXPLAIN 结果在所有情况下都是相同 (phpmyadmin/mysqli/putty+mariadb)。
[select_type] => SIMPLE
[table] => TitelDaggegevens
[type] => range
[possible_keys] => fondskosten,Datum+isbn+fondskosten
[key] => Datum+isbn+fondskosten
[key_len] => 3
[ref] =>
[Extra] => Using index condition; Using filesort
唯一的区别在于行数:
[rows] => 422796 for 2020-05-01
[rows] => 450432 for 2020-05-02
问题
您能否给我们任何 方向 我们应该寻求解决这个问题的地方?我们已经工作了一个星期来优化 mariadb 服务器(现在是最佳的,除了 phpmyadmin)并将我们的一些问题缩小到下面的示例。我们经常使用 phpmyadmin,但对表面下的内容(比如它如何连接到数据库)几乎没有经验。
关于indexing/ordering
在慢查询中,如果我们将ORDER BY
从索引isbn
字段更改为非索引字段或完全省略ORDER BY
,一切都会如常闪电再次提速。将 ORDER BY
更改为主键 id
也会使其变慢,但仍然比索引 isbn
字段快 10 倍。
我们*知道*我们可以通过更好的索引来解决这个特定的查询,我们已经准备好实施它。但是,我们想知道是什么原因导致 phpmyadmin 与 mysqli/directly.
中的时间不同
详情:
TitelDaggegevens 包含 < 1100 万条记录,甚至不到 3Gb,并且已经过优化(重建)
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
我们的虚拟网站+数据库+邮件服务器的配置:
MariaDB 10.4
InnoDB
CentOs7
phpMyAdmin 4.9.5
php 5.6
Apache
一些重要的 mariadb 配置参数,我们从我们的虚拟网络服务器的默认配置中更改:
[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
最大的区别是 当然 phpmyadmin 向查询添加了一个 LIMIT。这给出了主要的解释。我不敢相信那不是我们尝试的第一件事,我很尴尬。
不过phpMyAdmin和mysqli的速度差距还是很大的,结果还是不一样(2020-05-01 on server or mysqli):
+----------------------------+----------+
| Variable_name | Value |
+----------------------------+----------+
| Handler_commit | 1 |
| Handler_read_first | 1 |
| Handler_read_next | 11733306 |
| rest | 0 |
+----------------------------+----------+
limit
和 2020-05-02 的速度:都在 0.17-0.2 左右
limit
和 2020-05-01 的速度:
php/mysqli:声称:3.5 秒但页面加载大约 30 秒
putty/mariadb:声称也需要 3.5 秒,但会在大约 30 秒后显示结果
phpmyadmin:声称和实时大约 60 秒
此外,EXPLAIN 确实随着 LIMIT 发生了很大变化:
(第 1268 行数据<20200501 和第 1351 行数据<20200502)
+------+-------------+------------------+-------+------------------------------------+------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+------------------+-------+------------------------------------+------------+---------+------+------+-------------+
| 1 | SIMPLE | TitelDaggegevens | index | fondskosten,Datum+isbn+fondskosten | ISBN+datum | 9 | NULL | 1351 | Using where |
+------+-------------+------------------+-------+------------------------------------+------------+---------+------+------+-------------+
考虑 optimizer_search_depth=16 而不是 5
和
SELECT * 来自 TitelDaggegevens
WHERE datum
BETWEEN '2020-03-31' AND '2020-05-02' AND fondskosten
IS NULL
按 isbn 订购;
除您的所有提示外,我们还请专家对其进行了检查。
经过多次测试后发现,phpMyAdmin 添加的 LIMIT 0,25
是造成极度延迟的唯一因素。专家发现 mysqli/phpmyadmin 和直接在 mariadb 服务器上执行没有区别。
有时查询中的一个非常小的差异(比如为一个 returns 只有一条记录的查询添加 LIMIT)可能导致查询花费 100.000 的时间,因为它会扫描整个索引,因为引擎将看到适合该查询的另一种策略。这是标准行为。
我们已经找到了消除这个特定问题的索引,现在我们也确信我们的数据库没有任何问题。我们不确定的事情,因为它似乎是极端行为。所以:无事生非。
但是我从这次经历中学到了很多东西。既来自我们的专家,也来自这个社区。我了解了 MySQL 诊断、日志记录、mariaDB 如何处理查询...对于每一个结果证明不是问题的诊断,我都学会了在表、索引或查询中要避免或努力的事情。
谢谢大家,尤其是@Rick James、@Wilson Hauck 和@ExploitFate
(我来晚了。很高兴看到你 "resolved" 它。)
你发现了一个奇怪的,调查的很好
有没有办法从 phpmyadmin 获取 EXPLAIN
?如果是这样,那可能会提供另一条线索。
处理程序编号强烈暗示使用了不同的 EXPLAIN
。
显然 phpmyadmin 修改了查询(至少通过添加 LIMIT
)。我想知道它是否不小心弄乱了查询。您当时是否打开了 Slowlog 或 General 日志?两者都应该有 SQL as 运行.
将 (fondskosten)
上的索引替换为 INDEX(fondskosten, datum)
应该会提高性能。
("Sending data",一如既往,是引擎提供的无用信息。)
建议使用 mariadb.com 提交错误。
编辑:另请参阅我的回答,主要区别在于 phpmyadmin 添加的 LIMIT
,但我仍然不不明白 phpmyadmin 还是比 mysqli 慢。
在我们的数据库(+web)服务器上,在 phpmyadmin 中进行查询与在 php 中进行查询时,我们在性能上存在 巨大的 差异(mysqli) 或直接在 mariadb 服务器上。 60 秒 vs < 0.01 秒!
这个查询功能很好:
SELECT * FROM `TitelDaggegevens`
WHERE `datum` > '2020-03-31' AND datum < '2020-05-02' AND `fondskosten` IS NULL
ORDER BY isbn;
但是,只有 在phpMyAdmin 中,当我们将2020-05-02
更改为2020-05-01
时,查询变得非常慢。
SHOW PROCESSLIST
表明查询主要是 Sending data
而 运行.
在 mysql.rjweb.org/doc.php/index_cookbook_mysql#handler_counts 之后,我做了以下查询系列:
FLUSH STATUS;
SELECT-query above with one of the two dates;
SHOW SESSION STATUS LIKE 'Handler%';
差异很吸引人。 (在所有情况下,我都省略了所有等于 0 的值)。并且随着时间的推移保持一致。
| how: | server/MySqli | phpMyAdmin
| date used in query: | 2020-05-02 | 2020-05-01 | 2020-05-02 | 2020-05-01
| records returned: | 6912 | 1 | 6912 | 1
| avg speed: | 0.27s | 0.00s | 0.52s | 60s (!)
| Variable_name | Value | Value | Value | Value
| Handler_icp_attempts | 213197 | 206286 | 213197 | 0
| Handler_icp_match | 6912 | 1 | 6912 | 0
| Handler_read_next | 6912 | 1 | 26651 | 11728896 (!)
| Handler_read_key | 1 | 1 | 151 | 4
| Handler_commit | 1 | 1 | 152 | 5
| Handler_read_first | 0 | 0 | 1 | 1
| Handler_read_rnd_next | 0 | 0 | 82 | 83
| Handler_read_rnd | 0 | 0 | 0 | 1
| Handler_tmp_write | 0 | 0 | 67 | 67
EXPLAIN 结果在所有情况下都是相同 (phpmyadmin/mysqli/putty+mariadb)。
[select_type] => SIMPLE
[table] => TitelDaggegevens
[type] => range
[possible_keys] => fondskosten,Datum+isbn+fondskosten
[key] => Datum+isbn+fondskosten
[key_len] => 3
[ref] =>
[Extra] => Using index condition; Using filesort
唯一的区别在于行数:
[rows] => 422796 for 2020-05-01
[rows] => 450432 for 2020-05-02
问题
您能否给我们任何 方向 我们应该寻求解决这个问题的地方?我们已经工作了一个星期来优化 mariadb 服务器(现在是最佳的,除了 phpmyadmin)并将我们的一些问题缩小到下面的示例。我们经常使用 phpmyadmin,但对表面下的内容(比如它如何连接到数据库)几乎没有经验。
关于indexing/ordering
在慢查询中,如果我们将ORDER BY
从索引isbn
字段更改为非索引字段或完全省略ORDER BY
,一切都会如常闪电再次提速。将 ORDER BY
更改为主键 id
也会使其变慢,但仍然比索引 isbn
字段快 10 倍。
我们*知道*我们可以通过更好的索引来解决这个特定的查询,我们已经准备好实施它。但是,我们想知道是什么原因导致 phpmyadmin 与 mysqli/directly.
中的时间不同详情:
TitelDaggegevens 包含 < 1100 万条记录,甚至不到 3Gb,并且已经过优化(重建)
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
我们的虚拟网站+数据库+邮件服务器的配置:
MariaDB 10.4
InnoDB
CentOs7
phpMyAdmin 4.9.5
php 5.6
Apache
一些重要的 mariadb 配置参数,我们从我们的虚拟网络服务器的默认配置中更改:
[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
最大的区别是 当然 phpmyadmin 向查询添加了一个 LIMIT。这给出了主要的解释。我不敢相信那不是我们尝试的第一件事,我很尴尬。
不过phpMyAdmin和mysqli的速度差距还是很大的,结果还是不一样(2020-05-01 on server or mysqli):
+----------------------------+----------+
| Variable_name | Value |
+----------------------------+----------+
| Handler_commit | 1 |
| Handler_read_first | 1 |
| Handler_read_next | 11733306 |
| rest | 0 |
+----------------------------+----------+
limit
和 2020-05-02 的速度:都在 0.17-0.2 左右
limit
和 2020-05-01 的速度:
php/mysqli:声称:3.5 秒但页面加载大约 30 秒
putty/mariadb:声称也需要 3.5 秒,但会在大约 30 秒后显示结果
phpmyadmin:声称和实时大约 60 秒
此外,EXPLAIN 确实随着 LIMIT 发生了很大变化:
(第 1268 行数据<20200501 和第 1351 行数据<20200502)
+------+-------------+------------------+-------+------------------------------------+------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+------------------+-------+------------------------------------+------------+---------+------+------+-------------+
| 1 | SIMPLE | TitelDaggegevens | index | fondskosten,Datum+isbn+fondskosten | ISBN+datum | 9 | NULL | 1351 | Using where |
+------+-------------+------------------+-------+------------------------------------+------------+---------+------+------+-------------+
考虑 optimizer_search_depth=16 而不是 5
和
SELECT * 来自 TitelDaggegevens
WHERE datum
BETWEEN '2020-03-31' AND '2020-05-02' AND fondskosten
IS NULL
按 isbn 订购;
除您的所有提示外,我们还请专家对其进行了检查。
经过多次测试后发现,phpMyAdmin 添加的 LIMIT 0,25
是造成极度延迟的唯一因素。专家发现 mysqli/phpmyadmin 和直接在 mariadb 服务器上执行没有区别。
有时查询中的一个非常小的差异(比如为一个 returns 只有一条记录的查询添加 LIMIT)可能导致查询花费 100.000 的时间,因为它会扫描整个索引,因为引擎将看到适合该查询的另一种策略。这是标准行为。
我们已经找到了消除这个特定问题的索引,现在我们也确信我们的数据库没有任何问题。我们不确定的事情,因为它似乎是极端行为。所以:无事生非。
但是我从这次经历中学到了很多东西。既来自我们的专家,也来自这个社区。我了解了 MySQL 诊断、日志记录、mariaDB 如何处理查询...对于每一个结果证明不是问题的诊断,我都学会了在表、索引或查询中要避免或努力的事情。
谢谢大家,尤其是@Rick James、@Wilson Hauck 和@ExploitFate
(我来晚了。很高兴看到你 "resolved" 它。)
你发现了一个奇怪的,调查的很好
有没有办法从 phpmyadmin 获取 EXPLAIN
?如果是这样,那可能会提供另一条线索。
处理程序编号强烈暗示使用了不同的 EXPLAIN
。
显然 phpmyadmin 修改了查询(至少通过添加 LIMIT
)。我想知道它是否不小心弄乱了查询。您当时是否打开了 Slowlog 或 General 日志?两者都应该有 SQL as 运行.
将 (fondskosten)
上的索引替换为 INDEX(fondskosten, datum)
应该会提高性能。
("Sending data",一如既往,是引擎提供的无用信息。)
建议使用 mariadb.com 提交错误。