首先是表结构,部分字段脱敏已删除

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不走索引的情况.的更多相关文章

  1. 百万年薪python之路 -- MySQL数据库之 MySQL行(记录)的操作(二) -- 多表查询

    MySQL行(记录)的操作(二) -- 多表查询 数据的准备 #建表 create table department( id int, name varchar(20) ); create table ...

  2. python 3 mysql 单表查询

    python 3 mysql 单表查询 1.准备表 company.employee 员工id id int 姓名 emp_name varchar 性别 sex enum 年龄 age int 入职 ...

  3. python3 mysql 多表查询

    python3 mysql 多表查询 一.准备表 创建二张表: company.employee company.department #建表 create table department( id ...

  4. MySQL优化:如何避免回表查询?什么是索引覆盖? (转)

    数据库表结构: create table user ( id int primary key, name varchar(20), sex varchar(5), index(name) )engin ...

  5. MySQL多表查询合并结果union all,内连接查询

    MySQL多表查询合并结果和内连接查询 1.使用union和union all合并两个查询结果:select 字段名 from tablename1 union select 字段名 from tab ...

  6. 记一次mysql多表查询(left jion)优化案例

    一次mysql多表查询(left jion)优化案例 在新上线的供需模块中,发现某一个查询按钮点击后,出不来结果,找到该按钮对应sql手动执行,发现需要20-30秒才能出结果,所以服务端程序判断超时, ...

  7. MySQL 回表查询 & 索引覆盖优化

    回表查询 先通过普通索引的值定位聚簇索引值,再通过聚簇索引的值定位行记录数据 建表示例 mysql> create table user( -> id int(10) auto_incre ...

  8. MySQL多表查询之外键、表连接、子查询、索引

    MySQL多表查询之外键.表连接.子查询.索引 一.外键: 1.什么是外键 2.外键语法 3.外键的条件 4.添加外键 5.删除外键 1.什么是外键: 主键:是唯一标识一条记录,不能有重复的,不允许为 ...

  9. (转)Mysql 多表查询详解

    MySQL 多表查询详解 一.前言  二.示例 三.注意事项 一.前言  上篇讲到mysql中关键字执行的顺序,只涉及了一张表:实际应用大部分情况下,查询语句都会涉及到多张表格 : 1.1 多表连接有 ...

  10. Mysql 单表查询-排序-分页-group by初识

    Mysql 单表查询-排序-分页-group by初识 对于select 来说, 分组聚合(((group by; aggregation), 排序 (order by** ), 分页查询 (limi ...

随机推荐

  1. 在.Net中操作redis

    在.Net中操作redis 一.环境 .Net 7 redis 7.2.4 二.所需类包 StackExchange.Redis 三.连接redis信息 appsettings.json配置redis ...

  2. 从零开始:Django项目的创建与配置指南

    title: 从零开始:Django项目的创建与配置指南 date: 2024/5/2 18:29:33 updated: 2024/5/2 18:29:33 categories: 后端开发 tag ...

  3. pde复习笔记 第一章 波动方程 第六节 能量不等式、波动方程解的唯一性和稳定性

    能量不等式 这一部分需要知道的是能量的表达式 \[E(t)=\int_{0}^{l}u_{t}^{2}+a^{2}u_{x}^{2} dx \] 一般而言题目常见的问法是证明能量是减少的,也就是我们需 ...

  4. flask入门 快速入门后台写接口【API】【Python3】【无前端】【json格式】

    目录 新建项目 虚拟环境 安装flask插件包 新建hello_world.py debug调适 四.flask应用 flask路由 变量规则 唯一的 URL / 重定向行为 flask重定向 JSO ...

  5. nginx与location规则

    ========================================================================= 2018年3月28日 记录: location = ...

  6. gin框架对接快递100 查询快递跟踪记录 Golang实现快递查询

    参考ui效果: https://www.kuaidi100.com/?from=openv gin框架: 请求地址 http://localhost:8822/kd100/auto_com_num?n ...

  7. 【项目学习】Anchor:一种提供稳定币存款低波动收益率的去中心化的储蓄协议

    简介 基于稳定币的获利产品. 贷方人放出稳定币以供借款.借方通过抵押资产(base asset)的方式,以低于协议定义的借贷比率借入稳定币.Anchor 协议使用抵押资产进行质押以获得奖励,然后将质押 ...

  8. 【译】使用 GitHub Copilot 作为你的编码 GPS

    GitHub Copilot 是一个改变游戏规则的人工智能助手,可以彻底改变您在 Visual Studio 中的编码流程.在我们的视频系列中,Bruno  Capuano 探讨了这个智能编码伙伴如何 ...

  9. ❤️‍🔥 Solon Cloud Event 新的事务特性与应用

    1.Solon Cloud Event? 是 Solon 分布式事件总线的解决方案.也是 Solon "最终一致性"分布式事务的解决方案之一 2.事务特性 事务?就是要求 Even ...

  10. Python重试任务模块tenacity

    软硬件环境 windows 11 64bits python 3.6 tenacity 简介 在实际应用中,经常会碰到在web请求时,因为网络的不稳定,会有请求超时的问题,这时候,一般都是自己去实现重 ...