SQL 优化、索引、执行计划、慢查询
<h2>一、SQL 为什么会慢?</h2><div> </div>
<div>根本原因 3 个:</div>
<div> </div>
<ol>
<li>没走索引 / 索引失效(全表扫描)</li>
<li>数据量太大(没分页、没过滤)</li>
<li>写法复杂(子查询嵌套、join 太多、排序分组无索引)</li>
</ol>
<div> </div>
<hr>
<div> </div>
<h2>二、索引(SQL 优化的核心)</h2>
<div> </div>
<h3>1. 什么是索引?</h3>
<div> </div>
<div>可以理解为书的目录:</div>
<div> </div>
<ul>
<li>没有索引 = 一页一页翻(全表扫描)</li>
<li>有索引 = 直接查目录定位(快速查找)</li>
</ul>
<div> </div>
<h3>2. 最常用索引类型</h3>
<div> </div>
<ol>
<li>主键索引(PRIMARY KEY)
<div> </div>
唯一、非空、最快</li>
<li>唯一索引(UNIQUE)
<div> </div>
列值不能重复</li>
<li>普通索引(INDEX)
<div> </div>
最常用,加速查询</li>
<li>联合索引(复合索引)
<div> </div>
最关键! 多列一起建索引</li>
</ol>
<div> </div>
<h3>3. 联合索引最左前缀原则</h3>
<div> </div>
<div>索引 <code>(a, b, c)</code></div>
<div> </div>
<ul>
<li>能命中:<code>where a=?</code>、<code>where a=? and b=?</code>、<code>where a=? and b=? and c=?</code></li>
<li>不能命中:<code>where b=?</code>、<code>where c=?</code>、<code>where b=? and c=?</code></li>
</ul>
<div> </div>
<div>一句话:必须从左到右连续使用,不能跳过前面的列。</div>
<div> </div>
<h3>4. 哪些情况索引会失效?(高频)</h3>
<div> </div>
<ol>
<li>索引列上运算 / 函数:<code>where age+1=10</code>、<code>where substr(name,1,2)='ab'</code></li>
<li>隐式类型转换:<code>where phone=13800138000</code>(phone 是字符串)</li>
<li>like 以 % 开头:<code>where name like '%张三'</code></li>
<li>使用!= /not in /is not null(会导致索引失效)</li>
<li>or 连接非索引列</li>
<li>联合索引不满足最左前缀</li>
</ol>
<div> </div>
<h3>5. 建索引的最佳实践</h3>
<div> </div>
<ul>
<li>where 经常查询的列建索引</li>
<li>group by / order by 字段建索引</li>
<li>join 关联字段建索引</li>
<li>不要滥用索引(写入会变慢)</li>
<li>区分度低的列不要建索引(如性别、状态)</li>
<li>优先用联合索引,少用多个单值索引</li>
</ul>
<div> </div>
<hr>
<div> </div>
<h2>三、执行计划(看懂 SQL 到底怎么跑)</h2>
<div> </div>
<div>使用命令(MySQL):</div>
<div> </div>
<div>
<div dir="ltr">
<div>
<pre><code>EXPLAIN SELECT * FROM user WHERE name = '张三';
</code></pre>
</div>
<div> </div>
</div>
</div>
<div> </div>
<h3>重点看 3 个字段</h3>
<div> </div>
<ol>
<li>
<div>type(最重要)
<div> </div>
性能从好到坏:
<div> </div>
<code>system > const > eq_ref > ref > range > index > ALL</code></div>
<div> </div>
<ul>
<li>ALL = 全表扫描(必须优化)</li>
<li>ref = 走了索引(良好)</li>
<li>range = 范围索引(良好)</li>
</ul>
<div> </div>
</li>
<li>
<div>key
<div> </div>
实际使用了哪个索引,为 NULL 表示没走索引。</div>
<div> </div>
</li>
<li>
<div>Extra</div>
<div> </div>
<ul>
<li><code>Using filesort</code>:文件排序(必须优化)</li>
<li><code>Using temporary</code>:用到临时表(必须优化)</li>
<li><code>Using index</code>:覆盖索引(最优)</li>
</ul>
<div> </div>
</li>
</ol>
<div> </div>
<hr>
<div> </div>
<h2>四、常见慢查询 + 优化方案</h2>
<div> </div>
<h3>1. 慢查询 1:全表扫描</h3>
<div> </div>
<div>
<div dir="ltr">
<div>
<pre><code>SELECT * FROM user WHERE name = '张三';
</code></pre>
</div>
<div> </div>
</div>
</div>
<div> </div>
<div>优化:给 name 建索引</div>
<div> </div>
<div>
<div dir="ltr">
<div>
<pre><code>CREATE INDEX idx_name ON user(name);
</code></pre>
</div>
<div> </div>
</div>
</div>
<div> </div>
<hr>
<div> </div>
<h3>2. 慢查询 2:like % xxx(索引失效)</h3>
<div> </div>
<div>
<div dir="ltr">
<div>
<pre><code>SELECT * FROM user WHERE name LIKE '%张三';
</code></pre>
</div>
<div> </div>
</div>
</div>
<div> </div>
<div>优化:</div>
<div> </div>
<ul>
<li>业务允许:改成 <code>LIKE '张三%'</code></li>
<li>必须模糊:用 ES 或 MySQL 全文索引</li>
</ul>
<div> </div>
<hr>
<div> </div>
<h3>3. 慢查询 3:索引列上用函数</h3>
<div> </div>
<div>
<div dir="ltr">
<div>
<pre><code>SELECT * FROM user WHERE YEAR(create_time) = 2025;
</code></pre>
</div>
<div> </div>
</div>
</div>
<div> </div>
<div>优化:</div>
<div> </div>
<div>
<div dir="ltr">
<div>
<pre><code>SELECT * FROM user
WHERE create_time >= '2025-01-01'
AND create_time < '2026-01-01';
</code></pre>
</div>
<div> </div>
</div>
</div>
<hr>
<div> </div>
<h3>4. 慢查询 4:order by 无索引</h3>
<div> </div>
<div>
<div dir="ltr">
<div>
<pre><code>SELECT * FROM user ORDER BY create_time DESC;
</code></pre>
</div>
<div> </div>
</div>
</div>
<div> </div>
<div>优化:给排序字段建索引</div>
<div> </div>
<hr>
<div> </div>
<h3>5. 慢查询 5:join 无索引</h3>
<div> </div>
<div>
<div dir="ltr">
<div>
<pre><code>SELECT * FROM order o
JOIN user u ON o.user_id = u.id
WHERE u.name = '张三';
</code></pre>
</div>
<div> </div>
</div>
</div>
<div> </div>
<div>优化:</div>
<div> </div>
<ul>
<li><code>order.user_id</code> 建索引</li>
<li><code>user.name</code> 建索引</li>
</ul>
<div> </div>
<hr>
<div> </div>
<h3>6. 慢查询 6:not in / != 导致索引失效</h3>
<div> </div>
<div>
<div dir="ltr">
<div>
<pre><code>SELECT * FROM user WHERE status != 1;
</code></pre>
</div>
<div> </div>
</div>
</div>
<div> </div>
<div>优化:</div>
<div> </div>
<ul>
<li>改用范围</li>
<li>或业务上调整为正向查询</li>
</ul>
<div> </div>
<hr>
<div> </div>
<h3>7. 慢查询 7:分页太深</h3>
<div> </div>
<div>
<div dir="ltr">
<div>
<pre><code>SELECT * FROM user LIMIT 1000000,10;
</code></pre>
</div>
<div> </div>
</div>
</div>
<div>优化:</div>
<div> </div>
<div>
<div dir="ltr">
<div>
<pre><code>SELECT * FROM user
WHERE id > 1000000
LIMIT 10;
</code></pre>
</div>
<div> </div>
</div>
</div>
<hr>
<div> </div>
<h2>五、SQL 优化便签</h2>
<div> </div>
<ol>
<li>先看是否走索引</li>
<li>避免全表扫描(type=ALL)</li>
<li>索引列不运算、不函数</li>
<li>联合索引遵守最左前缀</li>
<li>少用 select *,只查需要的列</li>
<li>join 必须加索引</li>
<li>深度分页用主键过滤</li>
<li>避免 Using filesort 和 Using temporary</li>
</ol>
<div> </div>
<hr>
<div> </div>
<h2>六、快速排查慢查询步骤</h2>
<div> </div>
<ol>
<li>开启慢查询日志</li>
<li>用 <code>EXPLAIN</code> 看执行计划</li>
<li>看 <code>type</code> 是否 ALL(全表扫描)</li>
<li>看 <code>key</code> 是否为 NULL(没走索引)</li>
<li>看 <code>Extra</code> 是否有文件排序 / 临时表</li>
<li>建索引 / 改 SQL 重新验证</li>
</ol>
<div> </div>
<hr>
<div> </div>
<h3>总结</h3>
<div> </div>
<ul>
<li>索引是 SQL 优化的核心,联合索引最常用</li>
<li>执行计划判断 SQL 好坏</li>
<li>慢查询 90% 是索引问题</li>
<li>索引列不运算、不函数、不隐式转换</li>
</ul><br><br>
来源:https://www.cnblogs.com/chuansheng/p/19914144
頁:
[1]