有一张数据表,表数据现在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秒。
下面我们相办法把这个查询时间尽可能缩短。
其实我这里需要获取的只有时间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获取整表的所有数据,然后部分数据竟然要比获取所有数据要快。
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;
为数据表增加一个组合索引:
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;
这样就使用到我们建立的索引了。
查询效率也得到了大大的改善。