簡體   English   中英

索引列和非索引列研究

[英]Indexed column and not indexed column research

我生成了單獨的 MySQL Innodb 表,其中包含 2000、5000、10000、50000、10000、20000、50000、100 000、200 000 個元素(借助 php 循環和插入查詢)。 每個表有兩列:id(Primary Key INT autoincrement)、number(INT UNIQUE KEY)。 然后我做了同樣的但這次我生成了類似的表,其中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

結果如下:

  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

這是信息圖 它顯示了元素數量如何取決於索引/未索引列的最壞情況查詢時間。 索引是紅色。 當我測試速度時,我在 mysql 控制台中輸入了 2 次相同的查詢,因為我發現當您第一次進行查詢時,有時查詢未索引列甚至比索引列還要快一點。 問題是:為什么這種對 200000 個元素的查詢有時比對列號進行索引時對 100000 個元素的相同查詢花費的時間更少。 你可以看到還有其他對我來說不可預測的結果。 我問這個,因為當列號沒有被索引時,結果是可以預測的:200000 el 時間總是大於 100000。請告訴我在嘗試對 UNIQUE 索引列進行研究時我做錯了什么。

在未索引的情況下,它始終是全表掃描,因此時間與行號很好地吻合,如果它被索引,您正在測量索引查找時間,這在您的情況下是恆定的(小數字,小偏差)

這還不是“最壞”的情況。

  • 使UNIQUE密鑰隨機而不是與 PK 處於鎖定步驟。 這樣的一個例子是UUID()
  • 生成足夠多的行,以便表和索引無法放入 buffer_pool。

如果兩者兼而有之,您最終會看到性能顯着下降。

UNIQUE鍵對INSERTs有以下影響:返回給客戶端之前檢查唯一性約束。 對於非 UNIQUE 索引,插入索引的 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 大,所有運行都可能需要訪問磁盤。 因此,一切都可能很慢,而且可能比您發現的變化更不穩定。

從技術上講,您的標簽不正確。 MySQL的索引大多是BTrees(實際上是B+Trees),而不是二叉樹。 (當然,有很多相似之處,許多原則是共享的。)

回到你的研究目標。

  • 假設有“背景噪音”擾亂了您的數字。
  • 使您的測試不平凡(例如非索引情況),以便它壓倒噪音,或
  • 重復計時以掩蓋問題。 並且一定要忽略第一次運行。

執行任何SELECT主要成本是它接觸了多少行。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM