一次MySQL的優化之旅

一、問題

有一張數據表,表數據現在200W條左右。表結構如下:

CREATE TABLE `device_desk` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `running_number` varchar(45) DEFAULT NULL COMMENT '流水號',
  `time` timestamp NULL DEFAULT NULL COMMENT '時間',
  `temperature` double DEFAULT NULL COMMENT '溫度',
  `humidity` double DEFAULT NULL COMMENT '溼度',
  `body_infrared` int(11) DEFAULT NULL COMMENT '人體紅外',
  `particulate_matter` int(11) DEFAULT NULL COMMENT 'PM2.5',
  `air_quality` int(11) DEFAULT NULL COMMENT '空氣質量',
  `brightness` int(11) DEFAULT NULL COMMENT '燈光亮度',
  `device_id` int(11) DEFAULT NULL COMMENT '設備編號',
  `phone` varchar(45) DEFAULT NULL COMMENT '手機號碼',
  `origin` varchar(100) DEFAULT NULL COMMENT '原始值',
  `color` varchar(45) DEFAULT NULL COMMENT '顏色(用於自定義備註)',
  `remark` varchar(45) DEFAULT NULL COMMENT '備註'
  PRIMARY KEY (`id`),
  KEY `fk_device_idx` (`device_id`),
  KEY `index_common_2` (`body_infrared`,`day`,`device_id`,`phone`,`time`),
  CONSTRAINT `fk_device2` FOREIGN KEY (`device_id`) REFERENCES `device` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=2123065 DEFAULT CHARSET=utf8 COMMENT='桌子數據';

業務:
需要找出手機號碼爲13800000000,綁定的設備編號爲164,在2015年11月26日有人在的時間段。body_infrared字段0表示無人,1、16、17都表示有人。

查詢語句爲:

select *  from device_desk t 
where t.body_infrared>0
and t.phone='13800000000'
and t.device_id=164
and t.time like '2015-11-25%' 
order by t.time;

得到查詢結果所需耗時爲83秒。
這裏寫圖片描述

下面我們相辦法把這個查詢時間儘可能縮短。

二、優化過程

1、使用精確的字段代替*

其實我這裏需要獲取的只有時間time,備註remark,和顏色color。

select  t.time,t.remark,t.color from device_desk t 
where t.body_infrared>0
and t.phone='13800000000'
and t.device_id=164
and t.time like '2015-11-25%' 
order by t.time;

特別是使用Hibernate作爲ORM中間件的朋友,會習慣性地使用HQL獲取整表的所有數據,然後部分數據竟然要比獲取所有數據要快。

2、優化時間的模糊查詢

timestamp 類型的字段可以通過類字符串比較的方式來作爲模糊查詢的條件。而MySQL對於like %關鍵字作爲查詢條件是全表掃描的,則沒法使用索引。因爲我要查詢的是當天(某天)的數據,因爲我爲這張表增加一個day字段,用於保存每天的日期。

增加day字段:

ALTER TABLE `time_table`.`device_desk` 
ADD COLUMN `day` VARCHAR(45) NULL COMMENT '日期' AFTER `remark`;

爲原有的數據的day字段賦值:

update device_desk t set t.day= DATE_FORMAT(`t`.`time`, '%Y-%m-%d') where t.day is null; 

這個操作涉及數據比較多,執行過程會比較長。

更新查詢語句爲:

select  t.time,t.remark,t.color from device_desk t 
where t.body_infrared>0
and t.phone='13800000000'
and t.device_id=164
and t.day='2015-11-25' 
order by t.time;

3、建立索引

爲數據表增加一個組合索引:

ALTER TABLE `time_table`.`device_desk` 
ADD INDEX `index_common_2` (`body_infrared` ASC, `day` DESC, `device_id` ASC, `phone` ASC, `time` DESC);

索引建立成功後,執行查詢語句的時候發生需要的時間還是很長。
查看一下查詢語句的執行過程:

explain select  t.time,t.remark,t.color from device_desk t 
where t.body_infrared>0
and t.phone='13800000000'
and t.device_id=164
and t.day='2015-11-25' 
order by t.time;

結果:
這裏寫圖片描述

這裏發現我們的select語句並沒有使用到我們建立的索引。
經過一番度娘谷哥後,發現MySQL的where條件中用到大於或者小於時,也是進行全表掃描,是不會使用索引查詢的。所以把這個大於查詢更換掉就好。

explain select t.time,t.remark,t.color  from device_desk t  where (t.body_infrared=1 or t.body_infrared=16 or t.body_infrared=17)
and t.day= '2015-11-25' 
and t.device_id=164
and t.phone='13800000000'
order by t.time;

這裏寫圖片描述

這樣就使用到我們建立的索引了。

這裏寫圖片描述

查詢效率也得到了大大的改善。

發佈了146 篇原創文章 · 獲贊 130 · 訪問量 78萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章