应优先让小表驱动大表,为join和where字段建复合索引,用cte/子查询提前过滤,java端采用原生sql+dto投影优化性能。

明确驱动表顺序,小表驱动大表
数据库执行多表JOIN时,默认可能选择低效的连接顺序。应让数据量小的表作为驱动表(外层循环),减少嵌套匹配次数。例如:订单明细表(百万级)和商品分类表(几十行),应让分类表驱动明细表。
MySQL中可使用 STRAIGHT_JOIN 强制指定顺序:
SELECT /*+ STRAIGHT_JOIN */ o.id, c.name, p.title
FROM categories c
STRAIGHT_JOIN products p ON c.id = p.category_id
STRAIGHT_JOIN order_items oi ON p.id = oi.product_id
WHERE c.status = 1;
为JOIN字段和过滤条件建复合索引
仅在单列上建索引对多表JOIN效果有限。需结合ON条件与WHERE条件设计复合索引。例如:
- JOIN条件是 p.category_id = c.id,且常按 c.status 过滤 → 在 categories(status, id) 上建索引
- JOIN后查 products 的 title 和 price → 在 products(category_id, status) 上建索引,覆盖常用查询路径
用子查询或CTE提前缩小结果集
避免在大表上直接JOIN全量数据。先用子查询/CTE筛选出必要主键,再关联其他表:
WITH target_orders AS (
SELECT id, customer_id FROM orders WHERE created_at >= '2026-09-01'
)
SELECT o.id, c.name, SUM(i.quantity * i.price) AS amt
FROM target_orders o
JOIN customers c ON o.customer_id = c.id
JOIN order_items i ON o.id = i.order_id
GROUP BY o.id, c.name;
这样能显著降低JOIN的数据规模,尤其适用于时间范围、状态等高区分度过滤场景。
Java端配合:用@Query + DTO投影,避免实体映射开销
JPA默认将结果映射为完整实体,JOIN多表时易触发懒加载或生成冗余对象。推荐用原生SQL + 接口投影DTO:
@Query(value = """
SELECT o.id AS orderId, c.name AS customerName,
COUNT(i.id) AS itemCount, SUM(i.price) AS total
FROM orders o
INNER JOIN customers c ON o.customer_id = c.id
LEFT JOIN order_items i ON o.id = i.order_id
WHERE o.status = :status
GROUP BY o.id, c.name
""", nativeQuery = true)
List
其中 OrderSummary 是只含所需字段的接口,不走Hibernate代理,无反射开销,内存与序列化压力更小。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











