MySQL到底是 join 性能好,还是in一下更快呢?

94f4888d7e8554eaf6985d429eb59c71.jpeg来源:juejin.cn/post/7169567387527282701

👉 欢迎加入小哈的星球 ,你将获得: 专属的项目实战/ Java 学习路线 / 一对一提问 / 学习打卡/赠书福利

目前, 正在星球内部带小伙伴做第一个项目:全栈前后端分离博客,手摸手,后端 + 前端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,直到项目上线。目前已更新了125小节,累计17w+字,讲解图:805张,还在持续爆肝中.. 后续还会上新更多项目,目标是将Java领域典型的项目都整一波,如秒杀系统, 在线商城, IM即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,已有380+小伙伴加入(早鸟价超低)

  • 一、表结构

    • 1、用户表

    • 2、订单表

  • 二、先来试少量数据的情况

    • 1、join

    • 2、分开查

    • 3、代码层面

  • 三、试下多一些数据的情况

    • 1、join

    • 2、分开

    • 3、代码层面

  • 三、试下多一些数据的情况

    • 1、join

    • 2、分开

    • 3、代码层面

  • 四、到底怎么才能更好

  • 参考资料


先总结:

  1. 数据量小的时候,用join更划算

  2. 数据量大的时候,join的成本更高,但相对来说join的速度会更快

  3. 数据量过大的时候,in的数据量过多,会有无法执行SQL的问题,待解决

事情是这样的,去年入职的新公司,之后在代码review的时候被提出说,不要写join,join耗性能还是慢来着,当时也是真的没有多想,那就写in好了,最近发现in的数据量过大的时候会导致sql慢,甚至sql太长,直接报错了。

这次来浅究一下,到底是in好还是join好,仅目前认知探寻,有不对之处欢迎指正

以下实验仅在本机电脑试验

一、表结构

1、用户表

8f8032bc7c5a3b0232981914501bcec9.jpeg

图片
CREATE TABLE `user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '姓名',
  `gender` smallint DEFAULT NULL COMMENT '性别',
  `mobile` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '手机号',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `mobile` (`mobile`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1005 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci

2、订单表

0103d85daee2ba3f3e0c48e82ac56e14.jpeg

图片
CREATE TABLE `order` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `price` decimal(18,2) NOT NULL,
  `user_id` int NOT NULL,
  `product_id` int NOT NULL,
  `status` smallint NOT NULL DEFAULT '0' COMMENT '订单状态',
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  KEY `product_id` (`product_id`)
) ENGINE=InnoDB AUTO_INCREMENT=202 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci

二、先来试少量数据的情况

用户表插一千条随机生成的数据,订单表插一百条随机数据

查下所有的订单以及订单对应的用户

下面从三个维度来看

多表连接查询成本 = 一次驱动表成本 + 从驱动表查出的记录数 * 一次被驱动表的成本

1、join

JOIN:

explain format=json select order.id, price, user.`name` from `order` join user on order.user_id = user.id;

子查询:

select order.id,price,user.`name` from `order`,user where user_id=user.id;

db86464b9c19006d9c4c7e87c041a534.jpeg

图片

2、分开查

select `id`,price,user_id from `order`;

b73aff060bbe4e0617751fc77c9c7557.jpeg

图片
select name from user where id in (8, 11, 20, 32, 49, 58, 64, 67, 97, 105, 113, 118, 129, 173, 179, 181, 210, 213, 215, 216, 224, 243, 244, 251, 280, 309, 319, 321, 336, 342, 344, 349, 353, 358, 363, 367, 374, 377, 380, 417, 418, 420, 435, 447, 449, 452, 454, 459, 461, 472, 480, 487, 498, 499, 515, 525, 525, 531, 564, 566, 580, 584, 586, 592, 595, 610, 633, 635, 640, 652, 658, 668, 674, 685, 687, 701, 718, 720, 733, 739, 745, 751, 758, 770, 771, 780, 806, 834, 841, 856, 856, 857, 858, 882, 934, 942, 983, 989, 994, 995);  \[in的是order查出来的所有用户id\]

08bd1e4f7cb7f65ae6cb18ed46bbad7d.jpeg

图片

如此看来,分开查和join查的成本并没有相差许多

3、代码层面

主要用php原生写了脚本,用ab进行10个同时的请求,看下时间,进行比较

ab -n 100 -c 10

in
$mysqli = new mysqli('127.0.0.1', 'root', 'root', 'test');
 if ($mysqli->connect_error) {
     die('Connect Error (' . $mysqli->connect_errno . ') ' . $mysqli->connect_error);
 }

 $result = $mysqli->query('select `id`,price,user_id from `order`');
 $orders = $result->fetch_all(MYSQLI_ASSOC);

 $userIds = implode(',', array_column($orders, 'user_id')); // 获取订单中的用户id
 $result = $mysqli->query("select `id`,`name` from `user` where id in ({$userIds})");
 $users = $result->fetch_all(MYSQLI_ASSOC);// 获取这些用户的姓名

 // 将id做数组键
 $userRes = [];
 foreach ($users as $user) {
     $userRes[$user['id']] = $user['name'];
 }

 $res = [];
 // 整合数据
 foreach ($orders as $order) {
     $current = [];
     $current['id'] = $order['id'];
     $current['price'] = $order['price'];
     $current['name'] = $userRes[$order['user_id']] ?: '';
     $res[] = $current;
 }
 var_dump($res);

 // 关闭mysql连接

 $mysqli->close();

cf971fac87b3ec931669001c6b1d4327.jpeg

图片
join
$mysqli = new mysqli('127.0.0.1', 'root', 'root', 'test');
if ($mysqli->connect_error) {
    die('Connect Error (' . $mysqli->connect_errno . ') ' . $mysqli->connect_error);
}

$result = $mysqli->query('select order.id, price, user.`name` from `order` join user on order.user_id = user.id;');
$orders = $result->fetch_all(MYSQLI_ASSOC);

var_dump($orders);
$mysqli->close();

dbe2cde3ada0236118e216a0c8190613.jpeg

图片

看时间的话,明显join更快一些

三、试下多一些数据的情况

user表现在10000条数据,order表10000条试下

1、join

0502359e76c8eaf3ef9db7b90407c34d.jpeg

图片

2、分开

order

85609c66633edddb25a1697a617b9d0a.jpeg

图片

user

d34984080f648f7bcee203d5f8a5e976.jpeg

图片

3、代码层面

in

d510fbba1dfa0c33f7584bf0c0e0ceff.jpeg

图片

join

990c8236a71db1819955115cb6fddc1f.jpeg

图片

三、试下多一些数据的情况

随机插入后user表十万条数据,order表一百万条试下

1、join

1bbb3c2c5de4d85e37b92c9730d5b159.jpeg

图片

2、分开

order

afe220f85b506dcef7772de363b2c054.jpeg

图片

user

order查出来的结果过长了,,,

3、代码层面

in

2070f5926f7c6bef35cb6d6f5b30e30b.jpeg

图片

join

4777dad68d13523dad4a1484be136441.jpeg

图片

四、到底怎么才能更好

注:对于本机来说100000条数据不少了,更大的数据量害怕电脑卡死

总的来说,当数据量小时,可能一页数据就够放的时候,join的成本和速度都更好。数据量大的时候确实分开查的成本更低,但是由于数据量大,造成循环的成本更多,代码执行的时间也就越长。

实验过程中发现,当in的数据量过大的时候,sql过长会无法执行,可能还要拆开多条sql进行查询,这样的查询成本和时间一定也会更长,而且如果有分页的需求的话,也无法满足。。。

感觉这两个方法都不是太好,各位小伙伴,有没有更好的方法呢?

好书推荐

8db5160a397a286d081b037fe587fa46.jpeg

《Vue.js 3.0项目开发实战》以JavaScript为基础,以项目开发过程为主线,以Vue.js 3.0为开发工具,首先介绍从NoSQL数据库的搭建到Express项目API的编写过程,然后再由Vue.js显示在前端页面中,从而让读者可以迅速掌握Vue.js 3.0技术,提高项目开发水平。

👉 欢迎加入小哈的星球 ,你将获得: 专属的项目实战/ Java 学习路线 / 一对一提问 / 学习打卡/赠书福利

目前, 正在星球内部带小伙伴做第一个项目:全栈前后端分离博客,手摸手,后端 + 前端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,直到项目上线。目前已更新了125小节,累计17w+字,讲解图:805张,还在持续爆肝中.. 后续还会上新更多项目,目标是将Java领域典型的项目都整一波,如秒杀系统, 在线商城, IM即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,已有380+小伙伴加入(早鸟价超低)

 
 
 
 
 
 
 
 

84eedee509408f8a6808f9c4b84d8ce6.gif

 
 
 
 
 
 

1. 我的私密学习小圈子~

2. 面试官:一台服务器最大能支持多少条 TCP 连接?问倒一大片。。。

3. 阿里开源的一套国际化全流程解决方案,很不错!

4. 阿里终面:10亿数据如何快速插入MySQL?

b4c7b76187922ef54cfe96dd5a86b929.gif

最近面试BAT,整理一份面试资料《Java面试BATJ通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。
获取方式:点“在看”,关注公众号并回复 Java 领取,更多内容陆续奉上。
PS:因公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。
点“在看”支持小哈呀,谢谢啦
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值