MySQL 索引优化技巧

MySQL 索引优化技巧

索引是提升查询性能的关键,但用不好反而拖慢速度。

1. 为什么要建索引

  • 没有索引要全表扫描
  • 数据量大时查询很慢
  • 索引相当于书的目录
  • 合适的索引能提升百倍速度

2. 索引类型

主键索引:

  • 主键自动创建
  • 唯一且非空

普通索引:

  • 最基本的索引
  • 加速查询

唯一索引:

  • 值不能重复
  • 比如手机号

联合索引:

  • 多个字段组成
  • 遵循最左前缀原则

3. 建索引原则

1)最频繁查询的字段建索引

-- 经常按name查
CREATE INDEX idx_name ON users(name);

2)区分度高的字段建索引

  • 性别这种区分度低的不要建
  • 手机号、身份证这种适合

3)联合索引注意顺序

-- 联合索引 (a, b, c)
-- 最左前缀:
-- a 能用
-- a,b 能用
-- a,b,c 能用
-- 单独b,c 用不了

4. 索引失效情况

1)where 里对索引字段用函数

-- 失效
SELECT * FROM users WHERE DATE(create_time) = '2026-01-01';

-- 有效
SELECT * FROM users WHERE create_time >= '2026-01-01' AND create_time < '2026-01-02';

2)where 里对索引字段做运算

-- 失效
SELECT * FROM users WHERE age + 1 = 20;

-- 有效
SELECT * FROM users WHERE age = 19;

3)like 以 % 开头

-- 失效
SELECT * FROM users WHERE name LIKE '%张';

-- 有效
SELECT * FROM users WHERE name LIKE '张%';

4)隐式类型转换

-- phone是字符串
-- 失效(数字转字符串)
SELECT * FROM users WHERE phone = 13800138000;

-- 有效
SELECT * FROM users WHERE phone = '13800138000';

5. EXPLAIN 查看执行计划

EXPLAIN SELECT * FROM users WHERE name = '张三';

重点看:

  • type: 访问类型,至少要 range
  • key: 实际用到的索引
  • rows: 扫描行数
  • Extra: 额外信息

6. 索引不要建太多

  • 每个索引都占空间
  • 插入更新时要维护索引
  • 不是越多越好
  • 一张表索引建议不超过5个

7. 覆盖索引

避免回表:

-- 索引包含查询的所有字段
SELECT name, age FROM users WHERE name = '张三';

-- 索引 (name, age) 就是覆盖索引
-- 不用回表查主键

Extra 显示 Using index 就是覆盖索引。

8. 慢查询定位

-- 开启慢查询
slow_query_log = 1
long_query_time = 1  -- 超过1秒记录

-- 查看慢查询日志
-- 找到慢SQL,加索引优化

9. 常见优化场景

1)分页查询优化

-- 慢(深分页)
SELECT * FROM orders LIMIT 100000, 10;

-- 快(子查询)
SELECT * FROM orders 
WHERE id >= (SELECT id FROM orders LIMIT 100000, 1)
LIMIT 10;

2)JOIN 字段建索引

  • JOIN 的两边都要建索引
  • 类型要一致

3)ORDER BY 用索引

  • 排序字段建索引
  • 避免 filesort

10. 索引维护

查看索引:

SHOW INDEX FROM users;

删除多余索引:

DROP INDEX idx_name ON users;

分析表:

ANALYZE TABLE users;

总结

MySQL 索引优化要点:

  • 区分度高的字段建索引
  • 联合索引遵循最左前缀
  • 避免索引失效(函数、运算、like%)
  • 用 EXPLAIN 分析执行计划
  • 索引不要建太多
  • 覆盖索引避免回表
  • 慢查询日志找问题SQL
  • 深分页要优化
  • JOIN 两边都建索引
  • 定期维护索引

索引优化是MySQL性能优化的核心,一定要多实践。


emer 发布于  2026-10-9 07:32 

MySQL大表分页越查越慢,优化方案

数据量小的时候分页很快,一旦表数据上百万条,翻到后面的页就慢得要死?这是MySQL分页最常见的问题,下面说几个实用的优化方案。

1. 先看为什么会慢

SELECT * FROM 表名 ORDER BY id LIMIT 100000, 10;

这个SQL的意思是:扫描前100010条,丢掉前100000条,返回后10条。

数据量越大,扫描的行数越多,就越慢。这就是为什么越往后翻越慢。

2. 方案一:子查询优化(最常用)

SELECT * FROM 表名 
WHERE id >= (SELECT id FROM 表名 ORDER BY id LIMIT 100000, 1)
LIMIT 10;

原理:

  • 子查询先通过索引找到第100001条的id
  • 然后直接从这个id开始往后查
  • 不需要扫描前10万条

速度对比:

  • 原SQL:扫描10万+行,耗时几秒
  • 优化后:走索引,几毫秒就出来

3. 方案二:游标分页(推荐用在后台)

-- 第一页
SELECT * FROM 表名 ORDER BY id LIMIT 10;

-- 第二页(记住上一页最后一条的id)
SELECT * FROM 表名 WHERE id > 100 ORDER BY id LIMIT 10;

-- 第三页
SELECT * FROM 表名 WHERE id > 110 ORDER BY id LIMIT 10;

优点:

  • 永远都是走索引,不管翻到第几页都一样快
  • 适合无限滚动加载

缺点:

  • 不能直接跳到第100页
  • 不适合传统的"上一页/下一页"翻页

4. 方案三:延迟关联

SELECT t.* FROM 表名 t
INNER JOIN (
    SELECT id FROM 表名 ORDER BY id LIMIT 100000, 10
) tmp ON t.id = tmp.id;

原理:

  • 先通过索引查出需要的10个id
  • 再用这10个id去关联原表查完整数据
  • 避免回表太多行

5. 方案四:限制最大翻页深度

如果不是必须让用户翻到很后面,可以直接限制:

-- 最多只允许翻100页(1000条数据)
IF 页码 > 100 THEN
    -- 提示用户"请搜索更精确的条件"
END IF;

一般用户也不会翻到很后面的页,限制一下性能就好很多。

6. 方案五:用ES做搜索

如果是文章、商品之类的内容,直接用Elasticsearch做全文搜索和分页,MySQL只存原始数据。

ES天生适合做大数据量的分页搜索,MySQL不是干这个的。

常见坑

坑1:ORDER BY 字段没加索引
ORDER BY 的字段一定要加索引,不然不管怎么优化都是全表扫描。

坑2:OFFSET 太大了
OFFSET 100万,不管怎么优化都慢,直接用游标分页。

*坑3:SELECT 不要用**
只查需要的字段,减少回表数据量。


遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-9 07:27 

MySQL 索引优化技巧

MySQL 索引优化技巧

索引是数据库性能的关键,用对了查询快很多。

1. 索引类型

  • 主键索引: 主键自带,唯一且非空
  • 普通索引: 最基本的索引
  • 唯一索引: 字段值不能重复
  • 联合索引: 多个字段组成的索引
  • 全文索引: 全文搜索用

2. 创建索引

-- 普通索引
CREATE INDEX idx_name ON users(name);

-- 唯一索引
CREATE UNIQUE INDEX idx_phone ON users(phone);

-- 联合索引
CREATE INDEX idx_name_age ON users(name, age);

3. 索引失效情况

1)不要在索引字段上做运算:

-- 失效
SELECT * FROM users WHERE age + 1 = 20;

-- 生效
SELECT * FROM users WHERE age = 19;

2)like 左模糊:

-- 失效
SELECT * FROM users WHERE name LIKE '%张';

-- 生效
SELECT * FROM users WHERE name LIKE '张%';

3)联合索引不满足最左前缀:

-- 索引:(name, age)
-- 生效(从name开始)
SELECT * FROM users WHERE name = '张三' AND age = 20;

-- 失效(跳过name直接查age)
SELECT * FROM users WHERE age = 20;

4)用 or 连接:

  • or 两边有一个没索引就全表扫描

4. 优化原则

  • 频繁查询的字段加索引
  • 区分度高的字段适合做索引
  • 不要加太多索引(影响写入性能)
  • 联合索引注意最左前缀
  • 尽量扩展索引,不要新建索引

5. 查看索引

-- 查看表的索引
SHOW INDEX FROM users;

6. 常见问题

  • 索引加了还是慢?检查是不是失效了
  • 查询还是慢?用 EXPLAIN 分析
  • 写入慢?索引太多了

总结

MySQL 索引优化要点:

  • 常用查询字段加索引
  • 注意索引失效情况
  • 联合索引最左前缀
  • 不要加太多索引
  • EXPLAIN 分析执行计划

数据库性能优化,索引是第一步。


emer 发布于  2026-10-8 07:27 

Redis 缓存穿透解决

Redis 缓存穿透解决

缓存穿透是指查询不存在的数据,请求都打到数据库,可能被攻击。

1. 什么是缓存穿透

  • 缓存里没有这个数据
  • 数据库里也没有
  • 每次请求都查数据库
  • 有人恶意攻击数据库就挂了

2. 和其他问题的区别

  • 穿透: 查不存在的数据
  • 击穿: 热点key过期
  • 雪崩: 大量key同时过期

3. 解决方案

方案一:缓存空值

function getUser($id) {
    global $redis, $db;

    $key = "user:".$id;
    $user = $redis->get($key);

    if ($user !== false) {
        return json_decode($user, true);
    }

    // 查数据库
    $user = $db->query("SELECT * FROM users WHERE id=$id");

    if ($user) {
        // 存正常数据
        $redis->setex($key, 3600, json_encode($user));
    } else {
        // 存空值,短时间过期
        $redis->setex($key, 60, "null");
    }

    return $user;
}

方案二:布隆过滤器

  • 所有存在的数据存在布隆过滤器里
  • 查询前先过布隆过滤器
  • 不存在直接返回,不查数据库

方案三:接口校验

  • 前端做参数校验
  • 比如id必须大于0
  • 不合法的直接拒绝

4. 代码示例

// 简单防穿透
if ($id <= 0) {
    return null;
}

// 先查缓存
$user = $redis->get("user:".$id);

if ($user === "null") {
    return null;
}

if ($user) {
    return json_decode($user, true);
}

// 查数据库
$user = $db->query("SELECT * FROM users WHERE id=$id");

// 存缓存
if ($user) {
    $redis->setex("user:".$id, 3600, json_encode($user));
} else {
    $redis->setex("user:".$id, 60, "null");
}

return $user;

5. 最佳实践

  • 空值缓存,短时间过期
  • 重要接口加布隆过滤器
  • 前端做参数校验
  • 数据库做限流保护

总结

缓存穿透解决要点:

  • 查不到也存空值
  • 空值过期时间短
  • 用布隆过滤器
  • 做好接口校验

高并发系统,缓存穿透一定要防。


emer 发布于  2026-10-8 07:23 

MySQL 慢查询优化实战

MySQL 慢查询优化实战

数据库慢查询是性能瓶颈的常见原因,学会优化很重要。

1. 开启慢查询日志

-- 查看是否开启
SHOW VARIABLES LIKE 'slow_query_log';

-- 开启慢查询
SET GLOBAL slow_query_log = ON;

-- 设置慢查询时间(秒)
SET GLOBAL long_query_time = 1;

2. 分析慢查询

用 EXPLAIN 分析:

EXPLAIN SELECT * FROM users WHERE name = '张三';

重点看:

  • type:访问类型,ALL 是全表扫描,要优化
  • key:用了哪个索引
  • rows:扫描行数

3. 常见优化方法

1)加索引:

-- 加索引
ALTER TABLE users ADD INDEX idx_name(name);

*2)避免 SELECT :**

-- 不好
SELECT * FROM users;

-- 好
SELECT id, name FROM users;

3)避免在索引字段上做运算:

-- 不好
SELECT * FROM users WHERE YEAR(create_time) = 2026;

-- 好
SELECT * FROM users WHERE create_time >= '2026-01-01' AND create_time < '2027-01-01';

4)避免 LIKE 左模糊:

-- 不好(用不到索引)
SELECT * FROM users WHERE name LIKE '%张%';

-- 好
SELECT * FROM users WHERE name LIKE '张%';

4. 分页优化

-- 不好(大偏移量慢)
SELECT * FROM users LIMIT 100000, 10;

-- 好(用子查询)
SELECT * FROM users WHERE id >= (
    SELECT id FROM users LIMIT 100000, 1
) LIMIT 10;

5. 常见慢查询原因

  • 没加索引
  • 索引失效
  • 关联查询太多
  • 数据量太大
  • 大表分页

总结

MySQL 慢查询优化要点:

  • 开启慢查询日志
  • 用 EXPLAIN 分析
  • 合理加索引
  • 避免全表扫描
  • 优化分页查询

数据库优化是个细活,多分析多测试。


emer 发布于  2026-10-8 07:19 

Redis 缓存雪崩解决

Redis 缓存雪崩解决

缓存雪崩是指大量缓存同时失效,请求全部打到数据库,数据库直接挂掉。

1. 什么是缓存雪崩

原理:

  • 大量 key 同时过期
  • 请求都查数据库
  • 数据库压力瞬间暴增
  • 数据库扛不住挂掉

和缓存穿透的区别:

  • 穿透:查询不存在的数据
  • 雪崩:大量缓存同时失效

2. 产生原因

1)缓存大面积过期:

  • 大量数据设置了相同过期时间
  • 到时间一起失效

2)Redis 宕机:

  • Redis 服务挂了
  • 所有请求都走数据库

3. 解决方案

方案一:过期时间加随机值

// 不要设置相同过期时间
$expire = 3600 + rand(0, 600);
$redis->setex("key", $expire, $value);

加随机值,让 key 分散过期,不会同时失效。

方案二:缓存永不过期

  • 热点数据不设置过期时间
  • 更新数据时主动更新缓存
  • 异步更新缓存

方案三:Redis 高可用

  • 搭建 Redis 集群
  • 主从复制
  • 哨兵模式
  • 一台挂了还有其他的

方案四:熔断降级

  • 请求量太大时直接返回默认值
  • 不让请求打到数据库
  • 保护数据库

4. 代码示例

function getData($id) {
    global $redis, $db;

    $key = "data:".$id;
    $data = $redis->get($key);

    if ($data) {
        return json_decode($data, true);
    }

    // 查数据库
    $data = $db->query("SELECT * FROM table WHERE id=$id");

    // 缓存,过期时间加随机值
    $expire = 3600 + rand(0, 600);
    $redis->setex($key, $expire, json_encode($data));

    return $data;
}

5. 最佳实践

  • 过期时间加随机值
  • 搭建 Redis 集群
  • 热点数据永不过期
  • 加熔断降级机制
  • 数据库做限流

总结

缓存雪崩解决要点:

  • 过期时间加随机值,不要同时失效
  • Redis 做主从高可用
  • 热点数据不设置过期
  • 做好降级兜底

做高并发系统,缓存雪崩一定要防。


emer 发布于  2026-10-8 07:16 

MySQL索引失效常见场景,避免踩坑

明明建了索引,查询还是很慢?十有八九是索引失效了。MySQL 索引失效是开发中最常见的坑,下面整理了最容易导致索引失效的场景,写SQL的时候一定要避开。

1. 对索引字段做函数操作

-- 索引失效
SELECT * FROM user WHERE DATE(create_time) = '2026-10-01';

-- 正确写法,走索引
SELECT * FROM user WHERE create_time >= '2026-10-01' AND create_time < '2026-10-02';

对索引字段用了 DATE()、YEAR()、SUBSTRING() 等函数,索引就失效了。

2. 隐式类型转换

-- 假设 phone 是 varchar 类型
-- 索引失效(数字和字符串比较,MySQL自动转换)
SELECT * FROM user WHERE phone = 13800138000;

-- 正确写法,走索引
SELECT * FROM user WHERE phone = '13800138000';

字符串类型的字段,查询条件一定要加引号。

3. 联合索引不满足最左匹配原则

-- 联合索引:(a, b, c)
-- 走索引
SELECT * FROM t WHERE a = 1 AND b = 2;

-- 不走索引(跳过了a)
SELECT * FROM t WHERE b = 2 AND c = 3;

-- 走部分索引(a走,b和c不走)
SELECT * FROM t WHERE a = 1 AND c = 3;

联合索引必须从最左边的列开始用,不能跳过。

4. 使用了 != 或 <>

-- 索引大概率失效
SELECT * FROM user WHERE status != 1;

-- 可以改成范围查询
SELECT * FROM user WHERE status < 1 OR status > 1;

不等于操作通常会导致全表扫描,因为要返回的结果太多了。

5. LIKE 以 % 开头

-- 索引失效
SELECT * FROM user WHERE name LIKE '%张';

-- 走索引
SELECT * FROM user WHERE name LIKE '张%';

模糊查询左边带 %,索引就用不上了。

6. OR 连接的条件有一个没索引

-- name 有索引,age 没索引 → 整个SQL不走索引
SELECT * FROM user WHERE name = '张三' OR age = 20;

OR 两边只要有一个字段没索引,整个查询就不走索引。

7. 用 explain 验证索引是否生效

EXPLAIN SELECT * FROM user WHERE name = '张三';

看 key 列:

  • 显示索引名 → 走索引了
  • 显示 NULL → 没走索引

看 type 列:

  • ref、eq_ref → 正常
  • ALL → 全表扫描,索引失效了

常见坑总结

  1. 不要在索引字段上做运算:WHERE age + 1 = 20 → 改成 WHERE age = 19
  2. 不要用 NOT IN / NOT EXISTS:大数据量下很慢
  3. **不要用 SELECT ***:只查需要的字段,覆盖索引更快
  4. 数据量小的表不要建索引:全表扫描可能比索引还快

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 07:14 

MySQL 索引优化技巧

MySQL 索引优化技巧

索引是数据库性能的关键,建对了快10倍,建错了反而更慢。这篇讲实战技巧。

1. 索引类型

普通索引: 最基本的索引,没有限制
唯一索引: 列值不能重复
主键索引: 特殊的唯一索引,一张表只能一个
联合索引: 多个字段组成的索引

2. 建索引原则

1)where 条件经常用的字段建索引

-- 常用查询
SELECT * FROM users WHERE email = 'xxx@qq.com';

-- 给 email 建索引
CREATE INDEX idx_email ON users(email);

2)联合索引最左前缀原则

-- 建联合索引 (name, age)
CREATE INDEX idx_name_age ON users(name, age);

-- 能用索引
WHERE name = '张三'
WHERE name = '张三' AND age = 25

-- 不能用索引(跳过了name)
WHERE age = 25

3)避免建太多索引

  • 索引不是越多越好
  • 每个索引都占空间,影响写入速度
  • 一张表索引不要超过5个

3. 索引失效场景

1)不要在索引字段上做运算

-- 坏(索引失效)
SELECT * FROM users WHERE age + 1 = 26;

-- 好
SELECT * FROM users WHERE age = 25;

2)不要用函数

-- 坏
SELECT * FROM users WHERE DATE(create_time) = '2026-10-01';

-- 好
SELECT * FROM users WHERE create_time >= '2026-10-01' AND create_time < '2026-10-02';

3)like 前导模糊

-- 坏(用不到索引)
SELECT * FROM users WHERE name LIKE '%张%';

-- 好
SELECT * FROM users WHERE name LIKE '张%';

**4)避免 select ***

-- 坏(查所有字段,可能回表)
SELECT * FROM users WHERE name = '张三';

-- 好(覆盖索引)
SELECT id, name FROM users WHERE name = '张三';

4. 查看索引使用

-- 查看表索引
SHOW INDEX FROM users;

-- 用 explain 分析
EXPLAIN SELECT * FROM users WHERE name = '张三';

重点看:

  • type:const/ref 好,ALL 全表扫描坏
  • key:实际用到的索引
  • rows:扫描行数

5. 总结

索引优化记住:

  • where 字段建索引
  • 联合索引最左前缀
  • 别在索引字段上做运算
  • 避免前导模糊查询
  • 用 explain 检查

索引是数据库优化的第一步,做好了性能提升很大。


emer 发布于  2026-10-8 07:08 

Redis 缓存穿透解决

Redis 缓存穿透解决

缓存穿透是常见问题,大量请求查询不存在的数据,直接打到数据库。这篇讲怎么解决。

1. 什么是缓存穿透

用户查询一个不存在的数据,Redis 里没有,每次都去查数据库。恶意攻击的话,数据库扛不住。

2. 解决方案一:缓存空值

查询数据库也没结果,就缓存一个空值,设置短过期时间。

function getUser($id) {
    $key = "user:$id";

    // 先查缓存
    $user = $redis->get($key);
    if ($user !== false) {
        return json_decode($user, true);
    }

    // 查数据库
    $user = $db->query("SELECT * FROM users WHERE id = $id");

    if ($user) {
        // 缓存有数据的结果
        $redis->setex($key, 3600, json_encode($user));
    } else {
        // 缓存空值,短时间过期
        $redis->setex($key, 60, '');
    }

    return $user;
}

优点: 简单粗暴
缺点: 浪费内存,可能导致不一致

3. 解决方案二:布隆过滤器

在缓存前面加一层布隆过滤器,不存在的 key 直接拦截。

// 使用 RedisBloom 模块
// 添加到布隆过滤器
$redis->rawCommand("BF.ADD", "user_filter", "1001");

// 查询是否存在
$exists = $redis->rawCommand("BF.EXISTS", "user_filter", "1001");
if (!$exists) {
    return null; // 直接返回,不查数据库
}

原理: 用二进制数组和多个哈希函数,判断一个 key 一定不存在或者可能存在。

优点: 内存占用小,效率高
缺点: 有一定误判率,不支持删除

4. 两种方案对比

方案 优点 缺点 适用场景
缓存空值 简单 浪费内存 数据量小
布隆过滤器 高效省内存 有误差 数据量大

5. 其他注意事项

  • 接口增加参数校验,不合法的 id 直接拒绝
  • 增加接口访问限流,防止恶意攻击
  • 黑名单机制,频繁查不存在的 IP 封掉

总结

缓存穿透解决思路:

  1. 缓存空值: 简单,适合小数据量
  2. 布隆过滤器: 高效,适合大数据量
  3. 加限流和校验: 从入口防护

生产环境推荐布隆过滤器 + 缓存空值组合使用。


emer 发布于  2026-10-8 07:06 

MySQL 索引优化技巧

MySQL 索引优化技巧

索引是数据库性能的关键,加对了查询快十倍,加错了反而更慢。

1. 索引类型

主键索引:主键自带索引,一张表只能一个。

普通索引:最基本的索引,加速查询。

CREATE INDEX idx_name ON user(name);

唯一索引:字段值不能重复。

CREATE UNIQUE INDEX idx_email ON user(email);

联合索引:多个字段组合的索引。

CREATE INDEX idx_name_age ON user(name, age);

2. 索引失效的情况

1)like 以 % 开头

-- 索引失效
SELECT * FROM user WHERE name LIKE '%张';

-- 索引生效
SELECT * FROM user WHERE name LIKE '张%';

2)对字段做函数运算

-- 索引失效
SELECT * FROM user WHERE LEFT(name,1) = '张';

-- 索引生效
SELECT * FROM user WHERE name LIKE '张%';

3)联合索引不满足最左前缀

联合索引 (name, age):

-- 生效
SELECT * FROM user WHERE name = '张三';

-- 生效
SELECT * FROM user WHERE name = '张三' AND age = 25;

-- 失效
SELECT * FROM user WHERE age = 25;

4)隐式类型转换

-- phone 是 varchar
-- 索引失效
SELECT * FROM user WHERE phone = 13800001111;

-- 索引生效
SELECT * FROM user WHERE phone = '13800001111';

3. 查看索引使用情况

-- 查看执行计划
EXPLAIN SELECT * FROM user WHERE name = '张三';

-- 重点看 type 字段:
-- system > const > eq_ref > ref > range > index > ALL
-- 最差是 ALL(全表扫描)

4. 索引优化原则

  • 为高频查询加索引:不要乱加
  • 区分度高的字段优先:性别这种区分度低的不加
  • 联合索引把最常用的放左边
  • 索引不是越多越好:增删改会变慢
  • 避免索引冗余:(a,b) 包含了 (a)

5. 慢查询排查

-- 开启慢查询
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 2;

-- 查看慢查询日志
SHOW VARIABLES LIKE 'slow_query_log_file';

总结

MySQL 索引优化核心:

  • 联合索引满足最左前缀
  • like 不要以 % 开头
  • 不要对字段做函数运算
  • 用 EXPLAIN 看执行计划
  • 慢查询找出来再优化

搞懂这些,数据库性能提升一大截。


emer 发布于  2026-10-4 11:12 

MySQL 慢查询优化技巧

MySQL 慢查询优化实战技巧

数据库慢查询是生产环境最常见的性能问题之一。掌握慢查询优化技巧,能让系统性能提升数倍。

1. 开启慢查询日志

先开启慢查询日志,找出哪些 SQL 慢:

slow_query_log = 1
long_query_time = 1
slow_query_log_file = /var/log/mysql/slow.log

超过1秒的查询就会被记录。

2. 用 Explain 分析

慢 SQL 拿到后,用 EXPLAIN 分析执行计划:

EXPLAIN SELECT * FROM users WHERE name = '张三';

重点看:

  • type:访问类型,ALL 是全表扫描,要优化
  • key:实际用到的索引
  • rows:扫描行数
  • Extra:额外信息,Using filesort、Using temporary 要警惕

3. 加索引

最直接的优化就是加索引:

ALTER TABLE users ADD INDEX idx_name(name);

加索引原则:

  • WHERE、JOIN、ORDER BY 字段优先
  • 区分度高的字段效果好
  • 联合索引遵循最左前缀

4. 避免 SELECT *

只查需要的字段:

-- 不好
SELECT * FROM users WHERE id = 1;

-- 好
SELECT id, name FROM users WHERE id = 1;

5. 深分页优化

LIMIT 100000, 10 很慢,因为要扫描10万行:

-- 不好
SELECT * FROM users LIMIT 100000, 10;

-- 好,用子查询
SELECT * FROM users 
WHERE id >= (SELECT id FROM users LIMIT 100000, 1)
LIMIT 10;

6. 大表拆小

单表数据量超过千万行,性能会下降。可以:

  • 按时间分表
  • 按用户ID分库
  • 归档历史数据

7. 总结

慢查询优化的核心思路:先找慢 SQL,再分析执行计划,然后加索引或改写 SQL。优化完记得对比前后效果,确保真的变快了。


emer 发布于  2026-10-4 10:41 

MySQL 索引原理详解

MySQL 索引为什么能提速

索引是数据库性能优化的核心,搞懂原理才能用对。

1. 为什么慢查询慢

没有索引,MySQL 就得全表扫描,一行一行找。100万行数据就得扫100万次,当然慢。

有了索引,就像书有目录,直接定位到位置,查几百万行也很快。

2. B+ 树索引

MySQL InnoDB 默认用 B+ 树做索引。

  • 树的高度低,一般3-4层
  • 一次查询只需3-4次磁盘IO
  • 叶子节点存数据,非叶子节点存索引

3. 聚簇索引 vs 二级索引

聚簇索引:数据和索引放在一起,按主键组织。一张表只有一个聚簇索引。

二级索引:索引和数据分开,叶子节点存主键值。查二级索引需要回表查主键。

4. 覆盖索引

如果查询的字段都在索引里,就不用回表了,叫覆盖索引。

-- name有索引
SELECT name FROM users WHERE name = '张三';
-- 覆盖索引,不用回表

5. 最左前缀

联合索引 (a, b, c),必须从最左列开始用:

  • WHERE a = 1 能用
  • WHERE a = 1 AND b = 2 能用
  • WHERE b = 2 用不了

6. 索引失效场景

  • 索引字段做函数运算
  • 隐式类型转换
  • like 以 % 开头
  • 联合索引不满足最左前缀

总结

索引是空间换时间。不是越多越好,太多索引会影响写入性能,占用额外存储空间。


emer 发布于  2026-10-4 10:36 
'); })();