林先 發表於 2025-7-6 13:29:00

MySQL 05 深入浅出索引(下)

<h3 id="覆盖索引">覆盖索引</h3>
<p>假设要执行一条语句:</p>
<pre><code class="language-sql">select * from T where k between 3 and 5;
</code></pre>
<p>初始对列k建立了索引,表中数据为:</p>
<div align="center"><img src="https://img2024.cnblogs.com/blog/3389949/202507/3389949-20250705143019933-406424357.png" width="35%"></div>
<p>那么其执行流程为:</p>
<ul>
<li>
<p>在k索引树上找到k=3的记录,取得ID=300;</p>
</li>
<li>
<p>到ID索引树查到ID=300对应的记录。</p>
</li>
<li>
<p>在k索引树上找到下一个值k=5的记录,取得ID=500;</p>
</li>
<li>
<p>到ID索引树查到ID=500对应的记录。</p>
</li>
<li>
<p>在k索引树上找到下一个值k=6,发现不满足条件。</p>
</li>
</ul>
<p>这个过程中,发生了回表。假如建立的索引能“覆盖”我们的查询需求,就不需要回表,这种索引称为<strong>覆盖索引</strong>。比如在上面的查询语句中,若查询的是列k而不是*,就是一个覆盖索引。</p>
<p>需要注意的是,在引擎内部使用覆盖索引在索引k上其实能读到R3-R5三个记录,但是由于Server层只会从引擎得到两条记录,因此MySQL认为扫描行数为2。</p>
<h3 id="最左前缀原则">最左前缀原则</h3>
<p>B+树这种索引结构,可以利用索引的最左前缀,来定位记录。</p>
<p>比如建立了<code>(name,age)</code>的联合索引:</p>
<div align="center"><img src="https://img2024.cnblogs.com/blog/3389949/202507/3389949-20250705143109812-238786452.png" width="35%"></div>
<p>当要查询<code>name="张三"</code>,可以快速定位到ID4,然后向后遍历。</p>
<p>当要查询<code>where name like "张%"</code>,也能用上这个索引,定位到ID3,然后向后遍历。</p>
<p>因此,索引的最左前缀可以是联合索引的最左N个字段,也可以是字符串索引的最左M个字符。</p>
<h3 id="索引下推">索引下推</h3>
<p>还是假设建立了<code>(name,age)</code>的联合索引,现在有一条SQL语句如下:</p>
<pre><code class="language-sql">select * from tuser where name like '张%' and age=10 and ismale=1;
</code></pre>
<p>由最左前缀原则,这条语句能用上name的索引。</p>
<p>而其他条件的判断:</p>
<ul>
<li>
<p>在MySQL 5.6前,只能从找到的第一个记录ID3开始一个个回表,到主键索引上找出数据行,再对比后面两个字段。</p>
</li>
<li>
<p>MySQL 5.6引入了索引下推优化,可以在索引遍历过程中,对索引中包含的字段先做判断,<strong>直接过滤</strong>不满足条件的记录,减少回表次数。</p>
</li>
</ul>
<p>两者的比较如下,第一张图是无索引下推,第二张图是有索引下推,箭头表示回表:</p>
<div align="center"><img src="https://img2024.cnblogs.com/blog/3389949/202507/3389949-20250705143222993-694524967.png" width="35%"></div>
<div align="center"><img src="https://img2024.cnblogs.com/blog/3389949/202507/3389949-20250705143243888-954758985.png" width="35%"></div><br><br>
来源:https://www.cnblogs.com/san-mu/p/18967216
頁: [1]
查看完整版本: MySQL 05 深入浅出索引(下)