首先是表結構,部分字段脫敏已刪除
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;