在MySQL查询中添加limit子句会大大降低查询速度

在MySQL查询中添加limit子句会大大降低查询速度,mysql,performance,limit,Mysql,Performance,Limit,我试图解决MySQL上的一个性能问题,所以我想创建一个较小版本的表来使用。当我向查询中添加LIMIT子句时,它从大约2秒(对于完整插入)变为天文数字(42分钟) 答案有点长,但我希望你能从中学到一些东西 因此,根据explain语句中的证据,您可以看到MySQL查询优化器可以使用两个可能的索引,它们如下所示: possible_keys PRIMARY,insert_date 但是,MySQL查询优化器决定使用以下索引: key insert_date MySQL查询优化器很少使用错误的索

我试图解决MySQL上的一个性能问题,所以我想创建一个较小版本的表来使用。当我向查询中添加LIMIT子句时,它从大约2秒(对于完整插入)变为天文数字(42分钟)


答案有点长,但我希望你能从中学到一些东西

因此,根据explain语句中的证据,您可以看到MySQL查询优化器可以使用两个可能的索引,它们如下所示:

possible_keys
PRIMARY,insert_date 
但是,MySQL查询优化器决定使用以下索引:

key
insert_date
MySQL查询优化器很少使用错误的索引。现在有一个可能的原因。您正在处理静态开发数据库。您可能从生产中恢复了此功能,以进行开发

当MySQL优化器需要决定在查询中使用哪个索引时,它会查看所有可能索引的统计信息。您可以在这里阅读更多有关统计信息的入门资料

因此,在更新、插入和删除表时,您会更改索引统计信息。可能是MySQL服务器因为静态数据而具有错误的统计信息,并选择了错误的索引。然而,这只是一个猜测,在这一点上,作为一个可能的根本原因

现在让我们深入研究索引。有两种可能的索引可以使用主键索引和插入日期索引。MySQL使用了插入日期。请记住,在查询执行期间,MySQL始终只能使用一个索引。让我们看看主键索引和插入日期索引之间的区别

关于主键索引(也称为聚集索引)的简单事实:

  • 主键索引通常是包含数据行的btree结构,即它是包含日期的表 关于二级索引(也称为非聚集索引)的简单事实:

  • 辅助索引通常是一个btree结构,其中包含被索引的数据(索引中的列)和指向主键索引上数据行位置的指针
  • 这是一个微妙但巨大的区别

    让我解释一下,当您读取主键索引时,您正在读取表。该表也按主索引的顺序排列。因此,为了找到一个值,我将搜索索引,读取数据,这是一个操作

    在读取辅助索引时,搜索索引并找到指针,然后读取主键索引以根据指针查找数据。这实际上是两个操作,使得读取辅助索引的操作的成本是读取主键索引的两倍

    在您的例子中,因为它选择了insert_date作为索引来使用,所以它只是为了连接而做了双倍的工作。这是问题一

    现在,当您限制一个记录集时,它是查询的最后一个执行部分。MySQL必须获取整个记录集,根据ORDER BY和GROUP BY条件对其进行排序(如果未排序,则为ALLEADY),然后获取所需的记录数,并根据LIMIT BY section将其发回。MySQL需要做大量的工作来跟踪要发送的记录以及它在记录集中的位置等。LIMIT BY确实会影响性能,但我怀疑可能有一个影响因素

    按玩家id查看分组。使用的索引是插入日期。GROUP BY本质上是对您的记录集进行排序,但是因为它没有用于排序的索引(请记住,索引是按照其中包含的列的顺序排序的)。本质上,您是在请求对player_id进行排序/排序,并且使用的索引是在insert_日期进行排序的

    这一步导致了文件排序问题,该问题本质上是获取从读取二级索引和主索引(记住这两个操作)返回的数据,然后对它们进行排序。排序通常在磁盘上进行,因为在内存中进行排序是一项非常昂贵的操作。因此,整个查询结果都被写入磁盘,并且排序速度非常慢,无法获得结果

    通过删除插入日期索引,MySQL现在将使用主键索引,这意味着数据是按玩家id和插入日期排序的(ORDER By/GROUP By)。这将消除读取二级索引然后使用指针读取主键索引(即表)的需要,并且由于数据已经排序,所以MySQL在应用逐段查询时几乎没有什么工作

    下面是一个有根据的猜测,如果你能在索引被删除后发布解释声明的结果,我可能能够证实我的想法。因此,通过使用错误的索引,在磁盘上对结果进行排序,以正确应用限制。通过删除LIMIT BY,MySQL可能可以在内存中进行排序,因为它不必应用LIMIT BY并跟踪返回的内容。的限制可能导致创建临时表。再一次很难说,如果看不到语句之间的差异,即解释的输出


    希望这能让你更好地理解指数以及为什么它们是一把双刃剑。

    也有同样的问题。当我添加
    FORCE INDEX(id)
    时,它返回到了查询的几毫秒,没有限制,同时生成了相同的结果。

    我怀疑您的问题是文件排序,但我更担心查询在做什么。例如,将行添加到date\u curr,然后将行添加到date\u prev。然后在查询中选择“从日期\当前”,而在查询中从不使用“日期\当前”?请稍微澄清一下这个例子。另外,程序的哪一部分花费的时间最长?是精选的吗?是插页吗?请澄清。如何使用
    show create table player\u record
    ,以便我们可以查看所有其他内容,如索引等。我打赌,如果在要查询的日期列上添加索引,会加快查询速度。我在insert\u date上有一个索引,它是exp中使用的键
    mysql> explain SELECT pr.player_id, 
                          MAX(pr.insert_date) AS insert_date 
                   FROM   player_record pr 
                   INNER  JOIN date_curr dc 
                          ON pr.player_id = dc.player_id
                   WHERE  pr.insert_date < '2012-05-15' 
                   GROUP  BY pr.player_id 
                   LIMIT  0,20000;                    
    +----+-------------+-------+-------+---------------------+-------------+---------+------+--------+----------------------------------------------+
    | id | select_type | table | type  | possible_keys       | key         | key_len | ref  | rows   | Extra                                        |
    +----+-------------+-------+-------+---------------------+-------------+---------+------+--------+----------------------------------------------+
    |  1 | SIMPLE      | pr    | range | PRIMARY,insert_date | insert_date | 3       | NULL     | 396828 | Using where; Using temporary; Using filesort |
    |  1 | SIMPLE      | dc    | ALL   | PRIMARY             | NULL        | NULL    | NULL | 216825 | Using where; Using join buffer               |
    +----+-------------+-------+-------+---------------------+-------------+---------+------+--------+----------------------------------------------+
    2 rows in set (0.03 sec)
    
    | Status               | Duration   | CPU_user   | CPU_system | Context_voluntary | Context_involuntary | Block_ops_in | Block_ops_out | Messages_sent | Messages_received | Page_faults_major | Page_faults_minor | Swaps | Source_function       | Source_file   | Source_line |
    | Copying to tmp table | 999.999999 | 999.999999 |   0.383941 |            110240 |               18983 |        16160 |           448 |             0 |                 0 |                 0 |                43 |     0 | exec                  | sql_select.cc |        1976 |
    
    possible_keys
    PRIMARY,insert_date 
    
    key
    insert_date