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

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

公众号新闻

👉 这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入芋道快速开发平台知识星球。下面是星球提供的部分资料: 

👉这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号等等功能:

  • Boot 地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 地址:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn

来源:juejin.cn/post/
7169567387527282701


先总结:

  1. 数据量小的时候,用join更划算
  2. 数据量大的时候,join的成本更高,但相对来说join的速度会更快
  3. 数据量过大的时候,in的数据量过多,会有无法执行SQL的问题,待解决

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

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

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

一、表结构

1、用户表

 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、订单表

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

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

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

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

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

下面从三个维度来看

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

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;

2、分开查

select `id`,price,user_id from `order`;
select name from user where id in (81120324958646797105113118129173179181210213215216224243244251280309319321336342344349353358363367374377380417418420435447449452454459461472480487498499515525525531564566580584586592595610633635640652658668674685687701718720733739745751758770771780806834841856856857858882934942983989994995);  \[in的是order查出来的所有用户id\]

如此看来,分开查和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();

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();

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

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

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

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

1、join

2、分开

order

user

3、代码层面

in

join

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

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

1、join

2、分开

order

user

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

3、代码层面

in

join

四、到底怎么才能更好

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

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

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

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


欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,长按”或“扫描”下方二维码噢

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

文章有帮助的话,在看,转发吧。

谢谢支持哟 (*^__^*)

微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
基于 MySQL 多通道主主复制的机房容灾方案MySQL binlog 三个典型的业务应用场景一百零八 游石湖MySQL 8.2 正式可用,支持读写分离VLDB顶会论文解读 | PolarDB MySQL高性能强一致集群核心技术详解一网打尽总结 Mysql 的所有 Buffer“MySQL 之父”的 MariaDB 要完蛋了?叫停两款核心产品并裁员 28%第十一章第四节 国家资本主义的社会制度MySQL到TiDB:Hive Metastore横向扩展之路MySQL 分库分表实践MySQL中update“经典”的坑,这样写语句,直接劝退!Python如何使用MySQL 8.2读写分离?重磅 |《开源数据库生态发展研究报告》发布 GreatSQL为MySQL5.7最佳替代方案!4 种 MySQL 同步 ES 方案,yyds!MySQL数据导入方案推荐如何设计一款基于 MySQL 实现的 Message Queue其實家庭PM2.5及輻射超標或更可怕MYSQL事务的底层原理Redis缓存与Mysql如何保证双写一致一百零七 系主任基于MySQL多通道主主复制的机房容灾方案“MySQL 之父”的 MariaDB 要完蛋了?叫停两款核心产品并裁员 28%,分析师:该行为无异于自毁长城6位诺奖校友,老牌英国公学,多项荣誉加身,Bedford School到底是怎样一所私校?SQL骚操作,一条SQL 统计近 7天、30天、全部的订单量Nginx 代理 MySQL 连接,并限制可访问 IPMysql集群之PXC-Docker安装聊聊即将到来的MySQL5.7停服事件弃用 MySQL 后存储成本降低 85%,携程业务系统数据库升级技术实践DoltgreSQL发布,基于Git的PostgreSQL为何在中国MySQL远比PostgreSQL流行MySQL主从同步延迟原因与解决方案MySQL如何性能调优?上篇阿里终面:10亿数据如何快速插入MySQL?MySQL备份恢复最佳实践:终极指南游阿尔泰山喀纳斯湖
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。