select count(*) from arrival_record where product_id=26 and receive_time between '2019-03-25 14:00:00' and '2019-03-25 15:00:00' and receive_spend_ms>=0\G
select arrival_record 语句在 MySQL 中最多扫描的行数为 5600 万、平均扫描的行数为 172 万,推断由于扫描的行数多导致的执行时间长。
查看执行计划:
explain select count(*) from arrival_record where product_id=26 and receive_time between '2019-03-25 14:00:00' and '2019-03-25 15:00:00' and receive_spend_ms>=0\G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table: arrival_record partitions: NULL type: ref possible_keys: IXFK_arrival_record key: IXFK_arrival_record key_len: 8 ref: const rows: 32261320 filtered: 3.70 Extra: Using index condition; Using where 1 row in set, 1 warning (0.00 sec)
IFS_OLD=$IFS IFS=$'\n' for i in cat /tmp/select_arri.log ;do echo ${i#*'from'}; done | less IFS=$IFS_OLD
arrival_record arrivalrec0_ where arrivalrec0_.sequence=
'2019-03-27 08:40' and arrivalrec0_.product_id=17 and arrivalrec0_.station_no='56742' arrival_record arrivalrec0_ where arrivalrec0_.sequence='2019-03-27 08:40' and arrivalrec0_.product_id=22 and arrivalrec0_.station_no='S7100' arrival_record arrivalrec0_ where arrivalrec0_.sequence='2019-03-27 08:40' and arrivalrec0_.product_id=24 and arrivalrec0_.station_no='V4631' arrival_record arrivalrec0_ where arrivalrec0_.sequence='2019-03-27 08:40' and arrivalrec0_.product_id=22 and arrivalrec0_.station_no='S9466' arrival_record arrivalrec0_ where arrivalrec0_.sequence='2019-03-27 08:40' and arrivalrec0_.product_id=24 and arrivalrec0_.station_no='V4205' arrival_record arrivalrec0_ where arrivalrec0_.sequence='2019-03-27 08:40' and arrivalrec0_.product_id=24 and arrivalrec0_.station_no='V4105' arrival_record arrivalrec0_ where arrivalrec0_.sequence='2019-03-27 08:40' and arrivalrec0_.product_id=24 and arrivalrec0_.station_no='V4506' arrival_record arrivalrec0_ where arrivalrec0_.sequence='2019-03-27 08:40' and arrivalrec0_.product_id=24 and arrivalrec0_.station_no='V4617' arrival_record arrivalrec0_ where arrivalrec0_.sequence='2019-03-27 08:40' and arrivalrec0_.product_id=22 and arrivalrec0_.station_no='S8356' arrival_record arrivalrec0_ where arrivalrec0_.sequence='2019-03-27 08:40' and arrivalrec0_.product_id=22 and arrivalrec0_.station_no='S8356'
select 该表 where 条件中有 product_id,station_no,sequence 字段,可以使用到复合索引 IXFK_arrival_record 的前三个字段。
综上所示,优化方法为:
删除复合索引 IXFK_arrival_record
建立复合索引 idx_sequence_station_no_product_id
建立单独索引 indx_receive_time
delete xxx_record 语句
该delete 操作平均扫描行数为 1.1 亿行,平均执行时间是 262s。
delete 语句如下所示,每次记录的慢查询传入的参数值不一样:
delete from arrival_record where receive_time '2019-02-23', '%Y-%m-%d')\G
执行计划:
explain select * from arrival_record where receive_time '2019-02-23', '%Y-%m-%d')\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: arrival_record partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 109501508 filtered: 33.33 Extra: Using where 1 row in set, 1 warning (0.00 sec)
该delete 语句没有使用索引(没有合适的索引可用),走的全表扫描,导致执行时间长。
优化方法也是:建立单独索引 indx_receive_time(receive_time)。
测试
拷贝arrival_record 表到测试实例上进行删除重新索引操作。
XX实例 arrival_record 表信息:
du -sh /datas/mysql/data/3316/cq_new_cimiss/arrival_record* 12K /datas/mysql/data/3316/cq_new_cimiss/arrival_record.frm 48G /datas/mysql/data/3316/cq_new_cimiss/arrival_record.ibd
2019-04-08-11:17:36 stop slave mysql: [Warning] Using a password on the command line interfacecanbeinsecure. ddl_start 2019-04-08 11:17:36 ddl_stop 2019-04-08 11:45:13 2019-04-08-11:45:13 onlieddlstop 2019-04-08-11:45:13 addforeignkey mysql: [Warning] Usingapasswordonthecommandlineinterfacecanbeinsecure. 2019-04-08-12:33:48 addforeignkeystop 2019-04-08-12:33:48 startslave
删除重建索引花费时间为 28 分钟,添加外键约束时间为 48 分钟。
再次查看 delete 和 select 语句的执行计划:
explain select count(*) from arrival_record where receive_time STR_TO_DATE('2019-03-10', '%Y-%m-%d')\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: arrival_record partitions: NULL type: range possible_keys: idx_receive_time key: idx_receive_time key_len: 6 ref: NULL rows: 7540948 filtered: 100.00 Extra: Using where; Using index
explain select count(*) from arrival_record where product_id=26 and receive_time between '2019-03-25 14:00:00' and '2019-03-25 15:00:00' and receive_spend_ms>=0\G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table: arrival_record partitions: NULL type: range possible_keys: idx_product_id_sequence_station_no,idx_receive_time key: idx_receive_time key_len: 6 ref: NULL rows: 291448 filtered: 16.66 Extra: Using index condition; Using where
都使用到了 idx_receive_time 索引,扫描的行数大大降低。
索引优化后
delete 还是花费了 77s 时间:
delete from arrival_record where receive_time '2019-03-10', '%Y-%m-%d')\G
#得到满足时间条件的最大主键ID #通过按照主键的顺序去 顺序扫描小批量删除数据 #先执行一次以下语句 SELECT MAX(id) INTO @need_delete_max_id FROM arrival_record WHERE receive_time`; DELETE FROM arrival_record WHERE id<@need_delete_max_id LIMIT 20000; select ROW_COUNT(); #返回20000 #执行小批量delete后会返回row_count(), 删除的行数 #程序判断返回的row_count()是否为0,不为0执行以下循环,为0退出循环,删除操作完成 DELETE FROM arrival_record WHERE id<@need_delete_max_id LIMIT 20000; select ROW_COUNT(); #程序睡眠0.5s