当大数据受到影响时,索引不起作用

时间:2017-09-14 07:02:09

标签: mysql indexing

我有一个查询。如下

SELECT  SUM(principalBalance) as pos, COUNT(id) as TotalCases,
        SUM(amountPaid) as paid, COUNT(amountPaid) as paidCount,
        SUM(amountPdc) as Pdc, SUM(amountPtp), COUNT(amountPtp)
    FROM  caseDetails USE INDEX (updatedAt_caseDetails)
    WHERE  updatedAt BETWEEN '2016/06/01 00:00:00' AND '2016/06/30 23:59:00'

它有效地使用索引。解释结果的屏幕截图:Result of explain 日期范围'2016/06/01 00:00:00'和'2016/07/26 23:59:00'有154500条记录。

但是当我增加数据范围时,

SELECT SUM(principalBalance) as pos, COUNT(id) as TotalCases, SUM(amountPaid) as paid, COUNT(amountPaid) as paidCount, SUM(amountPdc) as Pdc, SUM(amountPtp), COUNT(amountPtp) FROM caseDetails USE INDEX (updatedAt_caseDetails) WHERE updatedAt BETWEEN '2016/06/01 00:00:00' AND '2016/07/30 23:59:00'

现在这不是使用索引。解释结果的屏幕截图:Result of explain 日期范围'2016/06/01 00:00:00'和'2016/07/30 23:59:00'有3089464条记录

增加日期范围查询后不再使用索引,所以它变得太慢了。即使在我强迫使用索引之后。我无法弄清楚为什么会发生这种情况,因为查询和索引都没有变化。你能帮我理解为什么会这样吗。

3 个答案:

答案 0 :(得分:2)

请勿使用$USE INDEX。当访问大多数表时,这将减慢查询速度。特别是,如果索引似乎指向超过大约20%的行,优化程序将决定正确进行表扫描。使用索引涉及在索引和数据之间来回弹跳,而执行表扫描可以顺畅地读取数据(尽管必须跳过许多行)。

真正的问题还有另一种解决方案。我假设您正在构建汇总来自大型数据仓库表的数据的“报告”?

不是始终从原始数据('Fact'表)开始,而是构建并维护“Summary Table”。对于您的数据,它可能每天有1行。每天晚上你都会为FORCE INDEXSUMs计算当天的各种事情。然后,“报告”将对总和求和,并对计数求和,以获得更大日期范围内的所需计数。

更多讨论:http://mysql.rjweb.org/doc.php/summarytables

您的“报告”的播放速度将提高10倍以上,您甚至不会被诱惑COUNTs。毕竟,60行应该比3089464快很多。

答案 1 :(得分:1)

更少的时间(更有可能)

即使磁盘读取次数较少,使用索引也可能较差(见下文)。大多数磁盘驱动器支持批量读取也就是说,您从某个块/页面以及 n 后面的页面请求数据。这对于几乎所有旋转磁盘,磁带和所有其他硬盘驱动器来说尤其快速,其中以顺序方式访问数据比随机访问更有效(例如......真的更高效)。

基本上,您通过顺序读取与随机访问获得时间优势。

磁盘读取次数减少(不太可能)

当您实际获得速度/效率时,使用索引是有效的。索引很好,当您显着减少磁盘读取次数并且需要更少时间时。当读取索引并读取使用索引确定的结果行将导致与读取整个表几乎相同的磁盘读取时,使用索引可能是不明智的。

如果您的数据足够分散(就搜索条件而言),这可能会发生,因此您最有可能必须阅读(几乎)所有页面/块。

修复的想法

如果您只以这种方式访问​​您的表(即日期是最重要的搜索条件),那么在磁盘上订购数据可能非常值得花时间。我相信mysql可能会提供这样的功能......(优化表似乎可以做到这一点)

这将减少索引使用的查询持续时间(并且更有可能使用索引)

替代方案

查看来自Rick James的帖子(主要是:存储聚合而不是重复计算)

答案 2 :(得分:0)

嘿,我问这个问题已有很长时间了,现在我对此有了更好的解决方案,对我来说真的很顺利。我希望我的回答可以对某人有所帮助。

我使用了Partitioning方法,并观察到查询的性能现在确实很高。我通过在updatedAt列上创建范围分区来更改表。

Range Partitioning