【发布时间】:2017-05-22 22:24:58
【问题描述】:
filtered列表示将按表条件过滤的表行的估计百分比。也就是说,rows显示检查的估计行数,rows × filtered / 100显示将与以前的表连接的行数。
为了更好地理解这一点,我在使用MySQL Sakila Sample Database 的查询中进行了尝试。有问题的表具有以下结构:
mysql> SHOW CREATE TABLE film \G
*************************** 1. row ***************************
Table: film
Create Table: CREATE TABLE `film` (
`film_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`description` text,
`release_year` year(4) DEFAULT NULL,
`language_id` tinyint(3) unsigned NOT NULL,
`original_language_id` tinyint(3) unsigned DEFAULT NULL,
`rental_duration` tinyint(3) unsigned NOT NULL DEFAULT '3',
`rental_rate` decimal(4,2) NOT NULL DEFAULT '4.99',
`length` smallint(5) unsigned DEFAULT NULL,
`replacement_cost` decimal(5,2) NOT NULL DEFAULT '19.99',
`rating` enum('G','PG','PG-13','R','NC-17') DEFAULT 'G',
`special_features` set('Trailers','Commentaries','Deleted Scenes','Behind the Scenes') DEFAULT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`film_id`),
KEY `idx_title` (`title`),
KEY `idx_fk_language_id` (`language_id`),
KEY `idx_fk_original_language_id` (`original_language_id`),
CONSTRAINT `fk_film_language` FOREIGN KEY (`language_id`) REFERENCES `language` (`language_id`) ON UPDATE CASCADE,
CONSTRAINT `fk_film_language_original` FOREIGN KEY (`original_language_id`) REFERENCES `language` (`language_id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8
这是EXPLAIN 查询计划:
mysql> EXPLAIN SELECT * FROM film WHERE release_year=2006 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: film
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1000
filtered: 10.00
Extra: Using where
此表的示例数据集共有 1,000 行,所有行都将 release_year 设置为 2006。使用 MySQL 文档中的公式:
rows x filtered / 100 = "将与以前的表连接的行数
所以,
1,000 x 10 / 100 = 100 = "100 行将与以前的表连接"
嗯?什么“前表”?这里没有JOIN。
文档中引用的第一部分怎么样? “将由表条件过滤的表行的估计百分比。”好吧,表格条件是release_year = 2006,所有记录都有那个值,所以filtered不应该是0.00或100.00(取决于他们所说的“过滤”的意思)?
也许是因为release_year 上没有索引,所以它的行为很奇怪?所以我创建了一个:
mysql> CREATE INDEX test ON film(release_year);
filtered 列现在显示100.00。那么,在我添加索引之前它不应该显示0.00 吗?嗯。如果我让表的一半有 release_year 是 2006,而另一半没有呢?
mysql> UPDATE film SET release_year=2017 ORDER BY RAND() LIMIT 500;
Query OK, 500 rows affected (0.03 sec)
Rows matched: 500 Changed: 500 Warnings: 0
现在EXPLAIN 看起来像这样:
mysql> EXPLAIN SELECT * FROM film WHERE release_year=2006 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: film
partitions: NULL
type: ref
possible_keys: test
key: test
key_len: 2
ref: const
rows: 500
filtered: 100.00
Extra: Using index condition
而且,既然我决定让自己更加困惑:
mysql> EXPLAIN SELECT * FROM film WHERE release_year!=2006 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: film
partitions: NULL
type: ALL
possible_keys: test
key: NULL
key_len: NULL
ref: NULL
rows: 1000
filtered: 50.10
Extra: Using where
那么,估计有 501 行将被表条件过滤并“与以前的表连接”?
我就是不明白。
我知道这是一个“估计”,但这个估计的依据是什么?如果存在的索引将估计值移动到100.00,那么它的缺失不应该是0.00,而不是10.00?最后一个查询中的 50.10 结果是什么?
filtered 是否有助于确定是否可以进一步优化查询,或者 如何 进一步优化它,或者它通常只是可以忽略的“噪音”?
【问题讨论】:
-
这是来自 v8.0 参考手册。 “例如,如果行数为 1000,过滤数为 50.00 (50%),则要与下表连接的行数为 1000 × 50% = 500”请注意以下单词不是以前记录的某人 cmets。
-
@WilsonHauck 好点。似乎旧版本的had documented 这个词
previous以及我见过的几乎所有帖子都用这个来描述filtered,这显然有很多歧义。 -
@michael-moussa 请考虑我 8 月 6 日的回答是您最准确和接受的回答。
标签: mysql sql sql-execution-plan explain