表格定义:
CREATE TABLE `titles` (
`emp_no` int(11) NOT NULL,
`title` varchar(50) NOT NULL,
`from_date` date NOT NULL,
`to_date` date DEFAULT NULL,
PRIMARY KEY (`emp_no`,`title`,`from_date`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8
查询是:
EXPLAIN SELECT * FROM employees.titles WHERE emp_no < '10010' and title='Senior Engineer';
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
| 1 | SIMPLE | titles | range | PRIMARY | PRIMARY | 4 | NULL | 16 | Using where |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
我的问题是为什么只有第一列可以使用索引?我知道很多文章/文件给出了这个结论,但我想知道详细解释。
我的理解是,MySQL可以扫描BTREE索引并查找与emp_no < '10010'
匹配的密钥集合,然后在title='Senior Engineer'
上对其进行过滤,为什么它说from_data
列不能使用指数? (顺便说一下,我想我知道B + Tree是如何工作的)。
感谢。
下面是解释格式= json:
的输出{
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "1.41"
},
"table": {
"table_name": "titles",
"access_type": "range",
"possible_keys": [
"PRIMARY"
],
"key": "PRIMARY",
"used_key_parts": [
"emp_no"
],
"key_length": "4",
"rows_examined_per_scan": 1,
"rows_produced_per_join": 1,
"filtered": "100.00",
"cost_info": {
"read_cost": "1.21",
"eval_cost": "0.20",
"prefix_cost": "1.41",
"data_read_per_join": "168"
},
"used_columns": [
"emp_no",
"title",
"from_date",
"to_date"
],
"attached_condition": "((`employees`.`titles`.`emp_no` < '10010') and (`employees`.`titles`.`title` = 'Senior Engineer'))"
}
}
}
答案 0 :(得分:0)
这里的问题是你的表非常小,即使索引降低你的搜索成本,使用索引本身也有成本。
因此,对于小型表,使用索引的成本大于仅读取整个表的成本。尝试包含更多行,然后重试。
答案 1 :(得分:0)
WHERE 'emp_no' < ... AND ...
KEY(emp_no, ...)
由于emp_no
用于“范围”(“&lt;”),因此只会使用密钥的emp_no
。
通过转动密钥:(title, emp_no, ...)
,可以使用title
和emp_no
。
但请注意......此索引更改将使此 SELECT
更好地工作。但是你有什么其他查询?你可能需要有多个索引,可能是:
PRIMARY KEY(emp_no, title, from_date),
INDEX(title, emp_no)
回到原来的问题。 EXPLAIN
表明它正在使用“范围”。但它没有说明是否正在检查title
。您使用的是哪个版本的MySQL?较新版本具有“索引条件下推”,可以对您提到的title
进行测试。 EXPLAIN FORMAT=JSON SELECT ...
应该说是否正在这样做。
但是,使用title
索引开始更有效率,因为它不需要繁琐地跨越不适用的标题。 所有相关的行都是连续的。
修改解释EXPLAIN
它表示它正在使用“范围”,并且仅使用(“used_key_parts”)索引的emp_no
部分。然而它说“used_columns”全部是4列,甚至超过它使用的KEY中的列。解释......
InnoDB中的PRIMARY KEY
与数据“聚集” - 也就是说,它们位于相同的 BTree中。
执行将仅使用emp_no < '10010'
- 它将从表的开头开始,但不包括emp_no = '10010'
的第一行。实际上,它(特别是InnoDB引擎)将应用“附加条件”“((employees
。titles
。emp_no
&lt;'10010')和({{1 } {。{1}}。employees
='高级工程师'))“过滤掉任何未通过该测试的行。 (这就是我说它“踩过”无趣的titles
值。)
正在扫描“数据”BTree。如果添加title
,您可能会发现完全不同的JSON。发表它;我会解释一下。
(注意:如果“太多”emps是'高级工程师',优化器可能决定扫描数据而不是使用新索引。)