MySQL 索引分析

<div class="markdown_views"> <h1 id="mysql複合惟一索引分析"><a name="t0"></a>MySQL複合惟一索引分析</h1>mysql

<hr>sql

<h4 id="關於複合惟一索引unique-key-或-unique-index網上搜索很多人說這種索引發到的關鍵做用是約束查詢時性能上沒有獲得提升或者查詢時根本沒有走索引列也有人說查詢時使用到了索引和普通索引同樣那麼問題究竟是怎樣的呢">關於複合惟一索引(unique key 或 unique index),網上搜索很多人說:」這種索引發到的關鍵做用是約束,查詢時性能上沒有獲得提升或者查詢時根本沒有走索引列「。也有人說:「查詢時使用到了索引和普通索引同樣「。那麼問題究竟是怎樣的呢?</h4>markdown

<h3 id="測試準備工做"><a name="t1"></a>測試準備工做</h3>app

<p>準備建表語句,插入數據等工做:</p>性能

<pre class="prettyprint" name="code"><code class="language-sql hljs has-numbering"><span class="hljs-comment">-- 建表(注:a,b是複合惟一索引)</span></br> <span class="hljs-operator"><span class="hljs-keyword">create</span> <span class="hljs-keyword">table</span> test0(id bigint <span class="hljs-keyword">not</span> <span class="hljs-keyword">null</span> <span class="hljs-keyword">primary</span> <span class="hljs-keyword">key</span> auto_increment, a <span class="hljs-keyword">varchar</span>(<span class="hljs-number">10</span>) <span class="hljs-keyword">not</span> <span class="hljs-keyword">null</span>, b <span class="hljs-keyword">varchar</span></br>(<span class="hljs-number">10</span>) <span class="hljs-keyword">not</span> <span class="hljs-keyword">null</span>, <span class="hljs-keyword">unique</span> index(a, b))engine=innodb charset=utf8 auto_increment=<span class="hljs-number">1</span>;</span></br></br> <span class="hljs-comment">-- 插入數據</span></br> <span class="hljs-operator"><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> test0(a, b)<span class="hljs-keyword">values</span>(<span class="hljs-string">'china'</span>, <span class="hljs-string">'chinese'</span>);</span></br> <span class="hljs-operator"><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> test0(a, b)<span class="hljs-keyword">values</span>(<span class="hljs-string">'japan'</span>, <span class="hljs-string">'japanese'</span>);</span></br> <span class="hljs-operator"><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> test0(a, b)<span class="hljs-keyword">values</span>(<span class="hljs-string">'germany'</span>, <span class="hljs-string">'german'</span>);</span></br> <span class="hljs-operator"><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> test0(a, b)<span class="hljs-keyword">values</span>(<span class="hljs-string">'korea'</span>, <span class="hljs-string">'korea'</span>);</span></br> <span class="hljs-operator"><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> test0(a, b)<span class="hljs-keyword">values</span>(<span class="hljs-string">'france'</span>, <span class="hljs-string">'frence'</span>);</span></br> <span class="hljs-operator"><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> test0(a, b)<span class="hljs-keyword">values</span>(<span class="hljs-string">'australia'</span>, <span class="hljs-string">'australia'</span>);</span></br> <span class="hljs-operator"><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> test0(a, b)<span class="hljs-keyword">values</span>(<span class="hljs-string">'america'</span>, <span class="hljs-string">'american'</span>);</span></br> <span class="hljs-operator"><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> test0(a, b)<span class="hljs-keyword">values</span>(<span class="hljs-string">'brazil'</span>, <span class="hljs-string">'brazil'</span>);</span></br></br> <span class="hljs-comment">-- 執行計劃一(查詢存在紀錄)</span></br> explain <span class="hljs-operator"><span class="hljs-keyword">select</span> * <span class="hljs-keyword">from</span> test0 <span class="hljs-keyword">where</span> a=<span class="hljs-string">'france'</span> <span class="hljs-keyword">and</span> b=<span class="hljs-string">'frence'</span>;</span></br> <span class="hljs-comment">-- 執行計劃二(查詢不存在紀錄)</span></br> explain <span class="hljs-operator"><span class="hljs-keyword">select</span> * <span class="hljs-keyword">from</span> test0 <span class="hljs-keyword">where</span> a=<span class="hljs-string">'france'</span> <span class="hljs-keyword">and</span> b=<span class="hljs-string">'america'</span>;</span></code></pre>測試

<p>執行計劃一: <br> <img src="https://img-blog.csdn.net/20160627181146503" alt="這裏寫圖片描述" title=""></p>優化

<p>執行計劃二: <br> <img src="https://img-blog.csdn.net/20160627181211987" alt="這裏寫圖片描述" title=""></p>this

<p>綜上能夠看出: <br> 執行計劃一,查詢條件匹配時命中全部的索引; <br> 執行計劃二,查詢條件不匹配時沒有命中索引,並返回一條Extra」Impossible WHERE noticed after reading const tables」</p>spa

<p>那麼到底這兩次查詢有什麼不一樣呢? <br> MySQL關於這種索引的執行計劃以及優化方案是什麼? <br> 我不得不把官方的Doc閱讀一遍,關鍵點總結以下:</p>.net

<p><strong>Explain join types</strong> 執行計劃」type」列表,從執行性能最好到最壞: <br> 1. system <br> 表中只有一行數據(=system table)。這是常數鏈接類型的特例。 <br> 2. const <br> 在查詢開始時讀取表時僅有一行能夠匹配的數據。由於僅匹配到一行數據,因此值列能夠被認爲是常數級優化。常數表查詢很是快由於它們只讀取一次就能命中。 <br> 常數類型僅僅在匹配」PRIMARY KEY」或者」UNIQUE INDEX」全部的列值時纔會被使用。 <br> 下面的查詢,表被當成常數表執行:</p>

<pre class="prettyprint" name="code"><code class="language-sql hljs has-numbering"> <span class="hljs-comment">-- query 1</span></br> <span class="hljs-operator"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> tbl_name <span class="hljs-keyword">WHERE</span> primary_key=<span class="hljs-number">1</span>;</span></br> <span class="hljs-operator"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> tbl_name</br> <span class="hljs-keyword">WHERE</span> primary_key_part1=<span class="hljs-number">1</span> <span class="hljs-keyword">AND</span> primary_key_part2=<span class="hljs-number">2</span>;</span></code></pre>

<ol> <li>ref <br> 全部帶索引值匹配的行都從這張表讀取各類組合的行從以前的表讀取。若是鏈接使用key的只是最左前綴,或者key不是PRIMARY KEY和UNIQUE index使用ref(換句話說,若是鏈接跟進key值沒有查詢單行數據)。若是這個key值使用時匹配了僅僅少數的行,這就是一個比較好的類型。 <br> ref 能夠用在索引列,條件匹配時使用」=」 或者 「&lt;=&gt;」操做。下面的示例,MySQL 可以使用ref鏈接處理 ref_table:</li> </ol>

<pre class="prettyprint" name="code"><code class="language-sql hljs has-numbering"> <span class="hljs-operator"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> ref_table <span class="hljs-keyword">WHERE</span> key_column=expr;</span></br></br> <span class="hljs-operator"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> ref_table,other_table</br> <span class="hljs-keyword">WHERE</span> ref_table.key_column=other_table.<span class="hljs-keyword">column</span>;</span></br></br> <span class="hljs-operator"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> ref_table,other_table</br> <span class="hljs-keyword">WHERE</span> ref_table.key_column_part1=other_table.<span class="hljs-keyword">column</span></br> <span class="hljs-keyword">AND</span> ref_table.key_column_part2=<span class="hljs-number">1</span>;</span></code></pre>

<ol> <li>ref_or_null <br> 這種鏈接有點相似於ref, 可是查詢行包含NULL值時,MySQL會做額外的查詢。這種鏈接類型的優化最常常在解決子查詢的時候使用。下面的例子,MySQL會使用ref_or_nulll鏈接處理ref_table:</li> </ol>

<pre class="prettyprint" name="code"><code class="hljs sql has-numbering"><span class="hljs-operator"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> ref_table <span class="hljs-keyword">WHERE</span> key_column=expr <span class="hljs-keyword">OR</span> key_column <span class="hljs-keyword">IS</span> <span class="hljs-keyword">NULL</span>;</span></code></pre>

<pre class="prettyprint"><code class="has-numbering">參見 8.2.1.6, 「IS NULL 優化」. </code></pre>

<ol> <li><p>index_merge <br> 這種鏈接類型說明已經啓用索引合併優化。這種場景下,輸出行中的key列包含用於索引的列表,而且key_len顯示索引使用的最長key parts。更多信息參見「索引合併優化」。</p>

<ol><li>range <br> 只有當行根據給定範圍檢索到時,使用一個索引查詢這些行。輸出行的key列代表使用哪一個索引。key_len列顯示使用最長的key part。這種鏈接ref列爲NULL。 <br> 當key列和一個常量使用」=」, 「&lt;&gt;」, 「&gt;」, 「&gt;=」, 「&lt;」, 「&lt;=」, IS NULL, 「&lt;=&gt;」, BETWEEN, 或者IN()比較時使用range鏈接:</li></ol></li> </ol>

<pre class="prettyprint" name="code"><code class="language-sql hljs has-numbering"> <span class="hljs-operator"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> tbl_name <span class="hljs-keyword">WHERE</span> key_column = <span class="hljs-number">10</span>;</span></br></br> <span class="hljs-operator"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> tbl_name <span class="hljs-keyword">WHERE</span> key_column BETWEEN <span class="hljs-number">10</span> <span class="hljs-keyword">and</span> <span class="hljs-number">20</span>;</span></br> <span class="hljs-operator"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> tbl_name <span class="hljs-keyword">WHERE</span> key_column <span class="hljs-keyword">IN</span> (<span class="hljs-number">10</span>,<span class="hljs-number">20</span>,<span class="hljs-number">30</span>);</span></br></br> <span class="hljs-operator"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> tbl_name <span class="hljs-keyword">WHERE</span> key_part1 = <span class="hljs-number">10</span> </br> <span class="hljs-keyword">AND</span> key_part2 <span class="hljs-keyword">IN</span> (<span class="hljs-number">10</span>,<span class="hljs-number">20</span>,<span class="hljs-number">30</span>);</span></code></pre>

<ol> <li><p>index <br> 索引鏈接類型和ALL同樣,除了掃描索引樹。兩種執行方式: <br> 若是索引在查詢裏是一個覆蓋性的索引,而且可以查詢表中全部知足的數據,只有索引樹被掃描。這種場景下,Extra列會顯示」Using index」。一般只走索引掃描比全表掃描要快不少,由於索引的數量一般比表中的數據量要少不少。全表掃描優化方案是經過讀取索引並按照索引順序檢索數據。使用索引不會在Etra列顯示。列使用單一索引時,MySQL使用這種鏈接類型。</p>

<ol><li>ALL <br> 全表掃描。一般,應該經過添加索引避免全表掃描。 </li></ol></li> </ol>

<p>EXPLAIN Extra Information</p>

<p>The Extra column of EXPLAIN output contains additional information about how MySQL resolves the query. The following list explains the values that can appear in this column. If you want to make your queries as fast as possible, look out for Extra values of Using filesort and Using temporary.</p>

<pre class="prettyprint"><code class="has-numbering">1. Child of 'table' pushed join@1</br></br> This table is referenced as the child of table in a join that can be pushed down to the NDB kernel. Applies only in MySQL Cluster NDB 7.2 and later, when pushed-down joins are enabled. See the description of the ndb_join_pushdown server system variable for more information and examples. 2. const row not found</br></br> For a query such as SELECT ... FROM tbl_name, the table was empty.</br> 3. Distinct</br></br> MySQL is looking for distinct values, so it stops searching for more rows for the current row combination after it has found the first matching row.</br></br> 4. Full scan on NULL key</br></br> This occurs for subquery optimization as a fallback strategy when the optimizer cannot use an index-lookup access method.</br></br> 5. Impossible HAVING</br></br> The HAVING clause is always false and cannot select any rows.</br></br> 6. Impossible WHERE</br></br> The WHERE clause is always false and cannot select any rows.</br></br> Impossible WHERE noticed after reading const tables</br></br> MySQL has read all const (and system) tables and notice that the WHERE clause is always false. </code></pre>

<hr> </div>

相關文章
相關標籤/搜索