写点什么

阿里巴巴为什么禁止超过 3 张表 join?

  • 2025-07-21
    福建
  • 本文字数:1926 字

    阅读完需:约 6 分钟

前言


2017 年,《阿里巴巴 Java 开发手册》 中一条规定掀起技术圈巨浪:“禁止超过三张表进行 join 操作”

时至今日,这条规范仍被众多企业奉为圭臬。

但背后原因你真的懂吗?

本文将从架构设计、执行原理、实战案例三方面深度解析,带你揭开这条军规背后的技术真相!

希望对你会有所帮助。


一、多表 JOIN 的性能噩梦


1.1 真实案例:一次血泪教训


某电商平台订单查询接口,原 SQL:

SELECT o.*, u.name, u.phone, p.product_name FROM orders oJOIN users u ON o.user_id = u.user_idJOIN products p ON o.product_id = p.product_idJOIN warehouses w ON o.warehouse_id = w.id  -- 第四张表!WHERE o.status = 1;
复制代码


现象

  • 单次查询耗时 800ms+

  • 高峰期数据库 CPU 飙升至 90%

  • 频繁触发慢查询告警

原因:MySQL 优化器面对四表 JOIN 时,错误选择了驱动表顺序,导致全表扫描超百万数据!


二、MySQL 的 JOIN 之殇


2.1 执行引擎的先天缺陷



MySQL 仅支持三种 JOIN 算法:

  1. Simple Nested-Loop Join:暴力双循环,复杂度 O(m*n)

  2. Block Nested-Loop Join:批量加载到 join_buffer,仍为 O(m*n)

  3. Index Nested-Loop Join:依赖索引,最优复杂度 O(m*log n)


致命缺陷

  • Hash Join(8.0.18 前)

  • Sort-Merge Join

  • 多表关联时优化器极易选错驱动表


2.2 优化器的局限性


当表数量增加时:

  1. 可能的 JOIN 顺序呈阶乘级增长(4 表=24 种,5 表=120 种)

  2. MySQL 优化器采用贪心算法而非穷举,易选劣质计划

  3. 统计信息不准时雪上加霜


三、分布式架构的致命一击


3.1 分库分表后的 JOIN 困境


阿里系业务普遍采用分库分表,此时多表 JOIN 会:



三大痛点

  1. 跨节点数据关联需业务层实现

  2. 网络传输成为性能瓶颈

  3. 事务一致性难以保障


3.2 分库分表后的性能对比



实测数据(订单表分 16 个库,每库 64 张表):



四、破局之道:阿里推荐解决方案


4.1 方案一:分步查询+内存计算

// 1. 查询订单基础信息List<Order> orders = orderDao.query("SELECT * FROM orders WHERE status=1");
// 2. 提取用户ID去重Set<Long> userIds = orders.stream().map(Order::getUserId).collect(Collectors.toSet());
// 3. 批量查询用户信息Map<Long, User> userMap = userDao.queryByIds(userIds).stream() .collect(Collectors.toMap(User::getId, Function.identity()));
// 4. 内存数据组装orders.forEach(order -> { order.setUserName(userMap.get(order.getUserId()).getName());});
复制代码


优势

  • 避免复杂 JOIN

  • 充分利用缓存机制

  • 易于分页处理


4.2 方案二:反范式设计


场景:订单列表需显示商品名称

优化前

SELECT o.*, p.name FROM orders o JOIN products p ON o.product_id = p.id  -- 需要JOIN
复制代码


优化后

CREATE TABLE orders (  id BIGINT,  product_id BIGINT,  product_name VARCHAR(100)  -- 冗余商品名称);
复制代码


取舍原则

  1. 高频查询字段可冗余

  2. 变更少的字段可冗余

  3. 写 QPS 低的业务可冗余


4.3 方案三:异步物化视图


-- 创建预计算视图CREATE MATERIALIZED VIEW order_detail_view ASSELECT o.*, u.name, u.phone, p.product_nameFROM orders o JOIN users u ON o.user_id = u.user_idJOIN products p ON o.product_id = p.product_idWHERE o.status = 1;
-- 查询直接访问视图SELECT * FROM order_detail_view WHERE user_id = 1001;
复制代码


适用场景

  • 实时性要求不高的报表

  • 聚合查询较多的场景


五、何时能打破禁令?


5.1 场景一:使用 TiDB 等 NewSQL 数据库


TiDB 的分布式 Hash Join 实现:



核心优化

  • 多线程并发构建 Hash 表

  • 智能选择 Build 端(小表)

  • 内存控制+磁盘 Spill 能力


5.2 场景二:OLAP 分析场景


ClickHouse 的 JOIN 策略:

SELECT     a.*, b.extra_dataFROM big_table aJOIN small_table b ON a.id = b.idSETTINGS     join_algorithm = 'hash',  -- 指定Hash Join    max_bytes_in_join = '10G' -- 内存控制
复制代码


适用特征

  • 大数据量低延迟分析

  • 主表远大于维表


六、黄金实践法则


6.1 JOIN 优化四原则


1、小表驱动大表

-- 反例:大表驱动小表SELECT * FROM 10m_big_table JOIN 100k_small_table
-- 正例:小表驱动大表SELECT * FROM 100k_small_table JOIN 10m_big_table
复制代码


2、被驱动表必须有索引

ON 条件字段必须有索引(除非维表<100 行)


3、拒绝 3 张以上 JOIN

超过时优先考虑业务拆分


4、禁止跨 DB 实例 JOIN


6.2 军规适用边界



总结


“禁止三表 JOIN”本质是架构思维的转变

  1. 从“数据库是全能选手”到数据库专注存储与事务

  2. 从“SQL 解决一切”到业务逻辑分层处理

  3. 从“实时一致性”到最终一致性的设计妥协


正如阿里资深 DBA 所言:

“当你的系统面临千万级并发时,每个微秒的优化都是在为业务争取生存权。规范不是枷锁,而是前辈用血泪换来的生存指南。”


文章转载自:苏三说技术

原文链接:https://www.cnblogs.com/12lisu/p/18987174

体验地址:http://www.jnpfsoft.com/?from=001YH

用户头像

还未添加个人签名 2025-04-01 加入

还未添加个人简介

评论

发布
暂无评论
阿里巴巴为什么禁止超过3张表join?_前端_量贩潮汐·WholesaleTide_InfoQ写作社区