阿里巴巴为什么禁止超过3张表join?
前言
2017年,《阿里巴巴Java开发手册》 中一条规定掀起技术圈巨浪:“禁止超过三张表进行join操作”。
时至今日,这条规范仍被众多企业奉为圭臬。
但背后原因你真的懂吗?
本文将从架构设计、执行原理、实战案例三方面深度解析,带你揭开这条军规背后的技术真相!
希望对你会有所帮助。
一、多表JOIN的性能噩梦
1.1 真实案例:一次血泪教训
某电商平台订单查询接口,原SQL:
SELECT o.*, u.name, u.phone, p.product_name
FROM orders o
JOIN users u ON o.user_id = u.user_id
JOIN products p ON o.product_id = p.product_id
JOIN warehouses w ON o.warehouse_id = w.id -- 第四张表!
WHERE o.status = 1;
现象:
- 单次查询耗时800ms+
- 高峰期数据库CPU飙升至90%
- 频繁触发慢查询告警
原因:MySQL优化器面对四表JOIN时,错误选择了驱动表顺序,导致全表扫描超百万数据!
二、MySQL的JOIN之殇
2.1 执行引擎的先天缺陷

MySQL仅支持三种JOIN算法:
- Simple Nested-Loop Join:暴力双循环,复杂度O(m*n)
- Block Nested-Loop Join:批量加载到join_buffer,仍为O(m*n)
- Index Nested-Loop Join:依赖索引,最优复杂度O(m*log n)
致命缺陷:
- 无Hash Join(8.0.18前)
- 无Sort-Merge Join
- 多表关联时优化器极易选错驱动表
2.2 优化器的局限性
当表数量增加时:
- 可能的JOIN顺序呈阶乘级增长(4表=24种,5表=120种)
- MySQL优化器采用贪心算法而非穷举,易选劣质计划
- 统计信息不准时雪上加霜
三、分布式架构的致命一击
3.1 分库分表后的JOIN困境
阿里系业务普遍采用分库分表,此时多表JOIN会:

三大痛点:
- 跨节点数据关联需业务层实现
- 网络传输成为性能瓶颈
- 事务一致性难以保障
3.2 分库分表后的性能对比

实测数据(订单表分16个库,每库64张表):
| 查询类型 | 响应时间 | CPU消耗 | 网络流量 |
|---|---|---|---|
| 单分片查询 | 25ms | 5% | 5KB |
| 跨分片JOIN | 1200ms | 85% | 120MB |
| 内存合并 | 800ms | 70% | 80MB |
四、破局之道:阿里推荐解决方案
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) -- 冗余商品名称
);
取舍原则:
- 高频查询字段可冗余
- 变更少的字段可冗余
- 写QPS低的业务可冗余
4.3 方案三:异步物化视图
-- 创建预计算视图
CREATE MATERIALIZED VIEW order_detail_view
AS
SELECT o.*, u.name, u.phone, p.product_name
FROM orders o
JOIN users u ON o.user_id = u.user_id
JOIN products p ON o.product_id = p.product_id
WHERE 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_data
FROM big_table a
JOIN small_table b ON a.id = b.id
SETTINGS
join_algorithm = 'hash', -- 指定Hash Join
max_bytes_in_join = '10G' -- 内存控制
适用特征:
- 大数据量低延迟分析
- 主表远大于维表
六、黄金实践法则
6.1 JOIN优化四原则
- 小表驱动大表
-- 反例:大表驱动小表
SELECT * FROM 10m_big_table JOIN 100k_small_table
-- 正例:小表驱动大表
SELECT * FROM 100k_small_table JOIN 10m_big_table
- 被驱动表必须有索引
ON条件字段必须有索引(除非维表<100行) - 拒绝3张以上JOIN
超过时优先考虑业务拆分 - 禁止跨DB实例JOIN
6.2 军规适用边界
| 场景 | 是否允许JOIN | 理由 |
|---|---|---|
| OLTP高频交易 | 禁用 | 响应时间敏感 |
| OLAP分析系统 | 允许 | 吞吐量优先 |
| 分库分表架构 | 禁用 | 跨节点JOIN性能差 |
| 小表(<100行)关联 | 允许 | 性能损耗可忽略 |
总结
“禁止三表JOIN”本质是架构思维的转变:
- 从“数据库是全能选手”到数据库专注存储与事务
- 从“SQL解决一切”到业务逻辑分层处理
- 从“实时一致性”到最终一致性的设计妥协
正如阿里资深DBA所言:
“当你的系统面临千万级并发时,每个微秒的优化都是在为业务争取生存权。规范不是枷锁,而是前辈用血泪换来的生存指南。”
最后说一句(求关注,别白嫖我)
如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下我的同名公众号:苏三说技术,您的支持是我坚持写作最大的动力。
求一键三连:点赞、转发、在看。
关注公众号:【苏三说技术】,在公众号中回复:进大厂,可以免费获取我最近整理的10万字的面试宝典,好多小伙伴靠这个宝典拿到了多家大厂的offer。
本文收录于我的技术网站:http://www.susan.net.cn
阿里巴巴为什么禁止超过3张表join?的更多相关文章
- 超过三张表禁止join
一. 问题提出 <阿里巴巴JAVA开发手册>里面写超过三张表禁止join,这是为什么? 二.问题分析 对这个结论,你是否有怀疑呢?也不知道是哪位先哲说的不要人云亦云,今天我设计sql,来验 ...
- 《阿里巴巴JAVA开发手册》里面写超过三张表禁止join这是为什么?
分库分页.应用里做join 多表join性能很差 参考: 1.https://www.zhihu.com/question/56236190
- 十几张表的join(千万级/百万级表) 7hours-->5mins
================START============================== 来了一个mail说是job跑得很慢,调查下原因 先来看下sql: SELECT h.order_ ...
- CROSS JOIN连接用于生成两张表的笛卡尔集
将两张表的情况全部列举出来 结果表: 列= 原表列数相加 行= 原表行数相乘 CROSS JOIN连接用于生成两张表的笛卡尔集. 在sql中cross join的使用: 1.返回的记录数为两个 ...
- sql用逗号连接多张表对应哪个join?
转自:http://blog.csdn.net/huanghanqian/article/details/52847835 四种join的区别已老生常谈: INNER JOIN(也可简写为JOIN): ...
- 转载:sql用逗号连接多张表对应哪个join?
http://blog.csdn.net/huanghanqian/article/details/52847835 四种join的区别已老生常谈: INNER JOIN(也可简写为JOIN): 如果 ...
- Azure SQL Database (21) 将整张表都迁移到Azure Stretch Database里
<Windows Azure Platform 系列文章目录> Azure SQL Database (19) Stretch Database 概览 Azure SQL Da ...
- update操作多张表
sql 语句多张表UPDATE用法一.当用一个表中的数据来更新另一个表中的数据,T-SQL提供多种写法(下面列出了二种),但建议用第一种写法,虽然传统,但结构清晰.飞.飞Asp技术乐园并且要注意,当用 ...
- 阿里规范不建议多表Join,可这SQL要怎么写?
阿里开发手册的描述,禁止多表join: 手册上写着[强制],相信很多同学项目里面的代码都不满足这个要求. 但是关键问题是:不用join,这SQL究竟要怎么写?! 分解关联查询 即对每个要关联的表进行单 ...
- SQLSERVER中如何快速比较两张表的不一样
SQLSERVER中如何快速比较两张表的不一样 不知不觉要写2014年的最后一篇博文了~ 一般来说,如何检测两张表的内容是否一致,体现在复制的时候发布端和订阅端的两端的数据上面 我这里罗列了一些如何从 ...
随机推荐
- 康谋分享 | aiSim5基于生成式AI扩大仿真测试范围(终)
在前面的几章节中探讨了aiSim仿真合成数据的置信度,此外在场景重建和测试流程闭环的过程中,难免会面临3D场景制作重建耗时长.成本高.扩展性低以及交通状况复杂程度难以满意等问题,当前的主要挑战在于如何 ...
- 『Plotly实战指南』--样式定制高级篇
在数据可视化领域,Plotly不仅是高效的绘图工具,更是设计师的创意画布. 当基础图表已无法满足品牌化需求时,样式定制能力将成为数据叙事的关键武器. 深入的样式定制能够帮助我们打造品牌化图表.实现精准 ...
- SpringBoot内容协商(Content Negotiation)二 —— 自定义消息转换器(MessageConverter)
SpringBoot内置的消息转换器 SpringBoot没有处理返回yaml格式的数据,这里需要手动添加处理这种返回格式的支持. 导入依赖 <dependency> <groupI ...
- maven配置jdk版本
修改默认的jdk版本 在maven安装目录 apache-maven-3.6.1\conf\setting.xml 添加 <profile> <id>jdk18</id& ...
- FastAPI与Alembic:数据库迁移的隐秘艺术
title: FastAPI与Alembic:数据库迁移的隐秘艺术 date: 2025/05/13 02:02:31 updated: 2025/05/13 02:02:31 author: cmd ...
- 网络编程:TCP故障模式
故障模式总结 异常情况可归结为两大类: 第一类,是对端无FIN包发送出来的情况:第二类是对端有FIN包发出来 对端无FIN包发送出 网络终端造成对端无FIN包 很多原因都会造成网络中断,这种情况,TC ...
- WebSocket 与 SSE 对比:即时通信的选择(一)
在现代 Web 开发中,实时通信需求越来越多,比如聊天应用.实时通知.直播弹幕.股票行情推送等.实现这些需求的常见技术有 WebSocket 和 SSE(Server-Sent Events),但它们 ...
- ps ef命令查询进程号pid
楼兰胡杨已经在<五分钟扫盲:25个工作中常用的Linux命令>分享了ps命令的简单使用方法,但是,写的过于笼统,这里详细介绍一下. 语法:ps -ef | grep process ...
- 面试题|Spring中BeanFactory与ApplicationContext的本质区别和作用
BeanFactory 是Bean工厂,是Spring 框架最核心的接口,它提供了高级IoC 的配置机制.如果说BeanFactory是Spring的心脏,那么应用上下文ApplicationCo ...
- Qt 图片轮播
最近研究了一下图片轮播,主要是用到了QPropertyAnimation这个类,具体代码示例如下: main.cpp #include <QApplication> #include &q ...