【问题标题】:Indexed column and not indexed column research索引列与非索引列研究
【发布时间】:2019-03-17 00:24:24
【问题描述】:

我生成了单独的 MySQL Innodb 表,其中包含 2000、5000、10000、50000、10000、20000、50000、100 000、200 000 个元素(借助 php 循环和插入查询)。 每个表有两列:id(Primary Key INT autoincrement),number(INT UNIQUE KEY)。然后我做了同样的事情,但这次我生成了类似的表,其中 number 列没有索引。我以这样的方式生成表:列的值 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 el 索引 0.00121750 未索引 - 0.07120075
  7. 200000 el 索引 0.00095025 未索引 - 0.11406950

这里是infographic。它显示了元素数量如何取决于索引/未索引列的最坏情况查询时间。索引为红色。 当我测试速度时,我在 mysql 控制台中输入了 2 次相同的查询,因为我发现当你第一次查询时,有时查询未索引的列会更快一些,比索引一个。问题是:为什么这种对 200000 个元素的查询有时比在索引列号时对 100000 个元素的相同查询花费更少的时间。你可以看到还有其他我无法预料的结果。我问这个,因为当列号没有被索引时,结果是相当可预测的:200000 el 时间总是大于 100000。请告诉我在尝试研究 UNIQUE 索引列时我做错了什么。

【问题讨论】:

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


    【解决方案1】:

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

    【讨论】:

    • 感谢您的回答。我想知道为什么会出现这个小偏差?我正在写一些研究工作,我想描述它为什么会出现。可能是电脑本身的原因,也可能是mysql计时系统的原因。
    【解决方案2】:

    这不是“最坏”的情况。

    • 使UNIQUE 密钥随机而不是与PK 同步。 UUID() 就是一个例子。
    • 生成足够多的行,以使表和索引无法放入 buffer_pool。

    如果你们两个都最终会看到性能显着下降。

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

    WHERE number=2002 --

    • 使用UNIQUE(number) -- 向下钻取 BTree。非常快,非常高效。
    • 使用 INDEX(number) -- 向下钻取 BTree。非常快,非常高效。但是它稍微慢一些,因为它不能假设只有一个这样的行。也就是说,在 BTree 中找到正确的位置后,它会向前扫描(非常有效),直到找到 2002 以外的值。
    • number 上没有索引——扫描整个表。所以成本取决于表大小,而不是number 的值。它不知道 2002 是否存在于表中的任何位置,或者存在多少次。如果你绘制你得到的时间,你会发现它是相当线性的。

    我建议您在图表中使用 log-log 'paper'。无论如何,请注意非索引案例的线性度。并且索引案例基本上是恒定的。查找 number=200002 与查找 number=2002 一样便宜。这适用于UNIQUEINDEX。 (实际上,由于BTree实际上是O(log n),而不是O(1),所以该行略有上升。对于2K行,BTree中可能有2个级别;对于200K,3个级别。)

    查询缓存可能会在时间上绊倒您(如果已打开)。计时时,请SELECT SQL_NO_CACHE ... 以避免QC。如果 QC 已打开并适用,则 第二次和后续次运行相同查询将需要非常接近 0.000 秒。

    那些在 0.5 毫秒和 1.2 毫秒之间变化的时间 - 将其归结为月相。严肃地说,任何低于 10 毫秒的时间都不应该被信任。这是因为计算机上可能同时发生的所有其他事情。您可以通过平均多次运行来缓和它——确保避免 (1) 查询缓存和 (2) I/O。

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

    • 当小于 RAM 时,第一次运行可能会从磁盘获取内容。第二次和后续运行可能会更快且更一致。
    • 如果比 RAM 大,所有运行可能都需要命中磁盘。因此,all 可能会很慢,而且可能比您发现的变体更不稳定。

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

    回到你的研究目标。

    • 假设有“背景噪音”干扰了您的数字。
    • 让您的测试不平凡(例如,非索引案例),以便它压倒噪音,或
    • 重复计时以掩盖问题。并确保忽略第一次运行。

    执行任何SELECTmain 成本是它触及的行数。

    • 使用您的UNIQUE 索引,它触及1 行。所以期待快速和 O(1)(加上噪音)。
    • 如果没有索引,它会触及 N 行表的 N 行。所以期望 O(N)。

    【讨论】:

    • 非常感谢您回答 Rick。我理解你告诉我的大意。但是,现在我不知道什么是 buffer_pool,如何查看它的当前值。我也不明白你的意思是让你的测试变得不平凡。但我觉得在那之后我可以做更好的研究。所以我会尽量在一周内完成。
    • SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
    • Order(log n) 和 O(log n) 一样吗?
    • @MarkiyanProniuk - 是的。 (我忘记了符号的正确拼写。)我指的是“大 O”:en.wikipedia.org/wiki/… 我修好了。
    猜你喜欢
    • 2018-07-27
    • 2020-05-06
    • 1970-01-01
    • 2011-11-07
    • 2010-09-15
    • 2023-04-08
    • 1970-01-01
    • 2019-03-22
    相关资源
    最近更新 更多