为什么我的索引不用于mysql选择

时间:2016-01-07 14:51:20

标签: mysql indexing

表格定义:

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'))"
    }
  }
}

2 个答案:

答案 0 :(得分:0)

这里的问题是你的表非常小,即使索引降低你的搜索成本,使用索引本身也有成本。

因此,对于小型表,使用索引的成本大于仅读取整个表的成本。尝试包含更多行,然后重试。

答案 1 :(得分:0)

WHERE 'emp_no' < ... AND ...
KEY(emp_no, ...)

由于emp_no用于“范围”(“&lt;”),因此只会使用密钥的emp_no

通过转动密钥:(title, emp_no, ...),可以使用titleemp_no

Cookbook on indexing

但请注意......此索引更改将使 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引擎)将应用“附加条件”“((employeestitlesemp_no&lt;'10010')和({{1 } {。{1}}。employees ='高级工程师'))“过滤掉任何未通过该测试的行。 (这就是我说它“踩过”无趣的titles值。)

正在扫描“数据”BTree。如果添加title,您可能会发现完全不同的JSON。发表它;我会解释一下。

(注意:如果“太多”emps是'高级工程师',优化器可能决定扫描数据而不是使用新索引。)