索引列和未索引列的研究

时间:2018-10-11 20:11:35

标签: mysql indexing innodb binary-search b-tree

我用2000、5000、10000、50000、10000、20000、50000、100000、200000元素生成了单独的MySQL Innodb表(借助php循环和插入查询)。 每个表都有两列:id(主键INT自动递增),数字(INT UNIQUE KEY)。然后我做了同样的事情,但是这次我生成了类似的表,其中 number 列没有INDEX 。我以这种方式生成表:列 number 等于索引值+ 2:第一个元素== 3,第1000个元素为1002,依此类推。我想测试这样的查询,因为它将在我的应用程序中使用:

SELECT count(number) FROM number_two_hundred_I WHERE number=200002;

为这些表生成数据后,我想测试最坏情况查询的时间。我使用了SHOW PROFILES。我假设最坏情况的查询将对应于列值为 number 的元素,值为1002,2002,依此类推,因此这是我测试过的所有查询和时间(由显示个人资料):

SELECT count(number) FROM number_two_thousand_I WHERE number=2002;
// for tables with indexed column number I used **suffix _I** in the end 
// of name of the table. Here is the time for it 0.00099250
SELECT count(number) FROM number_two_thousand WHERE number=2002;
// column number is not indexed when there is no **suffix _I** 
// time for this one is 0.00226275
SELECT count(number) FROM number_five_thousand_I WHERE number=5002;
// 0.00095600
SELECT count(number) FROM number_five_thousand WHERE number=5002;
// 0.00404125

结果如下:

  1. 2000 el-索引0.00099250未索引-0.00226275

  2. 5000 el-编制索引0.00095600未编制索引-0.00404125

  3. 10000 el-编制索引0.00156900未编制索引-0.00761750

  4. 20000 el-编制索引0.00155850未编制索引-0.01452820
  5. 50000 el-编制索引0.00051100未编制索引-0.04127450
  6. 100000条索引为0.00121750的索引未索引-0.07120075
  7. 未索引200000 el的0.00095025-0.11406950

这里是infographic。它显示了元素数如何取决于索引/未索引列的最坏情况查询时间。索引为红色。 测试速度时,我在mysql控制台中两次键入了相同的查询,因为我发现第一次进行查询时,有时对未索引列的查询甚至会更快一些,比索引的要多。问题是:为什么对索引编号的列进行这种类型的查询200000元素有时花费的时间少于同一查询100000元素的时间。您可以看到还有其他无法预测的结果。我之所以这样问,是因为当不对列号进行索引时,结果是可以预测的:200000 el time总是大于100000。请告诉我在尝试对UNIQUE索引列进行研究时我做错了。

2 个答案:

答案 0 :(得分:0)

在未建立索引的情况下,它始终是全表扫描,因此时间与行号很好地匹配,如果已建立索引,则您正在测量索引查找时间,该时间在您的情况下是恒定的(数字较小,偏差很小)

答案 1 :(得分:0)

这不是最糟糕的情况。

  • 使UNIQUE键是随机的,而不是与PK保持同步。例如UUID()
  • 生成足够的行,以使表和索引不能容纳在buffer_pool中。

如果同时使用这两者,则最终会发现性能显着下降。

UNIQUE键对INSERTs具有以下影响:返回客户之前检查唯一性约束。对于非UNIQUE索引,插入索引BTree的工作可能会(并且被)延迟。 (参见“更改缓冲区”。第二列没有索引,要做的工作更少。

WHERE number=2002-

  • 使用UNIQUE(number)-深入BTree。非常快,非常有效。
  • 使用INDEX(number)-深入BTree。非常快,非常有效率。但是,由于不能假设只有这样的一行,所以速度稍慢。也就是说,在BTree中找到正确的位置后,它将向前扫描(非常有效),直到找到2002以外的值。
  • number上没有索引-扫描整个表。因此,成本取决于表的大小,而不取决于number的值。它不知道2002年表中的任何位置或存在次数。如果绘制时间,将发现它是线性的。

我建议您对图形使用对数日志“纸”。无论如何,请注意非索引大小写的线性度。并且索引大小写基本上是恒定的。查找号码= 200002与查找号码= 2002一样便宜。这适用于UNIQUEINDEX。 (实际上,由于BTree确实是O(log n),而不是O(1),所以行中的上升很小。对于2K行,BTree中可能有2个级别;对于200K行,则是3个级别。) / p>

查询缓存可以使您按时跳闸(如果已打开)。计时时,请执行SELECT SQL_NO_CACHE ...以避免质量控制。如果启用了质量控制并应用了质量控制,则相同查询的秒和随后的运行将非常接近0.000秒。

那些时间在0.5毫秒至1.2毫秒之间变化-将其计时到月相。严重的是,任何低于10ms的时序都不应被信任。这是由于 可能同时在计算机上发生的所有其他情况。您可以通过平均多次运行来对它进行调整-确保避免(1)查询缓存和(2)I / O。

关于I / O ...这可以回溯到我之前的评论,即当表(和/或索引)大于可缓存在RAM中时可能发生的情况。

  • 当小于RAM时,第一次运行可能会从磁盘中获取内容。第二轮及以后的运行可能会更快,更一致。
  • 比RAM大,所有运行 都需要打入磁盘。因此, all 可能较慢,并且可能比您发现的变化更为脆弱。

从技术上讲,您的标签不正确。 MySQL的大多数索引都是BTrees(实际上是B + Trees),而不是Binary Trees。 (当然,有很多相似之处,并且许多原则是共享的。)

回到您的研究目标。

  • 假设存在“背景噪音”,干扰了您的数字。
  • 使测试变得平凡无奇(例如,没有索引的情况),以使其压倒一切,或者
  • 重复计时以掩盖问题。并且一定要忽略第一次运行。

执行任何SELECT main 成本是它接触的行数。

  • 使用您的UNIQUE索引,它正在触及1行。因此,请期待快速和O(1)(加上噪声)。
  • 没有索引,它将触摸N行表的N行。所以期望O(N)。