【问题标题】:MySQL trouble with table definition creating fulltable scanMySQL 表定义问题创建全表扫描
【发布时间】:2016-05-14 10:48:22
【问题描述】:

下面的解释我看不懂:

第一个仅涉及 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

也许我们可以说全表扫描 在这种情况下不是问题吗?

【问题讨论】:

  • 从您以前的帖子中了解如何格式化帖子
  • a) 编辑您的问题,不要添加答案。 b)为所有相关表添加显示索引(物种似乎有问题)c)我建议使用索引IDX_NOME (NOME, ID_SPECIE)(不是更多),反之亦然,但这似乎不是问题。 d) 您的统计数据看起来不错,但他们说每个ID_speciespaziente 中具有相同的值。那是对的吗? e) 由于某种原因,mysql 认为直接访问物种表太昂贵了。 f) 尝试: 1. 在like 'MARCO%'; 之后添加;。 2.尝试force index ... ,如我的回答中所述。 3.ANALYZE TABLE paziente, species 4.你的mysql是什么版本的?
  • 再次感谢您的宝贵时间,
  • 再次感谢您的宝贵时间,我修改了我的第一个问题。索引更大,因为我正在寻找我的最终配置,并且我需要将近一晚来加载数据。在我的实际应用程序中,我可以搜索 COGNOME、NOME、ID_SESSO 和 ID_SPECIE,因为您描述的只是可以使用索引,所以我需要将它们全部放入索引中,对吗?是的,你是对的,在这个测试中,我在表 PAZIENTE 中只有一个 ID_SPECIE 的值,但我可以有大约 20 种不同的类型。 Mysql 是最新版本 5.7。我还添加了力指数结果
  • range 表示 mysql 现在正在正确使用该索引(它不是全表扫描,这正是您所期望的)。您可以添加查询有无强制和第一个没有种类的查询所需的时间吗?我仍然不知道为什么必须强制使用mysql。您可以尝试使用结果较少的其他名称(然后没有强制)。您是否尝试过ANALYZE TABLES...(导入后)?尝试通过join Specie specie5_ ignore index (specie) 禁止物种索引并对其进行测试。还有一句话:你不必重新导入数据来改变你的索引,使用create index ...drop index ...

标签: mysql sql-execution-plan


【解决方案1】:

当询问 mysql 为什么使用特定索引(或不使用)时,请始终为您的表提供“在表名上显示索引”的输出。 mysql 使用那里给出的信息来决定使用什么索引。为了理解mysql为什么使用索引,了解mysql使用的信息来决定使用或不使用哪个索引是有帮助的。

而且这些信息可能已经过时了,所以你可能想先做一个ANALYZE TABLE ANALISI, PAZIENTE, SPECIE 并再次检查你的解释(它会锁定表直到它完成,以防你在生产服务器上使用它)。

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

要执行joinwhere,您需要PATIENZE 表中有关ID_SPECIENOME 的信息。不幸的是,您没有同时包含这两种信息的索引。

没有SHOW INDEX-data 很难确定,但要获得结果,最好在FK_PAZIENTE_SPECIE 中查找specie 信息条目,这样您就没有查看那个实际的表格;另一方面,您可以在IDX_NOME-index 中检查具有所需名称的条目。这将为您提供 2 个列表。现在您必须检查两个列表中都有哪些条目。想想你必须如何做到这一点:获取列表 1 的条目(它包含主键),然后滚动列表 2 以查看主键是否也存在。如果您首先按主键对列表 2 进行排序,这可能会更快。

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

所以你的索引实际上被使用了,但不是以最快的方式。

该怎么办?如果您一直关注到这里,您可能已经知道:“要执行joinwhere,您需要在PATIENZE 表中提供有关ID_SPECIENOME 的信息。”所以把它交给mysql:用两列创建一个索引。这取决于您的数据,哪个顺序更好,以及是否要先使用带有 '%MARCO%you should putID_SPECIE` 的查询,因为不能使用索引,否则。

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

当然,您总是可以通过... join Paziente paziente3_ force index (idx_PAZIENTE_nome_specie) ... 强制输入一个键,并检查它是否真的更快,或者mysql 是否真的不使用它是正确的。 (但是即使您搜索例如'%MARCO%',它也会尝试使用该索引,这实际上意味着根本没有索引,即使使用FK_PAZIENTE_SPECIE 可能会更好,所以你必须添加这个一个也一样,依此类推。所以要小心使用武力。)

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-09
    • 1970-01-01
    • 2016-11-24
    • 2015-01-07
    • 2014-02-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多