专题描述 详细说明使用覆盖索引减少查询字段大小和分页作用的SQL语句优化方法。
问题现象 1

PPS项目中通过处理慢查询记录,发现同类SQL语句,特征为查询字段较多

,WHERE子句中使用limit分页查询。

2 示例:

SELECT this_.id as id1_8_0_, this_.acc_period as acc_peri2_8_0_, 

this_.acc_rcv as acc_rcv3_8_0_, this_.acount_invoice_type as acount_i4_8_0_, this_.acount_pay_mode as acount_p5_8_0_, this_.activedate as activeda6_8_0_, this_.address as address7_8_0_, this_.b.........

FROM cdh_cust_info this_ 
WHERE (this_.shortname LIKE '%dff%' or this_.fullname LIKE '%dff%') 
limit 100,20;
分析过程 1 该SQL具有特征:主查询中读取字段多,需要减少匹配内容字段和长度。
2 利用索引,可以只在索引内查询获取主键值,通过主键值定位需要查询的字段。
解决方案 1 将原语句修改为如下形式:
SELECT ......
FROM cdh_cust_info this_ 
WHERE EXISTS
(
  SELECT 1 
  FROM cdh_cust_info cci 
  WHERE (cci.shortname LIKE '%dff%' or cci.fullname LIKE '%dff%' ) 
  AND cci.id=this_.id 
 LIMIT 1
) LIMIT 100,20;
2

建立联合索引:ALTER TABLE cdh_cust_info ADD INDEX

 idx_shortname_fullname(short_name,fullname);

3 主查询语句通过主键id匹配唯一记录获取查询字段。
知识点 1 索引覆盖指select的数据列只用从索引中就能够取得,不必读取数据行。
2 示例表结构定义如下,
root@localhost : employees:43: > desc employees;
+------------+---------------+------+-----+---------+-------+
| Field      | Type          | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| emp_no     | int(11)       | NO   | PRI | NULL    |       |
| birth_date | date          | NO   |     | NULL    |       |
| first_name | varchar(14)   | NO   |     | NULL    |       |
| last_name  | varchar(16)   | NO   |     | NULL    |       |
| gender     | enum('M','F') | NO   |     | NULL    |       |
| hire_date  | date          | NO   |     | NULL    |       |
+------------+---------------+------+-----+---------+-------+
6 rows in set (0.00 sec)
3 explain select first_name, last_name, emp_no,birth_date from employees where(first_name like '%ef%' )limit 20;
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | employees | 
ALL  | NULL          | NULL | NULL    | NULL | 298847 | Using where |
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
1 row in set (0.00 sec)
4 explain select first_name, last_name, emp_no from employees where(first_name like '%ef%' )limit 20;
+----+-------------+-----------+-------+---------------+------------------------+---------+------+--------+--------------------------+
| id | select_type | table     | type  | possible_keys | key                    | key_len | ref  | rows   | Extra                    |
+----+-------------+-----------+-------+---------------+------------------------+---------+------+--------+--------------------------+
|  1 | SIMPLE      | employees | 
index | NULL          | idx_firstname_lastname | 94      | NULL | 298847 | Using where; Using index |
+----+-------------+-----------+-------+---------------+------------------------+---------+------+--------+--------------------------+
1 row in set (0.00 sec)
5

假设数据库中该表有N行,limit限制条件为 limit M,L

,未优化SQL需要全表扫描直到获取L行为止,最大扫描范围为:查询所有字段长度*N。

6

优化之后,需要扫描数据行范围因为在索引中取出主键键值为L行

,假设扫描索引行Q可获取满足模糊匹配结果,

索引扫描范围为(short_name+full_name+id)*Q+L行主键ID回表读操作

7 执行计划中Extra列Using index意为索引覆盖。
8 二级索引中包含主键的键值。
9 通过主键可以匹配到唯一记录。
其他建议

建立索引:alter table employees

 add index idx_firstname_lastname(first_name,last_name);