記錄一次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;

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章