我分别生成了包含2000、5000、10000、50000、10000、20000、50000、100000、200000个元素的mysql innodb表(借助php循环和insert query)。每个表有两列:id(主键int autoincrement)、number(int unique key)。然后我做了同样的操作,但这次我生成了类似的表,其中numbercolumn没有索引,我以这样的方式生成表:column number的值等于index+2的值:第一个元素==3,第1000个元素是1002,依此类推。我想测试这样的查询,因为它将在我的应用程序中使用:
SELECT count(number) FROM number_two_hundred_I WHERE number=200002;
为这些表生成数据之后,我想测试最坏情况查询的时间。我用过它的展示资料。我假设最坏情况下的查询将对应于列号为1002、2002等的元素,因此下面是我测试的所有查询和时间(通过show profiles进行评估):
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
结果如下:
2000 el-索引0.00099250未索引-0.0026275
5000 el-索引0.00095600未索引-0.00404125
10000 el-索引0.00156900未索引-0.00761750
20000 el-索引0.00155850未索引-0.01452820
50000 el-索引0.00051100未索引-0.04127450
100000 el索引0.00121750未索引-0.07120075
200000 el索引0.00095025未索引-0.11406950
这是这方面的信息图表。它显示了元素的数量如何依赖于索引列/未索引列的最坏查询时间。它是红色的。当我测试速度时,我在mysql控制台中输入了两次相同的查询,因为我发现当您第一次进行查询时,有时对未索引列的查询甚至比对已索引列的查询快一点。问题是:为什么这种类型的对200000个元素的查询有时比对列编号进行索引时对100000个元素的查询花费更少的时间。你可以看到还有其他不可预知的结果。我问这个问题,因为当列号没有索引时,结果是可以预测的:200000 el时间总是大于100000。请告诉我,当我试图研究唯一索引列时,我做错了什么。
2条答案
按热度按时间lh80um4z1#
这不是“最坏”的情况。
使
UNIQUE
随机键,而不是与pk同步锁定。这方面的一个例子是UUID()
.生成足够的行,使表和索引无法放入缓冲池。
如果你同时使用这两种方法,你最终会看到性能明显下降。
UNIQUE
关键点对INSERTs
:在返回到客户端之前检查唯一性约束。对于非唯一索引,要插入到索引的btree中的工作可能会(并且会)延迟(cf“更改缓冲区”)。如果第二列没有索引,那么要做的工作就更少了。WHERE number=2002
--与
UNIQUE(number)
--向下钻取btree。速度很快,效率很高。与
INDEX(number)
--向下钻取btree。速度很快,效率很高。但是,它稍微慢了一点,因为它不能假设只有一行。也就是说,在btree中找到正确的位置后,它将向前扫描(非常有效),直到找到2002以外的值。没有索引
number
--扫描整个表格。因此,成本取决于表的大小,而不是表的值number
. 它不知道2002年是否存在于表中的任何地方,也不知道有多少次。如果你画出你得到的时间,你会发现它是相当线性的。我建议你用日志纸做图表。不管怎样,请注意非索引大小写是多么线性。索引的情况基本上是不变的。查找number=200002和查找number=2002一样便宜。这适用于
UNIQUE
以及INDEX
. (实际上,这条线有一个很小的上升,因为btree实际上是o(logn),而不是o(1)。对于2k行,btree中可能有2个级别;对于200k,3个级别。)查询缓存可能会在计时上绊倒您(如果它已打开)。在计时的时候,做什么
SELECT SQL_NO_CACHE ...
避免质量控制。如果qc已打开并应用,则相同查询的第二次和后续运行将花费非常接近0.000秒的时间。那些在0.5到1.2毫秒之间变化的时间——把它归为月球的相位。说真的,任何低于10毫秒的时间都是不可信的。这是因为计算机上可能同时发生的所有其他事情。您可以通过平均多次运行来调整它——确保避免(1)查询缓存和(2)i/o。
至于i/o。。。这又回到了我之前的评论,当表(和/或索引)大于可以缓存在ram中的值时会发生什么。
当小于ram时,第一次运行可能会从磁盘中获取内容。第二次和随后的运行可能会更快和一致。
当内存大于ram时,所有的运行可能都需要命中磁盘。因此,一切都可能是缓慢的,也许比你发现的变化更脆弱。
从技术上讲,你的标签是不正确的。mysql的大多数索引都是b树(实际上是b+树),而不是二叉树(当然,有很多相似之处,而且许多原则是共享的。)
回到你的研究目标。
假设有“背景噪音”干扰了你的数字。
使你的测试不琐碎(如非索引的情况),以便它压倒噪音,或
重复计时以掩盖问题。一定要忽略第一次跑步。
执行任何操作的主要成本
SELECT
它接触了多少行。用你的
UNIQUE
索引,它是接触1行。所以期待快速和o(1)(加上噪音)。如果没有索引,对于一个n行的表,它将接触n行。所以期待o(n)。
eufgjt7s2#
在没有索引的情况下,它始终是一个完整的表扫描,因此时间与行号很好地协调,如果它被索引,则您正在测量索引查找时间,在您的情况下它是恒定的(小数字,小偏差)