为什么MySQL会更改用于同一查询的索引?

为什么MySQL会更改用于同一查询的索引?,mysql,Mysql,我将此作为一个新问题发布(来自我在这里提出的另一个问题:),因为我认为出现的新问题需要它自己的帖子。我不确定这样做是否正确,但如果不正确,请告诉我 我目前在MySQL(V5.5.14)、InnoDB(V1.1.8)上有3个(简化的)表: 1) 这个表实际上有四个不同的版本,每个版本都存储特定类型的数据,但都有这三列 +--------------------------------------------------+ | PropData

我将此作为一个新问题发布(来自我在这里提出的另一个问题:),因为我认为出现的新问题需要它自己的帖子。我不确定这样做是否正确,但如果不正确,请告诉我

我目前在MySQL(V5.5.14)、InnoDB(V1.1.8)上有3个(简化的)表:

1) 这个表实际上有四个不同的版本,每个版本都存储特定类型的数据,但都有这三列

+--------------------------------------------------+
|  PropData                                        |
+--------------+-----------+-----------------------+
|  Id          |  BigTag   |  Date                 |
+--------------+-----------+-----------------------+
|  [SomeGUID]  |  10001AB  |  1000-01-01 00:00:00  |
+--------------+-----------+-----------------------+
2) 此表中的记录反映了上表中的数据,但它仅包含主键(id)和BigTag的前5个字符(以便与LimitTag轻松连接)。每个相应的PropData表都有一个这样的表

+------------------------+
|  PropDataTag           |
+--------------+---------+
|  Id          |  Tag    |
+--------------+---------+
|  [SomeGUID]  |  10001  |
+--------------+---------+
3) 此表只有一个版本

+-------------------+
|  LimitTags        |
+---------+---------+
|  Tag    |  Model  |
+---------+---------+
|  10001  |  Base   |
+---------+---------+
我基本上是在尝试为一段时间跨度内的一系列数据获取所有唯一的标记和模型对

对于这三个表,我最终提出了以下查询:

SELECT DISTINCT T.Tag, T.Model
FROM PropData P
   JOIN PropDataTag N ON P.Id=N.Id
      JOIN LimitTags T ON N.Tag=T.Tag
WHERE P.Date BETWEEN '0000-01-01 00:00:00' AND '9999-12-31 23:59:59'
结果如下所示:

+---------+----------+
|  Tag    |  Model   |
+---------+----------+
|  10001  |  Base    |
|  10002  |  Base    |
|  10003  |  Base    |
|  10004  |  Base    |
|  10001  |  Upgrade |
|  10002  |  Upgrade |
|  10001  |  Crappy  |
+---------+----------+
我有以下索引:

1) PropData:主(Id)、IdxDate(日期)、IdxTag(BigTag)、IDXandDate(Id、日期)

2) PropDataTag:主(Id)、IdxTag(标记)

3) 限制标签:主(Id)、IdxTag(标签)、IDXTAGAND模型(标签、模型)

我第一次运行它时,它运行得非常完美,我在0.016秒内得到了结果(696条记录)。我还运行了EXPLAIN命令,得到了以下结果。当我拍摄截图时,不幸的是,我忽略了扩展ref列,因此我不知道其中的两个值是什么,并且很难再现这些结果

id  select_type  table  type    possible_keys                 key             key_len   ref     rows     Extra 
1   SIMPLE       T      index   IdxTag,IdxTagAndModel         IdxTagAndModel  49        NULL    1427     Using index; Using temporary
1   SIMPLE       N      ref     IdxTag                        IdxTag          7         NoIdea  1238     Using index; Distinct
1   SIMPLE       P      eq_ref  PRIMARY,IdxDate,IdxIdAndDate  IdxDate         38        NoIdea  1        Using where; Distinct
我认为一切都很顺利,然后对所有4个PropData表进行了完全相同的查询,并将它们合并在一起,以获得标记/模型对的完整列表。运行查询后,我花了2分钟多的时间才停止查询,因为时间太长了。我尝试在上面显示的原始查询上运行explain命令,使用完全相同的表,但没有得到相同的结果,而是得到以下结果:

id  select_type  table  type    possible_keys                 key         key_len   ref    rows     Extra 
1   SIMPLE       P      range   PRIMARY,IdxDate,IdxIdAndDate  IdxDate     24        NULL   1785585  Using where; Using Index; Using temporary
1   SIMPLE       N      eq_ref  PRIMARY,IdxTag                PRIMARY     38        P.Id   1
1   SIMPLE       T      ref     IdxTag,IdxTagAndModel         IdxTag      7         N.Tag  1
现在运行原始查询需要>30秒,而不是~0.016秒。这些表上的数据和索引肯定没有改变,我每隔5分钟为这些查询运行explain命令

刚才发生了什么事?我不明白:

  • 解释输出中的行顺序为什么会改变

  • 为什么MySQL突然决定使用不同的索引

有人有什么想法或意见吗?我四处搜索其他帖子,但似乎没有人经历过以下结果

编辑1: 当我试图在执行过程中取消查询(没有解释)时,我能够重现这一点,这导致MySQL Workbench崩溃。重新启动时,它第一次工作,立即给我结果。当我对所有4个表运行查询时,索引再次切换,我经历了与上面相同的现象,但使用了一组新的、不同的解释结果:

id  select_type  table  type    possible_keys                 key             key_len   ref    rows     Extra 
1   SIMPLE       P      range   PRIMARY,IdxDate,IdxIdAndDate  IdxDate         24        NULL   1796958  Using where; Using Index; Using temporary
1   SIMPLE       N      eq_ref  PRIMARY,IdxTag                PRIMARY         38        P.Id   1
1   SIMPLE       T      ref     IdxTag,IdxTagAndModel         IdxTagAndModel  7         N.Tag  1        Using index
我尝试强制查询使用所需的第一组解释输出中显示的相同索引:

SELECT DISTINCT T.Tag, T.Model
FROM PropData P FORCE INDEX (PRIMARY)
   JOIN PropDataTag N FORCE INDEX (IdxTag) ON P.Id=N.Id
      JOIN LimitTags T FORCE INDEX (IdxTagAndModel) ON N.Tag=T.Tag
WHERE P.Date BETWEEN '0000-01-01 00:00:00' AND '9999-12-31 23:59:59'
我从解释中得到了这些结果:

id  select_type  table  type    possible_keys   key             key_len   ref    rows     Extra 
1   SIMPLE       N      index   IdxTag          PRIMARY         7         P.Id   1        Using index; Using temporary
1   SIMPLE       T      ref     IdxTagAndModel  IdxTagAndModel  7         N.Tag  1        Using index
1   SIMPLE       P      eq_ref  PRIMARY         IdxDate         38        NULL   1796958  Using where; Distinct
我看到的这些最新结果与原始工作版本之间的主要区别是IdxTagAndModel键只有一个7而不是49的键,并且表N在额外的列中没有Distinct

需要注意的其他差异是,表p的行数不同,而表N上使用的是临时行,而不是T

编辑2: 下面是我执行的完整查询,它似乎切换了使用的索引:

SELECT DISTINCT T.Tag, T.Model
FROM PropData1 P
   JOIN PropDataTag1 N ON P.Id=N.Id
      JOIN LimitTags T ON N.Tag=T.Tag
WHERE P.Date BETWEEN '0000-01-01 00:00:00' AND '9999-12-31 23:59:59'

UNION
SELECT DISTINCT T.Tag, T.Model
FROM PropData2 P
   JOIN PropDataTag2 N ON P.Id=N.Id
      JOIN LimitTags T ON N.Tag=T.Tag
WHERE P.Date BETWEEN '0000-01-01 00:00:00' AND '9999-12-31 23:59:59'

UNION
SELECT DISTINCT T.Tag, T.Model
FROM PropData3 P
   JOIN PropDataTag3 N ON P.Id=N.Id
      JOIN LimitTags T ON N.Tag=T.Tag
WHERE P.Date BETWEEN '0000-01-01 00:00:00' AND '9999-12-31 23:59:59'

UNION
SELECT DISTINCT T.Tag, T.Model
FROM PropData4 P
   JOIN PropDataTag4 N ON P.Id=N.Id
      JOIN LimitTags T ON N.Tag=T.Tag
WHERE P.Date BETWEEN '0000-01-01 00:00:00' AND '9999-12-31 23:59:59'

我最初没有包括它,因为它实际上是同一个查询在不同的表上重复了3次。每个表都包含不同类型的数据,例如double或BLOB,但它们根本不用于此查询。

这是大多数问题的根本原因

我目前在MySQL(V5.5.14)InnoDB上有3个(简化的)表 (V1.1.8):

1) 这个表实际上有四个不同的版本,其中 每个存储特定类型的数据,但都有这3列

+--------------------------------------------------+
|  PropData                                        |
+--------------+-----------+-----------------------+
|  Id          |  BigTag   |  Date                 |
+--------------+-----------+-----------------------+
|  [SomeGUID]  |  10001AB  |  1000-01-01 00:00:00  |
+--------------+-----------+-----------------------+
所有三个表都有相同的列,这意味着这基本上是相同的数据,但有细微的差异。RDBMS系统有一个内置的处理机制

。。。通过使您能够将各个表的部分分布到 文件系统根据您可以根据需要大量设置的规则。在里面 效果是,表的不同部分作为单独的表存储在 不同的地点。用户选择的规则,根据该规则划分 完成的数据称为分区函数

通过使用partitoning,您可以立即消除使用UNION的需要。你们的结合可以大大简化


至于为什么简单查询使用一个索引而联合查询使用另一个索引,这仅仅是因为它们在不同的
PropDataTagX
表中的行数似乎大不相同。如果它们都有相似的行数,则可以使用相同的查询计划。

Explain
是伏都教猜测的工作。不要太认真。如果你想排序,那么在查询中这样说。@Drew你能详细说明一下排序的含义吗?我可以看到,在这里你只显示了两个查询。没有按排序的
子句。因此,优化器使用它所拥有的速度,并按照它选择的顺序交付结果。因为你以前没有按日期时间排序我的结果,但为了简单起见,我在不久前删除了它。我仍然不明白为什么优化器会在所有数据或索引都没有更改的情况下不断更改顺序和索引。您是在讨论结果集中给您的顺序,还是解释在其3行表中转储的顺序,尽管这对我来说是有意义的,拥有一个分区的PropData表对我来说是行不通的。弗斯特