您的位置:首页 > 数据库 > MySQL

一次MySQL的优化之旅

2015-11-26 14:11 781 查看

一、问题

有一张数据表,表数据现在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;




这样就使用到我们建立的索引了。



查询效率也得到了大大的改善。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息