前言 最近,开发人员需要定期的删除表里一定时间以前的数据,SQL如下: mysql > delete from testtable WHERE biz_date <= '2017-08-21 00:00:00' AND status = 2 limit 500\G 前段时间在优化的时候,我们已经在相应的查询条件上加上了索引,如下:
表上的字段既然都有索引,那么按照之前的文章分析,是两个字段都可以走上索引的。 既然能够利用索引,表的总大小也就是200M左右,那么为什么形成了慢查呢? mysql > desc select * from testtable WHERE biz_date <= '2017-08-21 00:00:00'; ---- ------------- ----------- ------ ---------------- ------ --------- ------ -------- ------------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | ---- ------------- ----------- ------ ---------------- ------ --------- ------ -------- ------------- | 1 | SIMPLE | testtable | ALL | idx_bizdate_st | NULL | NULL | NULL | 980626 | Using where | ---- ------------- ----------- ------ ---------------- ------ --------- ------ -------- ------------- 1 row in set (0.00 sec)
-- 只查询biz_date -- 关键点:rows:980626;type:ALL
mysql > desc select * from testtable WHERE biz_date <= '2017-08-21 00:00:00' and status = 2; ---- ------------- ----------- ------ ---------------- ------ --------- ------ -------- ------------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | ---- ------------- ----------- ------ ---------------- ------ --------- ------ -------- ------------- | 1 | SIMPLE | testtable | ALL | idx_bizdate_st | NULL | NULL | NULL | 980632 | Using where | ---- ------------- ----------- ------ ---------------- ------ --------- ------ -------- ------------- 1 row in set (0.00 sec)
-- 查询biz_date status -- 关键点:rows:980632;type:ALL
mysql > desc select * from testtable WHERE biz_date <= '2017-08-21 00:00:00' and status = 2 limit 100; ---- ------------- ----------- ------- ---------------- ---------------- --------- ------ -------- ----------------------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | ---- ------------- ----------- ------- ---------------- ---------------- --------- ------ -------- ----------------------- | 1 | SIMPLE | testtable | range | idx_bizdate_st | idx_bizdate_st | 6 | NULL | 490319 | Using index condition | ---- ------------- ----------- ------- ---------------- ---------------- --------- ------ -------- ----------------------- 1 row in set (0.00 sec)
-- 查询biz_date status limit -- 关键点:rows:490319;
mysql > select count(*) from testtable WHERE biz_date <= '2017-08-21 00:00:00' and status = 2; ---------- | count(*) | ---------- | 0 | ---------- 1 row in set (0.34 sec)
mysql > select count(*) from testtable WHERE biz_date <= '2017-08-21 00:00:00'; ---------- | count(*) | ---------- | 970183 | ---------- 1 row in set (0.33 sec)
mysql > select count(*) from testtable; ---------- | count(*) | ---------- | 991421 | ---------- 1 row in set (0.19 sec)
mysql > select distinct biz_status from testtable; ------------ | biz_status | ------------ | 1 | | 2 | | 4 | ------------
我们发现,强制指定索引后,查询耗时和没有强制索引比较,的确执行速度快了很多,因为没有强制索引是全表扫描嘛!但是!依然非常慢! 并且业务逻辑上是定期删除一定日期之前的数据。所以逻辑上来说,每次删除都是只删除一天的数据,直接让SQL扫描一天的范围。那么我们就可以改写SQL啦! mysql > select * from testtable WHERE biz_date >= '2017-08-20 00:00:00' and biz_date <= '2017-08-21 00:00:00' and status = 2; Empty set (0.00 sec)
mysql > desc select * from testtable WHERE biz_date >= '2017-08-20 00:00:00' and biz_date <= '2017-08-21 00:00:00' and status = 2; ---- ------------- ------------------ ------- ---------------- ---------------- --------- ------ ------ ----------------------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | ---- ------------- ------------------ ------- ---------------- ---------------- --------- ------ ------ ----------------------- | 1 | SIMPLE | testtable | range | idx_bizdate_st | idx_bizdate_st | 6 | NULL | 789 | Using index condition | ---- ------------- ------------------ ------- ---------------- ---------------- --------- ------ ------ ----------------------- 1 row in set (0.00 sec)
-- rows降低了很多,乖乖的走了索引
mysql > desc select * from testtable WHERE biz_date >= '2017-08-20 00:00:00' and biz_date <= '2017-08-21 00:00:00' ; ---- ------------- ------------------ ------- ---------------- ---------------- --------- ------ ------ ----------------------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | ---- ------------- ------------------ ------- ---------------- ---------------- --------- ------ ------ ----------------------- | 1 | SIMPLE | testtable | range | idx_bizdate_st | idx_bizdate_st | 5 | NULL | 1318 | Using index condition | ---- ------------- ------------------ ------- ---------------- ---------------- --------- ------ ------ ----------------------- 1 row in set (0.00 sec)
-- 即使没有status,也是肯定走索引啦 这个问题,我原本打算用hint,强制让他走索引,但是实际上强制走索引的执行时间并不能带来满意的效果。结合业务逻辑,来优化SQL,是最好的方式,也是终极法宝,一定要好好利用。不了解业务的DBA,不是一个好DBA... |
|
来自: liang1234_ > 《数据库性能优化》