SQL优化案例


摘要:记录一些日常工作中优化过的案例

表字段的字符集和排序规则不一致引起索引失效

有个朋友说他有个SQL,两表join的字段都有索引的,为什么把left join两边的表换下顺序后,执行计划相差甚大。他实际查询耗时第一个是0.03秒,第二个是60秒。执行计划如下:
SQL1
SQL2
进一步查看执行计划扩展信息:
SQL3
为什么无法使用索引呢?随后问他要来了表结构,看出了问题所在:
SQL4
随即修改其中一个字符集和另一个一致即可。执行计划正常:
SQL5
SQL6

创建联合索引的顺序不合理引起的SQL查询慢

表结构

CREATE TABLE `m_stats_base` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`yearmonth` varchar(8) COLLATE utf8_bin DEFAULT NULL,
`school_id` varchar(32) COLLATE utf8_bin DEFAULT NULL,
`school_name` varchar(100) COLLATE utf8_bin DEFAULT NULL,
`user_id` varchar(32) COLLATE utf8_bin NOT NULL,
`real_name` varchar(32) COLLATE utf8_bin DEFAULT NULL,
`identity_id` varchar(32) COLLATE utf8_bin DEFAULT NULL,
`icon` varchar(64) COLLATE utf8_bin DEFAULT '0',
`add_time` datetime not null DEFAULT '1971-01-01',
... ...
PRIMARY KEY (`id`),
KEY `idx_addtime` (`add_time`,`identity_id`)
) ENGINE=InnoDB AUTO_INCREMENT=8617729 DEFAULT CHARSET=utf8 COLLATE=utf8_bin

开发反馈该SQL查询耗时:3秒

mysql> explain select COUNT(*) from m_stats_base where add_time < '2018-01-01' and identity_id='TEACHER';
+----+-------------+--------------+-------+---------------+-------------+---------+------+---------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+-------+---------------+-------------+---------+------+---------+--------------------------+
| 1 | SIMPLE | m_stats_base | range | idx_addtime | idx_addtime | 6 | NULL | 3003236 | Using where; Using index |
+----+-------------+--------------+-------+---------------+-------------+---------+------+---------+--------------------------+
1 row in set (0.00 sec)
mysql> select COUNT(*) from m_stats_base where add_time < '2018-01-01' and identity_id='TEACHER';
+----------+
| COUNT(*) |
+----------+
| 627038 |
+----------+
1 row in set (3.00 sec)

解决办法:调整联合索引字段顺序,将等值字段放前面范围字段放后面

mysql> alter table m_stats_base add key idx_identifityid_addtime(identity_id,add_time),drop KEY `idx_addtime`;
Query OK, 0 rows affected (46.23 sec)
Records: 0 Duplicates: 0 Warnings: 0
查看执行计划,可看到扫描的数据从300多W减少到120W,实际查询耗时:1秒内
mysql> explain select COUNT(*) from m_stats_base where add_time < '2018-01-01' and identity_id='TEACHER';
+----+-------------+--------------+-------+--------------------------+--------------------------+---------+------+---------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+-------+--------------------------+--------------------------+---------+------+---------+--------------------------+
| 1 | SIMPLE | m_stats_base | range | idx_identifityid_addtime | idx_identifityid_addtime | 105 | NULL | 1224698 | Using where; Using index |
+----+-------------+--------------+-------+--------------------------+--------------------------+---------+------+---------+--------------------------+
1 row in set (0.00 sec)
mysql> select COUNT(*) from m_stats_base where identity_id='TEACHER' and add_time < '2018-01-01';
+----------+
| COUNT(*) |
+----------+
| 627038 |
+----------+
1 row in set (0.97 sec)

当where条件中有范围条件和等值条件组合的时候,mysql会将SQL改写改写为等值条件放前面,范围条件放后面:

mysql> explain extended select COUNT(*) from m_stats_base where add_time < '2018-01-01' and identity_id='TEACHER';
+----+-------------+--------------+-------+--------------------------+--------------------------+---------+------+---------+----------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+-------+--------------------------+--------------------------+---------+------+---------+----------+--------------------------+
| 1 | SIMPLE | m_stats_base | range | idx_identifityid_addtime | idx_identifityid_addtime | 105 | NULL | 1224698 | 100.00 | Using where; Using index |
+----+-------------+--------------+-------+--------------------------+--------------------------+---------+------+---------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> show warnings;
+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select count(0) AS `COUNT(*)` from `xueledb`.`m_stats_base` where ((`xueledb`.`m_stats_base`.`identity_id` = 'TEACHER') and (`xueledb`.`m_stats_base`.`add_time` < '2018-01-01')) |
+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

文章目录
  1. 1. 表字段的字符集和排序规则不一致引起索引失效
  2. 2. 创建联合索引的顺序不合理引起的SQL查询慢
|