核心是让数据库少扫描、少排序、少回表;索引设计决定“能不能快”,分页策略决定“快不快得稳定”。两者必须配合,单靠一方效果有限。

核心是让数据库少扫描、少排序、少回表。索引设计决定“能不能快”,分页策略决定“快不快得稳定”。两者必须配合,单靠一方效果有限。
索引设计要聚焦查询模式
不是字段越多越好,而是要匹配你实际怎么查的。比如经常按 status + create_time 排序分页,就建联合索引:
CREATE INDEX idx_status_time ON orders(status, create_time);
- 遵循最左前缀:这个索引能加速
WHERE status = ? ORDER BY create_time,但对WHERE create_time > ?无效 - 避免函数操作:别写
WHERE YEAR(create_time) = 2024,改用create_time BETWEEN '2024-01-01' AND '2024-12-31',否则索引失效 - 覆盖索引优先:如果分页只查 id、name、status,可建
INDEX idx_cover ON users(id, name, status),查完索引就返回,不用再回表取数据 - 删掉冗余索引:已有
(a,b)就别再单独建(a);已有(a,b,c),又建了(a,b)和(a),属于浪费
分页不能只靠 LIMIT OFFSET
当 offset 超过几万,数据库仍要跳过前面所有行并排序——这些工作全白做了。换成基于游标的分页更可靠:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 第一页查: SELECT id, name, status FROM orders WHERE status = 'paid' ORDER BY id LIMIT 20;
- 下一页查(记下上页最后 id=1005): SELECT id, name, status FROM orders WHERE status = 'paid' AND id > 1005 ORDER BY id LIMIT 20;
- 优势明显:跳过扫描、无排序压力、响应时间稳定,适合滚动加载场景
- 注意:要求排序字段(如 id)严格递增且无重复,否则需组合多个字段,例如
ORDER BY create_time, id,查询条件也相应改为WHERE create_time > ? OR (create_time = ? AND id > ?)
Java 层配合减少无效查询
光有好 SQL 不够,Java 代码要避免拖后腿:
- 用 PreparedStatement 预编译语句,复用执行计划,防止 SQL 注入同时提升性能
- 分页结果集大时,用流式查询(如 MyBatis 的
selectCursor或 JDBC 的Statement.setFetchSize(Integer.MIN_VALUE)),避免一次性把百万行全 load 到内存 - 高频分页接口(如后台订单列表)加二级缓存:Redis 缓存「参数 → 分页结果 ID 列表」,再异步查详情;或 Caffeine 缓存最近几页的完整结果(注意缓存穿透和更新一致性)
- 禁用
SELECT *:明确列出字段,既减少网络传输,也利于覆盖索引生效
验证与持续维护不可少
建了索引、改了 SQL,得看真实执行计划是否如预期:
- MySQL 中用 EXPLAIN FORMAT=TREE 查看是否走索引、是否 Using filesort、是否 Using temporary
- 定期检查慢查询日志,重点关注 rows_examined 大、query_time 长的分页语句
- 索引不是一劳永逸:数据分布变化(如某 status 值占比超 95%)、新增查询维度,都可能让旧索引失效,需动态评估
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










