欢迎光临
我们一直在努力

SQL优化必看:4层架构破解性能瓶颈

SQL 优化

1.1 概念

在应用的开发过程中,由于初期数据量小,开发人员写 SQL 语句时更重视功能上的实现,但是当应用系统正式上线后,随着生产数据量的急剧增长,很多 SQL 语句开始逐渐显露出性能问题,对生产的影响也越来越大,此时这些有问题的 SQL 语句就成为整个系统性能的瓶颈,因此我们必须要对它们进行优化。

1.1.1 MySQL 的优化方式

  • 从设计上优化:库表结构、字段类型、范式 / 反范式选择
  • 从查询上优化:规范 SQL 写法,规避索引失效场景
  • 从索引上优化:合理创建索引,遵循联合索引最左前缀原则
  • 从存储上优化:引擎选型、分区、分库分表、读写分离、缓存架构

1.2 SQL 优化小技巧

1.2.1 原则

减小查询的数据量、提升 SQL 的索引命中率,尽量让数据库扫描更少的数据行,优先走索引而不是全表扫描。

1.2.2 查询时尽量不要使用*

日常开发图方便会写select *,但生产环境不推荐。 原因:

  • 分析成本变高:增加 SQL 解析器解析成本,需要去元数据中查找全部字段;
  • 网络开销变大:返回大量不需要的字段,数据包体积更大;
  • 内存占用变高:结果集更大,数据库、应用服务内存开销上升;
  • 维护性变差:表新增、删除字段时,SQL 返回结果会发生非预期变化。
  • ✅ 推荐写法:明确写出需要查询的字段

    — 不推荐
    select * from student where id=1;
    — 推荐
    select id,name,age from student where id=1;

    1.2.3 连表查询时尽量不要关联太多表

    多表 JOIN 时,MySQL 需要生成笛卡尔积、选择驱动表,表越多执行计划越复杂,消耗资源越高。

    • 交互业务(前端用户请求):关联表控制在5 张以内,阿里开发规范建议控制在 3 张以内;
    • 后台统计报表业务:业务复杂,可适当放宽,《高性能 MySQL》建议尽量不超过 16~18 张。

    1.2.4 多表查询一定要以小驱大

    小表驱动大表:优先查询数据量更小的表,拿小表的结果集合,去大表匹配检索。

    小表放前面,大表放后面;性能关键点:被驱动表的关联字段必须建立索引。 原理:外层循环次数少,内层通过索引快速定位,大幅减少扫描行数。

    1.2.5 不要使用 like 左模糊和全模糊查询

    %xxx、%xxx% 会导致索引失效,触发全表扫描,只有后缀匹配xxx%可以正常走索引。

    — 索引失效,禁止
    select * from student where name like '%张三';
    select * from student where name like '%张三%';
    — 可以走索引
    select * from student where name like '张三%';

    解决方案:

  • 少量场景:建立全文索引;
  • 大数据量搜索:引入 Elasticsearch、Solr 搜索引擎。
  • 1.2.6 查询时尽量不要对字段做空值判断

    select * from xxx where xxx is null;
    select * from xxx where xxx is not null;

    is null /is not null 很容易造成索引失效,走全表扫描。 ✅ 设计规范:字段定义为NOT NULL,用默认值代替 NULL,例如空字符串''、数字 0。

    — 改造后,可以正常走索引
    select * from xxx where xxx = '';

    1.2.7 不要在条件=前对字段做任何运算、函数操作

    — 索引失效示例
    select * from student where id * 2 = 8;
    select * from student where trim(name) = "张三";
    select * from student where date(create_time) = '2026-10-30';

    原理:MySQL 优化器无法识别运算、函数之后的值,不会使用该字段索引,触发全表扫描。 ✅ 改造思路:把运算转移到常量一侧

    select * from student where id = 8 / 2;
    select * from student where name = trim("张三");

    1.2.8 !=、not in、not like、or要慎用

    这类条件很容易造成索引失效,需要改写 SQL。

    — or示例,可能不走索引
    select name from student where id=1 or id=2;
    — 改写,union all可以分别走索引,性能更高
    select name from student where id=1
    union all
    select name from student where id=2;

    区分:union会去重排序,union all直接拼接结果,不需要排序,优先使用 union all。

    1.2.9 避免频繁创建、销毁临时表

    临时表存放在内存,查询速度快,适合复用结果集; 但是不要频繁新建销毁临时表,频繁创建释放会给 MySQL 带来大量开销,只适合多次复用的统计场景。

    1.2.10 从业务设计层面减少一次性返回大量数据

    禁止一次性查询全表数据,在应用层做筛选。 一次性返回海量数据会造成:网络阻塞、服务 OOM、数据库 IO 飙升。 ✅ 优化方案:分页查询、分批拉取数据。

    1.2.11 尽量避免深分页

    — 深分页,低效
    select xx,xx,xx from yyy limit 100000,10;

    执行逻辑:先读取 100010 行数据,丢弃前 10 万条,只返回最后 10 条,扫描大量无效数据。

    ✅ 两种优化方案

    1.有序连续主键(自增 id):主键筛选分页(书签分页)

    select xx,xx,xx from yyy where id >= 100000 limit 10;

    2.无序搜索分页:业务层面限制,不提供深分页入口(搜索引擎常用方案,用户一般只看前几十页)

    1.2.12 SQL 务必写完整,不要使用隐式简写

    隐式写法底层需要额外语法转换,多一层开销,规范项目建议写完整 SQL。

    — 简写(不推荐)
    select name "姓名" from student;
    select * from t1,t2 where t1.id=t2.tid;

    — 完整规范写法(推荐)
    select name as "姓名" from student;
    select * from t1 inner join t2 on t1.id = t2.tid;

    1.2.13 明确只返回一条数据,加上 limit 1

    — 优化前,扫描全部匹配行
    select * from student where name = "李四";
    — 优化后,找到第一条匹配数据立刻停止扫描
    select * from student where name = "李四" limit 1;

    1.2.14 批量操作合并 SQL,减少循环单次提交

    ❌ 错误写法:循环里面多次执行 insert,频繁网络交互

    for (Student stu : list) {
    insert into student values(?,?,?);
    }

    ✅ 推荐批量插入,一条 SQL 完成

    insert into student(id,name,age) values (1,'a',18),(2,'b',19),(3,'c',20);

    批量 SQL 大幅减少网络往返次数,提升吞吐。

    1.3 查看 SQL 执行效率

    优化第一步:找到慢 SQL,MySQL 提供多种工具定位低效语句。

    1.3.1 show status 查看数据库统计信息

    作用:查看当前会话 / 全局的增删改查统计,判断数据库负载类型。

    — 当前会话统计
    show session status like 'Com_______';
    — 全局统计,MySQL启动后累计
    show global status like 'Com_______';
    — InnoDB行操作统计
    show status like 'Innodb_rows_%';

    字段说明:

    • Com_select:查询次数
    • Com_insert / Com_update / Com_delete:写入次数
    • Innodb_rows_read:InnoDB 读取行数,数值过大说明大量扫描

    1.3.2 定位低效率 SQL 两种方式

    1.慢查询日志 slow_query_log:自动记录超过阈值的慢 SQL

    — 查询慢日志配置
    show variables like '%slow_query_log%';
    — 开启慢查询日志(会话级别重启失效,生产建议配置文件持久化)
    set global slow_query_log=1;
    — 查询慢查询阈值,单位秒
    show variables like 'long_query_time%';
    — 设置阈值:执行超过4秒就记录
    set global long_query_time=4;

    2.show processlist:实时查看当前正在运行的线程、SQL、锁状态

    show processlist;

    字段解析:

    字段说明
    Id 会话 ID,对应 connection_id ()
    User 执行用户
    Host 客户端 IP + 端口
    db 当前操作的数据库
    Command 命令类型 sleep/query
    Time 持续执行时间,秒
    State SQL 执行状态(copying to tmp table、sorting result 等,重点观察)
    Info 正在执行的 SQL 语句

    1.3.3 explain 执行计划(重点)

    作用:拿到慢 SQL 后,使用explain分析 MySQL 的执行计划,判断索引是否生效、扫描行数。

    explain select * from student where sno = 's003';

    核心字段解读:

    • type:访问类型,性能从优到劣:system > const > eq_ref > ref > range > index > ALL,出现 index、ALL 代表全索引扫描 / 全表扫描,需要优化;
    • key:实际使用到的索引,NULL = 没有走索引;
    • rows:预估扫描行数,数值越大性能越差;
    • filtered:满足条件的数据占扫描行比例,值越低代表过滤效率差;
    • Extra:额外信息,常见Using filesort(文件排序,无索引排序)、Using temporary(使用临时表,需要优化)。

    1.4 show profile(MySQL8.0 已废弃,仅了解)

    ⚠️ 该命令在新版 MySQL 中已经失效,推荐使用 performance_schema 替代,老版本 5.7 可以使用。 作用:分析 SQL 各个阶段耗时,定位时间消耗在哪一步。

    — 查看是否支持
    select @@have_profiling;
    — 开启
    set profiling=1;

    — 执行多条SQL
    select * from student where year(now())-year(birth)<20;
    select count(*) from student;

    — 查看所有SQL的profile编号
    show profiles;
    — 查看指定SQL详细耗时
    show profile for query 3;
    — 查看CPU资源消耗
    show profile cpu for query 3;

    1.5 trace 分析优化器执行计划

    作用:查看 MySQL 优化器的决策过程,搞清楚为什么优化器没有选择预期索引、为什么选择这个执行方案。

    — 开启trace,json格式
    SET optimizer_trace="enabled=on",end_markers_in_json=on;
    set optimizer_trace_max_mem_size=1000000;

    — 执行目标SQL
    select * from student where year(now())-year(birth)<16;

    — 读取trace结果
    select * from information_schema.optimizer_trace\\G;

    输出是 JSON 格式,包含优化、成本计算、索引选择逻辑,适合深度调优。

    1.6 使用索引优化

    索引是 MySQL 优化最核心、最高频的手段,大部分慢 SQL 问题都可以通过合理索引解决。

    1.6.1 索引建立原则

  • 选择区分度高的字段建索引:id、手机号、订单号;性别、状态这类基数很小的字段索引收益很低;
  • 优先短字段索引,int、varchar 短字段;大文本不适合普通索引,使用全文索引;
  • 联合索引遵循最左前缀原则;
  • 频繁更新的字段谨慎建索引,更新索引成本高;
  • where、order by、group by常用字段优先建立索引;
  • 在线业务单表索引不要过多,建议控制在 5 个以内;只读统计库可以适当增加索引;
  • 唯一索引谨慎创建,唯一约束会带来锁开销。
  • 1.7 架构层面优化

    SQL 和索引优化到瓶颈之后,就需要架构改造:

  • 业务拆分:全文模糊搜索剥离 MySQL,交给 ES;
  • 冷热数据分离、引入 NoSQL:热点数据、粉丝关系、计数类场景使用 Redis;
  • 增加缓存层:MySQL 前置 Redis,缓存高频查询结果,减少数据库查询压力;
  • 页面静态化:动态页面静态化,直接返回静态资源,不访问数据库;
  • 读写分离:一主多从,写主库,读从库,分摊查询压力;
  • 分库分表:单表数据量超过 2000w,水平拆分,解决单表容量、性能上限。
  • 赞(0)
    未经允许不得转载:171主机测评 » SQL优化必看:4层架构破解性能瓶颈
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址