Java案例怎么避免全表扫描?

wen java案例 62

本文目录导读:

Java案例怎么避免全表扫描?

  1. 核心原则:让查询走索引
  2. 具体案例与解决方案
  3. Java 代码层面的通用优化模板
  4. 避免全表扫描的检查清单

在Java开发中,避免全表扫描的核心思路是让数据库查询尽量使用索引,而不是在内存中处理大量数据,下面从设计层面代码层面数据库层面给出具体案例和解决方案。


核心原则:让查询走索引

全表扫描通常发生在:

  • 查询条件没有索引
  • 查询条件使用了函数或计算
  • 使用了 LIKE '%keyword' 模糊查询
  • 隐式类型转换
  • 数据量很小(MySQL 可能直接全表扫描)

具体案例与解决方案

案例1:模糊查询导致全表扫描

错误写法

String sql = "SELECT * FROM user WHERE name LIKE '%" + keyword + "%'";

解决方案

  • 使用前缀匹配(走索引):
    String sql = "SELECT * FROM user WHERE name LIKE '" + keyword + "%'";
  • 或使用全文索引(MySQL):
    ALTER TABLE user ADD FULLTEXT INDEX idx_name (name);
    SELECT * FROM user WHERE MATCH(name) AGAINST('keyword');

案例2:隐式类型转换

错误写法

String sql = "SELECT * FROM user WHERE phone = " + phone;  // phone是varchar,phone是String

👉 phone 是 VARCHAR 类型,但传入的是数字,MySQL 会转成数字比较,导致索引失效。

正确写法

String sql = "SELECT * FROM user WHERE phone = '" + phone + "'";

👉 或者使用 PreparedStatement:

PreparedStatement ps = conn.prepareStatement("SELECT * FROM user WHERE phone = ?");
ps.setString(1, phone);

案例3:在索引列上使用函数

错误写法

String sql = "SELECT * FROM order WHERE DATE(create_time) = '2024-01-01'";

正确写法

String sql = "SELECT * FROM order WHERE create_time >= '2024-01-01 00:00:00' AND create_time < '2024-01-02 00:00:00'";

案例4:分页查询深度偏移

错误写法

String sql = "SELECT * FROM user LIMIT 100000, 10";

👉 先扫描10万行,再取最后10行。

优化方案

  • 延迟关联:先查主键,再关联查数据

    String sql = "SELECT u.* FROM user u " +
               "INNER JOIN (SELECT id FROM user ORDER BY id LIMIT 100000, 10) tmp " +
               "ON u.id = tmp.id";
  • 游标分页:记录上一次查询的最后id

    String sql = "SELECT * FROM user WHERE id > ? ORDER BY id LIMIT 10";

案例5:用 Java Stream 代替 SQL 过滤

错误写法

List<User> allUsers = userRepository.findAll();  // 全表扫描
List<User> activeUsers = allUsers.stream()
    .filter(u -> "ACTIVE".equals(u.getStatus()))
    .collect(Collectors.toList());

正确写法

List<User> activeUsers = userRepository.findByStatus("ACTIVE");  // 走索引

案例6:批量操作使用 IN 替代多个 OR

错误写法

String sql = "SELECT * FROM user WHERE id = 1 OR id = 2 OR id = 3";

正确写法

String sql = "SELECT * FROM user WHERE id IN (1, 2, 3)";

👉 IN 在 MySQL 中会使用 range 扫描,性能远优于 OR。


Java 代码层面的通用优化模板

使用 PreparedStatement + 绑定变量

// 避免SQL注入 + 减少SQL解析次数 + 让数据库使用缓存执行计划
String sql = "SELECT * FROM user WHERE name = ? AND age > ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "张三");
ps.setInt(2, 20);
ResultSet rs = ps.executeQuery();

使用 ORM 框架时要小心

  • MyBatis
    • 避免在 <if> 标签中拼接 OR 1=1
    • <bind>CONCAT 控制模糊查询前缀
  • JPA/Hibernate
    • 注意 @Query 中的 HQL 是否有隐式类型转换
    • Specification + CriteriaBuilder 避免全表

数据量大时使用游标或流式查询

// JPA 流式查询(避免一次性加载全部数据到内存)
@QueryHints(@QueryHint(name = "org.hibernate.fetchSize", value = "1000"))
Stream<User> streamAllUsers();

避免全表扫描的检查清单

场景 错误做法 正确做法
模糊查询 LIKE '%keyword%' LIKE 'keyword%' 或全文索引
隐式类型转换 WHERE phone = 123 WHERE phone = '123'
函数使用 WHERE DATE(col)=... WHERE col BETWEEN ...
分页深偏移 LIMIT 100000,10 游标/延迟关联
代码过滤 查询全部后Java过滤 SQL条件过滤
多个OR OR 连接 IN (...)
无索引查询 频繁查询无索引字段 加索引

一句话总结
Java 中避免全表扫描,最有效的方法不是优化代码,而是让 SQL 走索引——包括明确 SQL 执行计划、检查索引覆盖、避免破坏索引的函数/计算/类型转换。

抱歉,评论功能暂时关闭!