简体   繁体   中英

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

Question 1:

Let's begin with two pictures:

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

PIC-2: 在此处输入图片说明 Note in particularr the bottom line of the explain output.

Then compare the table in PIC-1 with the table in 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 .
  2. Then in PIC-2, the key is not used.
  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 
   ( 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.

Question 2:

Review the PIC-2, 2rd row of the table, which explains the select of table oc_circle_of_community . There are two possible keys: circle_id and community_id . Why the two keys not be used?
(There are total 14 rows in table oc_circle_of_community . This may help.)

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

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

b) you attempt to use an adequate composite index, or

c) you go for the gold with a covering index and avoid the datapage read

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. 7 bytes wide total.

As you only have 300 rows, analyze table should take a split second. 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

The 14 rows with REF NULL is related to the beginning of this Answer.

The first query uses the index because it is "covering". That is, all the fields in the SELECT are in that index:

  • Index keys: house_id, state, hidden
  • The implicitly added primary key: id

Well, I could be wrong. It says Using index condition , which refers to Index Condition Pushdown ; it does not say Using index , which would imply "covering". And the ORDER BY weight... prevents "covering". To get more insight, do EXPLAIN FORMAT=JSON SELECT ... .

For the second query, don't use IN ( SELECT ... ) , it optimizes very poorly. Instead, turn into a 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). The optimizer will not deal with multiple "ranges" at the same time, and may not deal well with IN ( constants ) . More discussion in my Index Cookbook .

(Unrelated...)

Shouldn't state be NOT NULL?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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