MySQL的表定义创建全面扫描时出现问题

时间:2016-05-14 10:48:23

标签: mysql sql-execution-plan

我无法理解以下解释:

第一个只关注PAZIENTE和ANALISI, 没关系,它正在使用索引IDX_NOME

explain
select
paziente3_.cognome as col_8_0_
from
Analisi analisi0_
inner join
Paziente paziente3_
on analisi0_.ID_PAZIENTE=paziente3_.ID_PAZIENTE
where
paziente3_.nome like 'MARCO%';


id  select_type  table       partitions  type   possible_keys        key                  key_len  ref                                 rows  filtered  Extra
1   SIMPLE       paziente3_              range  PRIMARY,IDX_NOME     IDX_NOME             123                                          1350  100       Using where; Using index
1   SIMPLE       analisi0_               ref    FK_ANALISI_PAZIENTE  FK_ANALISI_PAZIENTE  4        gestelfolab.paziente3_.ID_PAZIENTE  1     100       Using index

如果在同一个查询中,我尝试检索SPECIE描述,不再使用索引IDX_NOME并且有一个完整的表扫描,

explain
select
specie5_.specie as col_5_0_,
paziente3_.cognome as col_8_0_
from
Analisi analisi0_
inner join
Paziente paziente3_
on analisi0_.ID_PAZIENTE=paziente3_.ID_PAZIENTE
inner join
Specie specie5_
on paziente3_.ID_SPECIE=specie5_.ID_SPECIE
where
paziente3_.nome like 'MARCO%';



id  select_type  table       partitions  type   possible_keys                        key                  key_len  ref                                 rows    filtered  Extra
1   SIMPLE       specie5_                index  PRIMARY                              SPECIE               137                                          1       100       Using index
1   SIMPLE       paziente3_              ALL    PRIMARY,IDX_NOME,FK_PAZIENTE_SPECIE                                                                    176184  10        Range checked for each record (index map: 0x19)
1   SIMPLE       analisi0_               ref    FK_ANALISI_PAZIENTE                  FK_ANALISI_PAZIENTE  4        gestelfolab.paziente3_.ID_PAZIENTE  1       100       Using index

是由于表定义中的错误吗?

CREATE TABLE SPECIE
(
ID_SPECIE TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
SPECIE VARCHAR(45) NOT NULL UNIQUE,
PRIMARY KEY (ID_SPECIE)
)
ENGINE=InnoDB;


Table   Non_unique  Key_name  Seq_in_index  Column_name  Collation  Cardinality  Sub_part  Packed  Null  Index_type  Comment  Index_comment
specie  0           PRIMARY   1             ID_SPECIE    A          0                                    BTREE                
specie  0           SPECIE    1             SPECIE       A          0                                    BTREE                



CREATE TABLE PAZIENTE
(
   ID_PAZIENTE          INT           UNSIGNED NOT NULL AUTO_INCREMENT,
   ID_PAZIENTE_LAB      VARCHAR(20),
   COGNOME              VARCHAR(40),
   NOME                 VARCHAR(40),
   DATA_NASCITA         DATE,
   ID_SESSO             TINYINT UNSIGNED NOT NULL,
   RECAPITO             VARCHAR(50),
   CODICE_FISCALE       VARCHAR(30),
   ID_SPECIE            TINYINT UNSIGNED NOT NULL,
   PRIMARY KEY (ID_PAZIENTE), 
   INDEX IDX_DATA_NASCITA (DATA_NASCITA, ID_SESSO, ID_SPECIE),
   INDEX IDX_COGNOME (COGNOME, NOME, ID_SESSO, ID_SPECIE),
   INDEX IDX_NOME (NOME, COGNOME, ID_SESSO, ID_SPECIE),
   CONSTRAINT FK_PAZIENTE_SPECIE FOREIGN KEY (ID_SPECIE) REFERENCES SPECIE(ID_SPECIE),  
   CONSTRAINT FK_PAZIENTE_SESSO FOREIGN KEY (ID_SESSO) REFERENCES SESSO(ID_SESSO)   
)
ENGINE=InnoDB;




Table     Non_unique  Key_name            Seq_in_index  Column_name   Collation  Cardinality  Sub_part  Packed  Null  Index_type  Comment  Index_comment
paziente  0           PRIMARY             1             ID_PAZIENTE   A          176176                               BTREE
paziente  1           IDX_DATA_NASCITA    1             DATA_NASCITA  A          15594                          YES   BTREE
paziente  1           IDX_DATA_NASCITA    2             ID_SESSO      A          25007                                BTREE
paziente  1           IDX_DATA_NASCITA    3             ID_SPECIE     A          17922                                BTREE
paziente  1           IDX_COGNOME         1             COGNOME       A          62479                          YES   BTREE
paziente  1           IDX_COGNOME         2             NOME          A          163074                         YES   BTREE
paziente  1           IDX_COGNOME         3             ID_SESSO      A          170270                               BTREE
paziente  1           IDX_COGNOME         4             ID_SPECIE     A          154908                               BTREE
paziente  1           IDX_NOME            1             NOME          A          55289                          YES   BTREE
paziente  1           IDX_NOME            2             COGNOME       A          166062                         YES   BTREE
paziente  1           IDX_NOME            3             ID_SESSO      A          176184                               BTREE
paziente  1           IDX_NOME            4             ID_SPECIE     A          176184                               BTREE
paziente  1           FK_PAZIENTE_SPECIE  1             ID_SPECIE     A          1                                    BTREE
paziente  1           FK_PAZIENTE_SESSO   1             ID_SESSO      A          1                                    BTREE

如果我强制使用新的IDX_NOME:

    explain
    select
    specie5_.specie as col_5_0_,
    paziente3_.cognome as col_8_0_
    from
    Analisi analisi0_     
    inner join
    Paziente paziente3_     FORCE INDEX (IDX_NOME) 
    on analisi0_.ID_PAZIENTE=paziente3_.ID_PAZIENTE 
    inner join
    Specie specie5_
    on paziente3_.ID_SPECIE=specie5_.ID_SPECIE
    where
    paziente3_.nome like 'MARCO%'
;

结果如下:

id  select_type  table       partitions  type   possible_keys        key                  key_len  ref                                 rows  filtered  Extra
1   SIMPLE       specie5_                index  PRIMARY              SPECIE               137                                          1     100       Using index
1   SIMPLE       paziente3_              range  IDX_NOME             IDX_NOME             123                                          1350  100       Using where; Using index; Using join buffer (Block Nested Loop)
1   SIMPLE       analisi0_               ref    FK_ANALISI_PAZIENTE  FK_ANALISI_PAZIENTE  4        gestelfolab.paziente3_.ID_PAZIENTE  1     100       Using index

也许我们可以说FULL TABLE SCAN 在这种情况下不是问题吗?

1 个答案:

答案 0 :(得分:0)

当询问mysql为什么使用特定索引(或不是)时,请始终提供"在tablename"上显示索引的输出。为你的桌子。 mysql使用那里给出的信息来决定使用哪个索引。要理解为什么mysql使用索引,有助于使用mysql用来决定使用哪个索引的信息。

并且该信息可能已过期,因此您可能需要先执行ANALYZE TABLE ANALISI, PAZIENTE, SPECIE并再次检查解释(它会锁定表格直到完成,以防万一您在生产服务器)。

Like不是使用特定索引的强大指标,因为您可能会使用查询like '%Marco%。如果发生这种情况,您现在也可以停止阅读,因为您的索引一般不会被使用(但您可能想要尝试全文索引)。

要执行joinwhere,您需要在ID_SPECIE - 表格中提供有关NOMEPATIENZE的信息。不幸的是,你没有一个同时包含这两种信息的索引。

如果没有SHOW INDEX - 数据,很难确定,但要获得结果,查找specie信息条目​​可能是个好主意。 1}},所以你不必查看那张实际的桌子;另一方面,您可以在FK_PAZIENTE_SPECIE - index中查找具有所需名称的条目。这将为您提供2个列表。现在您必须检查两个列表中的哪些条目。考虑一下如何执行此操作:获取列表1的条目(它包含主键),然后滚动列表2以查看主键是否也存在。如果您首先按主键对列表2进行排序,则可能会更快。

MySQL不能同时为同一个表使用两个索引(因为"使用索引"技术上意味着什么),但它当然可以做到与上面描述的类似的事情并仍然使用索引来获取所需的信息。这就是IDX_NOME的含义:它结合了来自多个索引的信息(这里:Range checked for each record (index map: 0x19) - 表中的第1,第4和第5个索引,如果我没有弄错的话,应该是{,{ {1}},SHOW INDEX FROM PAZIENTEPRIMARY),每个都包含一些所需的信息。由于IDX_NOMEFK_PAZIENTE_SPECIE之间没有直接链接(它们通过IDX_NOME间接链接),这可能会很快或很慢,具体取决于您的数据(因此,如果分析结果,它会有所帮助)信息是最新的,以帮助mysql决定做什么 - 在选择一个策略后,mysql不会改变策略,即使在执行过程中它被证明是一个糟糕的策略。)

因此,您的索引实际上已被使用,但不是以最快的方式使用。

该怎么办?如果你跟着到这里,你可能已经知道:"要执行FK_PAZIENTE_SPECIEPRIMARY,您需要join中的whereID_SPECIE的相关信息}} - 表&#34。所以只需将其提供给mysql:使用两列创建索引。这取决于您的数据哪个订单更好,如果您想首先使用'%MARCO%NOME ID_SPECIE`查询,因为索引无法使用,否则就会使用。

试试看,这应该让mysql现在使用新的索引。如果不是(也没有其他索引),这可能是一个很好的跟进问题,但请真的添加PATIENZE - 信息。

您当然可以通过you should put强制键,并检查它是否真的更快,或者mysql是否真的不使用它。 (但是,即使你搜索例如'%MARCO%'它也会尝试使用该索引,这实际上意味着根本没有索引,即使最好只使用show index那么,你也必须添加这个,依此类推。所以请小心使用。)

尝试进行测试和比较可能值得尝试,如果现在... join Paziente paziente3_ force index (idx_PAZIENTE_nome_specie) ...(在添加另一个索引之前和FK_PAZIENTE_SPECIE之前),就可以了。乍一看,我认为这应该已经使它更快,但也许mysql已经正确不使用索引。