简体   繁体   English

为什么MySQL不使用索引?

[英]Why does MySQL not use index?

I have two tables: (omitting columns not relevant to this question): 我有两个表:(省略与该问题无关的列):

CREATE TABLE 'oc_room' (
  'id' int(11) NOT NULL AUTO_INCREMENT,
  'house_id' int(11) NOT NULL,
  'style_id' int(11) DEFAULT NULL,
  'weight' int(11) DEFAULT '0',
  'state' tinyint(4) DEFAULT '0',
  -- (more columns, omitted for clarity)
  PRIMARY KEY ('id'),
  KEY 'house_id' ('house_id'),
  KEY 'style_id' ('style_id'),
  KEY 'butler_id' ('butler_id'),
  KEY 'oc_room_house_state_hidden_ik_1' ('house_id','state','hidden'),
  CONSTRAINT 'oc_room_ibfk_1' FOREIGN KEY ('house_id') REFERENCES 'oc_house' ('id'),
  CONSTRAINT 'oc_room_ibfk_2' FOREIGN KEY ('style_id') REFERENCES 'oc_room_style' ('id'),
  CONSTRAINT 'oc_room_ibfk_3' FOREIGN KEY ('butler_id') REFERENCES 'oc_butler' ('id')
) ENGINE=InnoDB AUTO_INCREMENT=267 DEFAULT CHARSET=utf8;

CREATE TABLE 'oc_circle_of_community' (
  'id' int(11) NOT NULL AUTO_INCREMENT,
  'circle_id' int(11) NOT NULL,
  'community_id' int(11) NOT NULL,
  PRIMARY KEY ('id'),
  KEY 'circle_id' ('circle_id'),
  KEY 'community_id' ('community_id'),
  CONSTRAINT 'oc_circle_of_community_ibfk_1' FOREIGN KEY ('circle_id') REFERENCES 'oc_circle' ('id'),
  CONSTRAINT 'oc_circle_of_community_ibfk_2' FOREIGN KEY ('community_id') REFERENCES 'oc_community' ('id')
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8;

When I explain a select statement, I have TWO questions 当我explain选择语句时,我有两个问题

Question 1: 问题1:

Let's begin with two pictures: 让我们从两张图片开始:

PIC-1: PIC-1: 在此处输入图片说明

PIC-2: PIC-2: 在此处输入图片说明 Note in particularr the bottom line of the explain output. 注意particularr的底线explain输出。

Then compare the table in PIC-1 with the table in PIC-2. 然后将PIC-1中的表与PIC-2中的表进行比较。 You will find that: 您会发现:

  1. The select of oc_room in PIC-1 uses the combination-key oc_room_house_state_hidden_ik_1 . 在PIC-1中选择oc_room使用组合键oc_room_house_state_hidden_ik_1
  2. Then in PIC-2, the key is not used. 然后在PIC-2中,不使用密钥。
  3. The only difference between the two statements is that oc_room.id in (5,7,9,20,40,60 ) replaced 两条语句之间的唯一区别是oc_room.id in (5,7,9,20,40,60 )替换
oc_room.id in 
   ( select id 
       from oc_house
       where community_id in 
     ( select community_id
         from oc_circle_of_community
        where circle_id in
      ( select id 
          from oc_circle
         where oc_circle.district_id in
         ( select id
             from oc_district
            where oc_district.id = 3 ))))  

Why the difference? 为什么会有所不同?

There are about 300 rows in table oc_room in total. oc_room中总共约有300行。

Question 2: 问题2:

Review the PIC-2, 2rd row of the table, which explains the select of table oc_circle_of_community . 查看表的第二行PIC-2,该表说明了对表oc_circle_of_community的选择。 There are two possible keys: circle_id and community_id . 有两个可能的键: circle_idcommunity_id Why the two keys not be used? 为什么不使用两个键?
(There are total 14 rows in table oc_circle_of_community . This may help.) (表oc_circle_of_community中共有14行。这可能会有所帮助。)

From the Manual at the bottom . 底部的手册。

Indexes are less important for queries on small tables, or big tables where report queries process most or all of the rows. 对于报表查询处理大多数或所有行的小型表或大型表,索引的重要性不那么重要。 When a query needs to access most of the rows, reading sequentially is faster than working through an index. 当查询需要访问大多数行时,顺序读取要比处理索引快。 Sequential reads minimize disk seeks, even if not all the rows are needed for the query. 顺序读取可以最大程度地减少磁盘查找,即使查询不需要所有行。

PIC-1 PIC-1

a) the 300 rows is too small a number for it to hone in on a single index and scan thereafter so it aborts caring about indexes at all, or a)300行太小,无法在单个索引上进行磨合并随后进行扫描,因此它根本无法关心索引,或者

b) you attempt to use an adequate composite index, or b)您尝试使用适当的复合索引,或者

c) you go for the gold with a covering index and avoid the datapage read c)寻求具有覆盖指数的黄金,避免阅读数据页

But note , it resolved it in 8 rows, not 300. It went with composite ( house_id , state , hidden ), where the last is not shown by you. 但是请注意 ,它解决了8行而不是300行。它与复合( house_idstatehidden )一起使用,其中最后一个未显示。 7 bytes wide total. 总共7个字节宽。

As you only have 300 rows, analyze table should take a split second. 由于您只有300行,因此analyze table需要花费一秒钟的时间。 It refreshes the statistics of key distributions than can become stale, thus forcing a key not to be used. 它刷新了密钥分布的统计信息,使统计信息变得过时,从而强制不使用密钥。 A key may be target for use but ultimately abandoned during execution. 密钥可能是使用目标,但最终在执行过程中被放弃。 As such, it is a general statement about its usefulness, such as for large tables, not your issue. 因此,这是关于其有用性的一般说明,例如用于大型表,而不是您的问题。

PIC-2 PIC-2

The 14 rows with REF NULL is related to the beginning of this Answer. 具有REF NULL的14行与此答案的开头有关。

The first query uses the index because it is "covering". 第一个查询使用索引,因为它正在“覆盖”。 That is, all the fields in the SELECT are in that index: 也就是说, SELECT中的所有字段都在该索引中:

  • Index keys: house_id, state, hidden 索引键:house_id,状态,隐藏
  • The implicitly added primary key: id 隐式添加的主键:id

Well, I could be wrong. 好吧,我可能是错的。 It says Using index condition , which refers to Index Condition Pushdown ; 它说Using index condition ,指的是Index Condition Pushdown it does not say Using index , which would imply "covering". 它没有说Using index ,这意味着“覆盖”。 And the ORDER BY weight... prevents "covering". 而且ORDER BY weight...可以防止“覆盖”。 To get more insight, do EXPLAIN FORMAT=JSON SELECT ... . 要获取更多信息,请执行EXPLAIN FORMAT=JSON SELECT ...

For the second query, don't use IN ( SELECT ... ) , it optimizes very poorly. 对于第二个查询,不要使用IN ( SELECT ... ) ,它的优化效果很差。 Instead, turn into a JOIN . 相反,变成JOIN Once you have done that, we can discuss performance of it, if still necessary. 完成此操作后,如有必要,我们可以讨论其性能。

We will probably find that it is still not using the index, but that is is running fast enough to not worry. 我们可能会发现它仍未使用索引,但是运行速度足够快,不必担心。

Composite indexes should start with any columns that are "= constant" ( hidden , in the first query). 组合索引应以“ = constant”(在第一个查询中为hidden任何列开头 The optimizer will not deal with multiple "ranges" at the same time, and may not deal well with IN ( constants ) . 优化器不会同时处理多个“范围”,并且可能无法很好地处理IN ( constants ) More discussion in my Index Cookbook . 在我的索引食谱中有更多讨论。

(Unrelated...) (无关...)

Shouldn't state be NOT NULL? state不应该为非空吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM