记录一次MySQL多表查询,order by不走索引的情况.
首先是表结构,部分字段脱敏已删除
CREATE TABLE `log_device_heart` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`device_number` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`time_periods_begin` datetime NOT NULL,
`time_periods_end` datetime DEFAULT NULL,
`create_time` timestamp NULL DEFAULT NULL,
`date` datetime DEFAULT NULL,
`num` int DEFAULT NULL,
`other_num` int DEFAULT '0',
PRIMARY KEY (`id`) USING BTREE,
KEY `device_number` (`device_number`) USING BTREE,
KEY `time_periods_begin_desc` (`time_periods_begin` DESC)
) ENGINE=InnoDB AUTO_INCREMENT=1168466 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; CREATE TABLE `m_device` (
`id` int NOT NULL AUTO_INCREMENT,
`type` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '仪器类型',
`number` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '仪器编号',
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '仪器名称',
`organization` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '机构',
`dealer` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '经销商', `area` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '区县',
`engineer_id` int DEFAULT NULL COMMENT '工程师id',
`update_date` datetime DEFAULT NULL,
`maintain_date` datetime DEFAULT NULL COMMENT '保养时间',
`dealer_id` int DEFAULT NULL COMMENT '经销商id',
`organization_id` int DEFAULT NULL COMMENT '机构id',
`socket_heart_last_time` datetime DEFAULT NULL COMMENT 'socket最后一次心跳时间',
`flag` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '标志位,0:未填写地址;1:已填写地址',
PRIMARY KEY (`id`) USING BTREE,
KEY `fk_device` (`engineer_id`) USING BTREE,
KEY `index_m_device_number` (`number`) USING BTREE,
KEY `organization_id` (`organization_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4615 DEFAULT CHARSET=utf8; CREATE TABLE `m_organization` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`deleted_flag` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '0' COMMENT '删除标志',
`dealer_id` int DEFAULT NULL COMMENT '经销商id',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=10066 DEFAULT CHARSET=utf8;
其中log_device_heart大概200w条数据,其它表都在200条左右
这是实际业务中的查询条件,这个项目是后续接手的,所以是不熟悉的Java项目
<select id="findDeviceHeartList" resultType="com.egs.biz.entity.vo.rsp.DeviceLogRsp$HeartRsp" parameterType="com.egs.biz.entity.vo.req.DeviceLogReq">
SELECT
t3.`name` as orgName,t1.num,t1.other_num as otherNum,
t1.device_number as deviceNumber,t1.time_periods_begin as timePeriodsBeginStr,
t1.time_periods_end as timePeriodsEndStr,t1.create_time as createTimeStr,t1.date as updateTimeStr
FROM log_device_heart t1 FORCE INDEX (time_periods_begin_desc)
left join m_device t2
on t1.device_number=t2.number
left join m_organization t3
on t3.id=t2.organization_id
WHERE 1=1
<if test="heartReq.organization!=null and heartReq.organization!=''">
and t3.`name` like '%${heartReq.organization}%'
</if>
<if test="heartReq.dataCleanFlag==true">
and organization_id<>'10007' and organization not like '%测试机器%'
</if>
<if test="heartReq.deviceNumber!=null and heartReq.deviceNumber!=''">
and t1.device_number=#{heartReq.deviceNumber}
</if>
<if test="heartReq.heart4GNum!=null and heartReq.heart4GNum!=''">
and t1.num < #{heartReq.heart4GNum}
</if>
<if test="heartReq.startDate!=null">
and t1.time_periods_begin >= DATE_FORMAT(#{heartReq.startDate},'%Y-%m-%d 00:00:00')
</if>
<if test="heartReq.endDate!=null">
and t1.time_periods_begin <= DATE_FORMAT(#{heartReq.endDate},'%Y-%m-%d 23:59:59')
</if>
order by t1.time_periods_begin desc
</select>
1 常规查询写法分析
select t3.`name` as orgName,t1.num,t1.other_num as otherNum,
t1.device_number AS deviceNumber,
t1.time_periods_begin AS timePeriodsBeginStr,
t1.time_periods_end AS timePeriodsEndStr,
t1.create_time AS createTimeStr,
t1.date AS updateTimeStr
FROM
log_device_heart t1 -- use index(time_periods_begin_desc)
STRAIGHT_JOIN m_device t2 ON t1.device_number = t2.number
LEFT JOIN m_organization t3 ON t3.id = t2.organization_id
WHERE
1 = 1
ORDER BY
t1.time_periods_begin DESC
LIMIT 10;
查询耗时3秒左右,查询计划显示驱动表并没有走索引,

200多w的主表数据,显然是无法接受这个结果的.
于是加上force index

SELECT
t3.`name` AS orgName,
t1.num,
t1.other_num AS otherNum,
t1.device_number AS deviceNumber,
t1.time_periods_begin AS timePeriodsBeginStr,
t1.time_periods_end AS timePeriodsEndStr,
t1.create_time AS createTimeStr,
t1.date AS updateTimeStr
FROM
log_device_heart t1 force INDEX ( time_periods_begin_desc )
LEFT JOIN m_device t2 ON t1.device_number = t2.number
LEFT JOIN m_organization t3 ON t3.id = t2.organization_id
WHERE
1 = 1
ORDER BY
t1.time_periods_begin DESC
LIMIT 10
> OK
> 时间: 0.001s
查询耗时可以忽略,提升了1000多倍.
但这会带来另一个问题,当我where条件添加筛选的时候,强制索引会导致效率降低,如下:
SELECT
t3.`name` AS orgName,
t1.num,
t1.other_num AS otherNum,
t1.device_number AS deviceNumber,
t1.time_periods_begin AS timePeriodsBeginStr,
t1.time_periods_end AS timePeriodsEndStr,
t1.create_time AS createTimeStr,
t1.date AS updateTimeStr
FROM
log_device_heart t1 force INDEX ( time_periods_begin_desc )
LEFT JOIN m_device t2 ON t1.device_number = t2.number
LEFT JOIN m_organization t3 ON t3.id = t2.organization_id
WHERE
1 = 1
-- and t1.device_number='MDA20122110039' --该查询性能正常
and t3.`name`='xxx医院'
ORDER BY
t1.time_periods_begin DESC
LIMIT 10;
该查询耗时0.6秒,已经较慢了.
查看查询计划,优化成了t3作为驱动表了,

那么根据情况,把,left join 改成straight_join
SELECT t3.`name` as orgName,t1.num,t1.other_num as otherNum,
t1.device_number as deviceNumber,t1.time_periods_begin as timePeriodsBeginStr,
t1.time_periods_end as timePeriodsEndStr,t1.create_time as createTimeStr,t1.date as updateTimeStr
FROM log_device_heart t1 -- use index(time_periods_begin_desc)
STRAIGHT_JOIN m_device t2
on t1.device_number=t2.number
left join m_organization t3
on t3.id=t2.organization_id
WHERE 1=1
and t1.device_number='MDA20123010006'
and t3.`name`='xxx医院'
-- and organization_id<>'10007' and organization not like '%学术机%'
order by t1.time_periods_begin desc limit 100;
改成这样子,速度变成0.009秒.
查询计划也正常了

STRAIGH_JOIN会有两个问题,本质上更类似于inner join,不过这边对我的逻辑影响不大
第二个就是当查询结果匹配不到,会奇慢无比.大概耗时10秒左右.
目前尚未有更好的解决方法.
另外新增索引后,记得再次重新优化下表结构
CREATE INDEX idx_time_number ON log_device_heart (time_periods_begin DESC,device_number);
ANALYZE TABLE log_device_heart;
记录一次MySQL多表查询,order by不走索引的情况.的更多相关文章
- 百万年薪python之路 -- MySQL数据库之 MySQL行(记录)的操作(二) -- 多表查询
MySQL行(记录)的操作(二) -- 多表查询 数据的准备 #建表 create table department( id int, name varchar(20) ); create table ...
- python 3 mysql 单表查询
python 3 mysql 单表查询 1.准备表 company.employee 员工id id int 姓名 emp_name varchar 性别 sex enum 年龄 age int 入职 ...
- python3 mysql 多表查询
python3 mysql 多表查询 一.准备表 创建二张表: company.employee company.department #建表 create table department( id ...
- MySQL优化:如何避免回表查询?什么是索引覆盖? (转)
数据库表结构: create table user ( id int primary key, name varchar(20), sex varchar(5), index(name) )engin ...
- MySQL多表查询合并结果union all,内连接查询
MySQL多表查询合并结果和内连接查询 1.使用union和union all合并两个查询结果:select 字段名 from tablename1 union select 字段名 from tab ...
- 记一次mysql多表查询(left jion)优化案例
一次mysql多表查询(left jion)优化案例 在新上线的供需模块中,发现某一个查询按钮点击后,出不来结果,找到该按钮对应sql手动执行,发现需要20-30秒才能出结果,所以服务端程序判断超时, ...
- MySQL 回表查询 & 索引覆盖优化
回表查询 先通过普通索引的值定位聚簇索引值,再通过聚簇索引的值定位行记录数据 建表示例 mysql> create table user( -> id int(10) auto_incre ...
- MySQL多表查询之外键、表连接、子查询、索引
MySQL多表查询之外键.表连接.子查询.索引 一.外键: 1.什么是外键 2.外键语法 3.外键的条件 4.添加外键 5.删除外键 1.什么是外键: 主键:是唯一标识一条记录,不能有重复的,不允许为 ...
- (转)Mysql 多表查询详解
MySQL 多表查询详解 一.前言 二.示例 三.注意事项 一.前言 上篇讲到mysql中关键字执行的顺序,只涉及了一张表:实际应用大部分情况下,查询语句都会涉及到多张表格 : 1.1 多表连接有 ...
- Mysql 单表查询-排序-分页-group by初识
Mysql 单表查询-排序-分页-group by初识 对于select 来说, 分组聚合(((group by; aggregation), 排序 (order by** ), 分页查询 (limi ...
随机推荐
- 运营也用的起来的数据分析工具:Quick BI即席分析详解
简介: 数据部门是一个容易被投诉的"高危"部门,需求响应慢.数据准确性不高会影响业务的发展. 然而数据分析师每周动辄就有几十个需求在手,无限的加班也无法解决所有问题,到底怎样才能 ...
- 记 dotnet 8.0.4 修复的 WPF 的触摸模块安全问题
本文记录 dotnet 8.0.4 版本修复的 WPF 的触摸模块安全问题,此问题影响所有的 .NET 版本,修复方法是更新 SDK 和运行时 宣布安全漏洞地址: https://github.com ...
- ES Monitoring 整理笔记
Monitoring是elastic stack的监控模块,监控信息存在es索引中,并且可以通过kibana进行可视化的展示.(收集监控数据的方式从6.5版本起由Collectors-Exporter ...
- 一键自动化博客发布工具,用过的人都说好(segmentfault篇)
segmentfault是我在这些平台中看过界面最为简洁的博客平台了. 今天就以segmentfault为例,讲讲在blog-auto-publishing-tools中的实现原理. 前提条件 前提条 ...
- docker-compose搭建的Mysql主主复制
注意下面几点: 1)要保证同步服务期间之间的网络联通.即能相互`ping`通,能使用对方授权信息连接到对方数据库(防火墙开放3306端口). 2)关闭selinux. 3)同步前,双方数据库中需要同步 ...
- FileInputStream和FileOutputStream
FileInputstream 字节输入流 用于文件内容的读取操作. 创建FileInputstream对象用于读取文件内容,使用后需要进行关闭操作 常用方法: read(); //每次仅读取一个字节 ...
- 原生微信小程序button去掉边框
直接改没反应,需要使用::after更改
- Hugging Face 与 Wiz Research 合作提高人工智能安全性
我们很高兴地宣布,我们正在与 Wiz 合作,目标是提高我们平台和整个 AI/ML 生态系统的安全性. Wiz 研究人员 与 Hugging Face 就我们平台的安全性进行合作并分享了他们的发现. W ...
- 通过 OpenAPI 部署 Nbsf_Management API Service
目录 文章目录 目录 准备 部署 启动 API 服务 调用 准备 GentOS7 Golang1.12.5 Swagger YAML TS29521_Nbsf_Management.yaml TS29 ...
- Axure和墨刀——两款原型设计工具介绍
Axure与墨刀是两款在原型设计领域广受欢迎的工具,各具特点和优势: Axure: Axure RP是一款功能强大的原型设计工具,广泛应用于交互设计和用户体验设计领域.它提供了丰富的交互元素库.高保真 ...