【发布时间】:2019-12-22 07:55:48
【问题描述】:
我有 2 个表(页面和 cmets),每个表大约 130 000 行。
我想列出没有任何 cmets 的页面(外键是 cmets.page_id)
如果我执行正常的左外连接,它会花费惊人的超过 750 秒来运行。 (130k^2 = 17B)。而如果我执行相同的连接,但对表使用子查询,则只需 1 秒。
服务器版本:5.6.44-log - MySQL 社区服务器 (GPL):
查询 1. 普通连接,750+ 秒
SELECT p.id
FROM `pages` AS p
LEFT JOIN `comments` AS c
ON p.id = c.page_id
WHERE c.page_id IS NULL
GROUP BY 1
查询 2. 将第一个表作为子查询加入,时间过长
SELECT p.id
FROM (
SELECT id FROM `pages`
) AS p
LEFT JOIN `comments` AS c
ON p.id = c.page_id
WHERE c.page_id IS NULL
GROUP BY 1
查询 3. 以子查询形式加入第二个表,1.6 秒
SELECT p.id
FROM `pages` AS p
LEFT JOIN (
SELECT * FROM `comments`
) AS c
ON p.id = c.page_id
WHERE c.page_id IS NULL
GROUP BY 1
查询 4。加入 2 个子查询,1 秒
SELECT p.id
FROM (
SELECT id FROM `pages`
) AS p
LEFT JOIN (
SELECT * FROM `comments`
) AS c
ON p.id = c.page_id
WHERE c.page_id IS NULL
GROUP BY 1
查询5.加入2个子查询,只选择1列,0.2秒
SELECT p.id
FROM (
SELECT id FROM `pages`
) AS p
LEFT JOIN (
SELECT page_id FROM `comments`
) AS c
ON p.id = c.page_id
WHERE c.page_id IS NULL
GROUP BY 1
查询 6. 时间过长
SELECT p.id
FROM `pages` AS p
WHERE NOT EXISTS( SELECT page_id FROM `comments`
WHERE page_id = p.id );;
现在,在 MySql 5.7 版中,所有上述查询都需要 “太多时间” 来执行。
在MySql 5.7中,查询1和4解释相同:
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 SIMPLE p NULL index PRIMARY PRIMARY 4 NULL 147626 100.00 Using index; Using temporary; Using filesort
1 SIMPLE c NULL ALL NULL NULL NULL NULL 147790 10.00 Using where; Not exists; Using join buffer (Block Nested Loop)
在 MySql 5.6 中,不幸的是我现在无法得到查询 1 的解释(花费太多时间),但查询 4 如下:
id select_type table type possible_keys key key_len ref rows Extra
---------------------------------------------------------------------------------------------------------------------------
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 147626 Using temporary; Using filesort
1 PRIMARY <derived3> ref <auto_key0> <auto_key0> 4 p.id 10 Using where; Not exists
3 DERIVED comments ALL NULL NULL NULL NULL 147790 NULL
2 DERIVED pages index NULL PRIMARY 4 NULL 147626 Using index
表格:
CREATE TABLE `pages` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`identifier` varchar(250) NOT NULL DEFAULT '',
`reference` varchar(250) NOT NULL DEFAULT '',
`url` varchar(1000) NOT NULL DEFAULT '',
`moderate` varchar(250) NOT NULL DEFAULT 'default',
`is_form_enabled` tinyint(1) unsigned NOT NULL DEFAULT '1',
`date_modified` datetime NOT NULL,
`date_added` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=147627 DEFAULT CHARSET=utf8
CREATE TABLE `comments` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL DEFAULT '0',
`page_id` int(10) unsigned NOT NULL DEFAULT '0',
`website` varchar(250) NOT NULL DEFAULT '',
`town` varchar(250) NOT NULL DEFAULT '',
`state_id` int(10) NOT NULL DEFAULT '0',
`country_id` int(10) NOT NULL DEFAULT '0',
`rating` tinyint(1) unsigned NOT NULL DEFAULT '0',
`reply_to` int(10) unsigned NOT NULL DEFAULT '0',
`comment` text NOT NULL,
`reply` text NOT NULL,
`ip_address` varchar(250) NOT NULL DEFAULT '',
`is_approved` tinyint(1) unsigned NOT NULL DEFAULT '1',
`notes` text NOT NULL,
`is_admin` tinyint(1) unsigned NOT NULL DEFAULT '0',
`is_sent` tinyint(1) unsigned NOT NULL DEFAULT '0',
`sent_to` int(10) unsigned NOT NULL DEFAULT '0',
`likes` int(10) unsigned NOT NULL DEFAULT '0',
`dislikes` int(10) unsigned NOT NULL DEFAULT '0',
`reports` int(10) unsigned NOT NULL DEFAULT '0',
`is_sticky` tinyint(1) unsigned NOT NULL DEFAULT '0',
`is_locked` tinyint(1) unsigned NOT NULL DEFAULT '0',
`is_verified` tinyint(1) unsigned NOT NULL DEFAULT '0',
`date_modified` datetime NOT NULL,
`date_added` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=147879 DEFAULT CHARSET=utf8
问题
为什么会这样? MySql 在幕后做了什么?
这种情况只发生在 MySql 中,还是任何其他 Sql 中也发生?
如何编写快速查询来获得我需要的内容? (在 v 5.6、5.7 中)
【问题讨论】:
-
您是否尝试在慢查询中删除
GROUP BY? -
@DonJoe 是的,没有区别
-
GROUP BY 在这种情况下毫无意义。
-
请为两个表提供
SHOW CREATE TABLE,为两个选择提供EXPLAIN SELECT ...,RAM的大小,MySQL的版本号。 -
“为什么会发生这种情况?MySql 在幕后做了什么?” 广泛回答 “这种情况是否只发生在 MySql 或任何其他 Sql 中?好吧?” 宽泛地回答,因为其他 RDMS 中的 SQL 优化器完全不同...... “我怎样才能编写一个快速查询来获得我需要的东西?(在 v 5.6、5.7 中)” 如果您同时索引
comments(page_id),则很可能您可以使用@RickJames 查询...
标签: mysql join subquery query-performance