Jw
首页
技术
英语
首页
技术
英语
  • MySQL

    • MySQL 基础
    • 环境变量配置(PATH)
    • SQL 语法与分类
    • MySQL 数据类型
    • SQL - DDL 数据定义语言
    • SQL - DML 数据操作语言
    • SQL - DQL 数据查询语言
    • SQL - DCL 数据控制语言
    • MySQL 函数
    • SQL 约束
    • SQL 外键约束
    • 多表关系
    • 多表查询(连接查询)
    • 隐式内连接 vs 显式内连接
    • 子查询
    • 多表查询案例
    • 事务
    • 并发事务问题与隔离级别
    • MySQL 体系结构与存储引擎
    • 索引概述
    • 索引结构
    • 索引分类
    • 索引语法
    • SQL 性能分析
    • 索引使用
    • 索引设计原则
    • SQL 优化 - 插入数据与主键优化
    • SQL 优化 - order by 与 group by
    • SQL 优化 - limit / count / update
    • 视图
    • 存储过程
    • 存储过程 - 变量
    • 存储过程 - 流程控制、游标与条件处理
    • 存储函数
    • 触发器
    • 锁概述与全局锁
    • 表级锁
    • 行级锁
    • InnoDB 逻辑存储结构
    • InnoDB 架构
    • 事务原理(redo / undo log)
    • MVCC
    • MySQL 系统数据库
    • MySQL 常用工具
    • 数据备份与导入导出
    • 错误日志与二进制日志
    • 查询日志与慢查询日志
    • 主从复制 - 概述与原理
    • 主从复制 - 搭建
    • Mycat 配置
    • Mycat 垂直拆分与水平拆分
    • Mycat 分片规则
    • Mycat 管理与监控
    • Mycat 读写分离 - 一主一从
    • Mycat 读写分离 - 双主双从

SQL 优化 - limit / count / update

三个高频单点优化:深分页用覆盖索引 + 子查询,计数用 count(*),更新必须走索引以免行锁升级为表锁。

一、limit 优化

深分页的典型问题:limit 2000000, 10,此时需要 MySQL 排序前 2000010 条记录,最后仅仅返回 2000000 - 2000010 的记录,其他记录全部丢弃,查询排序的代价非常大。

优化思路:一般分页查询时,通过创建覆盖索引能够比较好地提高性能,可以通过覆盖索引 + 子查询的形式进行优化。

explain select * from tb_sku t,
       (select id from tb_sku order by id limit 2000000,10) a
where t.id = a.id;

先在覆盖索引(这里是主键)上完成分页、只取出 10 个 id,再靠这 10 个 id 回表取整行,避免对两百万行做回表与排序。

二、count 优化

explain select count(*) from tb_user;
引擎count(*) 的执行方式
MyISAM把一个表的总行数存在磁盘上,执行时直接返回这个数,效率很高
InnoDB需要把数据一行一行地从引擎里面读出来,然后累积计数,比较麻烦

优化思路:自己计数(用 Redis 等外部存储维护计数,或维护计数表)。

2.1 count 的几种用法

count() 是一个聚合函数:对返回的结果集一行行地判断,如果 count 函数的参数不是 NULL,累计值就加 1,否则不加,最后返回累计值。

用法:count(*)、count(主键)、count(字段)、count(1)。

用法InnoDB 的执行过程
count(主键)遍历整张表,把每一行的主键 id 值都取出来返回给服务层;服务层拿到主键后直接按行累加(主键不可能为 null)
count(字段),无 not null 约束遍历整张表把每一行的字段值都取出来返回给服务层;服务层判断是否为 null,不为 null 才累加
count(字段),有 not null 约束遍历整张表把每一行的字段值取出来返回给服务层,直接按行累加
count(1)遍历整张表但不取值;服务层对返回的每一行放一个数字 “1” 进去,直接按行累加
count(*)遍历整张表但不取值,InnoDB 专门做了优化;服务层直接按行累加

按照效率排序:count(字段) < count(主键 id) < count(1) ≈ count(*),所以尽量使用 count(*)。

三、update 优化

-- where 条件是主键:走主键索引,加行锁
update student set no = '2000100100' where id = 1;

-- where 条件是 name(无索引):无法定位索引 → 表锁
update student set no = '2000100105' where name = '韦一笑';

InnoDB 的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则会从行锁升级为表锁,并发更新时阻塞其他事务。

速查

-- limit 深分页:覆盖索引 + 子查询
explain select * from tb_sku t,
       (select id from tb_sku order by id limit 2000000,10) a
where t.id = a.id;

-- count:优先 count(*);效率 count(字段) < count(主键 id) < count(1) ≈ count(*)
explain select count(*) from tb_user;
-- 优化思路:自己计数(Redis / 计数表)

-- update:where 必须走索引,否则行锁升级为表锁
update student set no = '2000100100' where id = 1;          -- 主键索引 → 行锁
update student set no = '2000100105' where name = '韦一笑';  -- name 无索引 → 表锁
优化点一句话结论
limit 深分页先按覆盖索引取 id 完成分页,再回表取整行,避免丢弃前面的大量记录
countInnoDB 只能逐行累积计数;写 count(*),或改用外部计数
updatewhere 走主键 / 索引,否则行锁升级为表锁,阻塞并发更新

相关

  • 索引使用:覆盖索引与索引失效场景
  • 索引设计原则:建索引的取舍原则
  • SQL 优化 - 插入数据与主键优化 / SQL 优化 - order by 与 group by
  • 事务:锁与并发事务
最近更新: 2026/10/11 07:51
Contributors: liujw
Prev
SQL 优化 - order by 与 group by
Next
视图