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

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. Paillier半同态加密:原理、高效实现方法和应用

    ​简介: <数据安全法>已于9月1日起正式实施,两个月后<个人信息保护法>也将开始施行,意味着数据安全和隐私保护方面的监管将会在年内陆续到位.在合规收紧大背景下,"数 ...

  2. clickhouse数据操常见执行语句

    1.清空本地表数据 truncate table 数据库名.表名 :) select * from test_local; SELECT * FROM test_local Query id: ab1 ...

  3. 01二分 [AGC006D] Median Pyramid Hard + P2824 [HEOI2016/TJOI2016] 排序

    [AGC006D] Median Pyramid Hard 考虑对于一个长度为 2n + 1 的 01 序列 b 如何快速确定堆顶元素. _ _ _ _ x _ _ _ 0 x _ _ 0 0 x _ ...

  4. gprMax项目代码分解:gprMax.py

    目录 1. 引言 2. 代码分解 (1) 导入模块 (2)main()函数 (3)run_main()函数 (4)run_std_sim()函数 (5)run_benchmark_sim()函数 (6 ...

  5. 更新package.json里所有模块

    安装该插件 cnpm install -g npm-check-updates 或者 npm install -g npm-check-updates 在有package.json的目录执行 npm- ...

  6. 移动端termux安装kali

    1.相关准备一部安卓手机,termux,NVAC,浏览器2.安装kali首先进入kali的官网选择文档找到Android手机上的kali找到NetHunter-Rootless找到kali安装命令:t ...

  7. 4G EPS 第四代移动通信系统

    目录 文章目录 目录 4G EPS 4G EPS 4G(the 4th generation mobile communication technology,第四代移动通信技术)提供了 3G 不能满足 ...

  8. pageoffice6 版本实现在线打开word 文件禁止保存、禁止另存、禁止打印的需求

    在实际项目需求中,有时需要限制用户的保存.另存.打印文件操作,实现此效果只需在OnPageOfficeCtrlInit或AfterDocumentOpened事件中调用js设置PageOffice控件 ...

  9. sql 常用手册

    根据code 分组然后取出每组中的任意一行 SELECT *from( SELECT *, row_number () OVER ( partition BY code ORDER BY code D ...

  10. Js实现任意位置缩放图片,深入理解背后原理

    前言 本文将用一个简单的例子详细讲解如何用原生JS一步步实现完整的任意位置缩放图片功能,无任何第三方依赖,指针事件 进行多端统一的事件监听,干货满满. 完整代码 为提升阅读体验,正文中代码展示有部分省 ...