一、索引基本知识 1、索引的优点
- 很大程度上减少服务器扫描的数据量
- 很大程度上避免服务器排序和临时表
- 将随机IO变成顺序IO
- 使用索引列可以快速查找Where条件的行数据
mysql> explain select * from emp where empno = 7469;
+----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key| key_len | ref| rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
|1 | SIMPLE| emp| NULL| ref| pk_emp_no| pk_emp_no | 4| const |1 |100.00 | NULL|
+----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
- 假如存在多个索引,mysql优化器会使用最少行的索引。
mysql> explain select * from emp where emp.empno = 1 and job >1;
+----+-------------+-------+------------+------+------------------+-----------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys| key| key_len | ref| rows | filtered | Extra|
+----+-------------+-------+------------+------+------------------+-----------+---------+-------+------+----------+-------------+
|1 | SIMPLE| emp| NULL| ref| uk_job,pk_emp_no | pk_emp_no | 4| const |1 |100.00 | Using where |
+----+-------------+-------+------------+------+------------------+-----------+---------+-------+------+----------+-------------+
1 row in set, 2 warnings (0.00 sec)
- 假如存在多个索引,则优化器可以使用索引的任何最左前缀来查找行数据。
- 当表有连接的时候,从其他表检索行数据。
- 索引树是有排序机制的,所有能很快找到min或max值。
- 如果排序或分组时在可以用索引的最左前缀上完成的,则对表进行排序和分组。
- 在某些情况下,可以优化查询以检索值而无需查询数据行。
- 主键索引
- 唯一索引
- 普通索引
- 全文索引
- 组合索引
where语句 | 索引是否发挥作用 |
where a=4 | 只使用a列索引 |
where a=4 and b = 5 | 只使用a、b列 |
where a=4 and b=5 and c=6 | 使用了a、b、c列 |
where b=3 or c=6 | 没使用 |
where a=4 and c=6 | 只使用a |
where a=4 and b>5 and c=6 | 只使用了a、b列 |
where a=4 and b like '%5%' and c=6 | 只使用了a列 |
- 哈希表
优点:查询速度快
缺点:需要将数据文件放到内存中,比较吃内存空间。由于他是通过散列算法计算后存储数据,故不支持排序,范围查询,所以实际工作中很少使用。根据Mysql官网描述,使用哈希索引只在Memory存储引擎中。
- B+树
B+树示意图:

文章图片
下面我们分别对InnoDB、MyISAM引擎分析数据机构。
InnoDB引擎是聚簇索引,它将索引和数据保存在一个文件里面,可以直接根据主键扫描叶子节点中的数据,最大限度提高了IO密集型应用的性能。更新数据成本相对高点,因为它会强制将每个被更新的行移动到新的位置,当主键被更新导致需要移动时,可能面临叶分裂的问题。

文章图片
MyISAM引擎是非聚簇索引,它的数据文件、索引文件是分开存放的。

文章图片
5、索引匹配方式
- 全值匹配
/*
全值匹配指的是和所有的索引列进行匹配
*/
mysql> explain select * from staffs where names = 'July' and age = '23' and pos = 'dev';
+----+-------------+--------+------------+------+---------------+---------+---------+-------------------+------+----------+-------+
| id | select_type | table| partitions | type | possible_keys | key| key_len | ref| rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+---------+---------+-------------------+------+----------+-------+
|1 | SIMPLE| staffs | NULL| ref| idx_nap| idx_nap | 140| const,const,const |1 |100.00 | NULL|
+----+-------------+--------+------------+------+---------------+---------+---------+-------------------+------+----------+-------+
1 row in set, 1 warning (0.03 sec)
- 最左前缀匹配
/*
最左前缀匹配,只匹配前面的几列,如果
*/
mysql> explain select * from staffs where names = 'July' and age > 25;
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------+
| id | select_type | table| partitions | type| possible_keys | key| key_len | ref| rows | filtered | Extra|
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------+
|1 | SIMPLE| staffs | NULL| range | idx_nap| idx_nap | 78| NULL |1 |100.00 | Using index condition |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)mysql> explain select * from staffs where names >1 and age =22 and pos = '111';
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table| partitions | type | possible_keys | key| key_len | ref| rows | filtered | Extra|
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
|1 | SIMPLE| staffs | NULL| ALL| idx_nap| NULL | NULL| NULL |1 |100.00 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 2 warnings (0.00 sec)
- 列前缀匹配
/*
列前缀匹配,匹配索引列值的前部分字符
*/
mysql> explain select * from staffs where names like 'J%';
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------+
| id | select_type | table| partitions | type| possible_keys | key| key_len | ref| rows | filtered | Extra|
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------+
|1 | SIMPLE| staffs | NULL| range | idx_nap| idx_nap | 74| NULL |1 |100.00 | Using index condition |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.01 sec)
- 范围值匹配
/*
范围值匹配,可以匹配索引列某个范围的数据。
*/
mysql> explain select * from staffs where id > 1;
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table| partitions | type| possible_keys | key| key_len | ref| rows | filtered | Extra|
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|1 | SIMPLE| staffs | NULL| range | PRIMARY| PRIMARY | 4| NULL |1 |100.00 | Using where |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
- 精确匹配
/*
精确匹配,根据索引列,精确指定一个数值进行筛选。
*/
mysql> explain select * from staffs where names = '1';
+----+-------------+--------+------------+------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table| partitions | type | possible_keys | key| key_len | ref| rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+---------+---------+-------+------+----------+-------+
|1 | SIMPLE| staffs | NULL| ref| idx_nap| idx_nap | 74| const |1 |100.00 | NULL|
+----+-------------+--------+------------+------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
二、索引优化 1、覆盖索引 如果有一个查询包含了所有索引的列,并且条件中也是根据这些索引字段检索,我们称之为覆盖索引。不是所有类型的索引都可以称为覆盖索引,它必须要存储索引列的值,不同的存储实现索引的方式不同,不是所有引擎都支持覆盖索引,Memory引擎不支持。
当执行计划中的Extra列中出现了Using index属性,表示已经实现了覆盖索引。
mysql> explain select names,age,pos from staffs where names =1 and age =22 and pos = '111';
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
| id | select_type | table| partitions | type| possible_keys | key| key_len | ref| rows | filtered | Extra|
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
|1 | SIMPLE| staffs | NULL| index | idx_nap| idx_nap | 140| NULL |1 |100.00 | Using where;
Using index |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
1 row in set, 3 warnings (0.00 sec)
2、常用索引优化小技巧
- 使用索引查询时,避免使用索引列来运算,可以将运算转到业务层。
mysql> explain select * from staffs where id= 1;
+----+-------------+--------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table| partitions | type| possible_keys | key| key_len | ref| rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|1 | SIMPLE| staffs | NULL| const | PRIMARY| PRIMARY | 4| const |1 |100.00 | NULL|
+----+-------------+--------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)mysql> explain select * from staffs where id +1 = 2;
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table| partitions | type | possible_keys | key| key_len | ref| rows | filtered | Extra|
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
|1 | SIMPLE| staffs | NULL| ALL| NULL| NULL | NULL| NULL |1 |100.00 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
- 尽量使用主键查询,主键查询不会触发回表。
- 前缀索引
一般情况下某个列前缀的选择性也是足够高的,足以满足查询的性能,但是对应BLOB,TEXT,VARCHAR。类型的列,必须要使用前缀索引,因为mysql不允许索引这些列的完整长度,使用该方法的诀窍在于要选择足够长的前缀以保证较高的选择性,通过又不能太长。
模拟一个字符串比较多,存在一定重复性的字段做实例
mysql> select count(*) as cnt,userName from test_user_copy group by userName order by cnt desc limit 10;
+-----+-------------------+
| cnt | userName|
+-----+-------------------+
| 188 | wangwu334444|
| 144 | zhengqiang334444|
| 144 | zhengqiang114444|
| 144 | zhengqiang1134444 |
| 144 | huangli1134444|
| 144 | huangli2234444|
| 144 | huangli1234444|
| 144 | lisi334444|
|44 | lili1111|
|44 | lili12222|
+-----+-------------------+
10 rows in set (0.01 sec)--可以通过如下方式来计算完整列的选择性,可以看到当前缀长度到达6之后,再增加前缀长度,选择性提升的幅度已经很小了,所以我们可以截取该字段的前6位做索引。
mysql> select count(distinct left(userName,3))/count(*) as sel3,
-> count(distinct left(userName,4))/count(*) as sel4,
-> count(distinct left(userName,5))/count(*) as sel5,
-> count(distinct left(userName,6))/count(*) as sel6,
-> count(distinct left(userName,7))/count(*) as sel7,
-> count(distinct left(userName,8))/count(*) as sel8
-> from test_user_copy;
+--------+--------+--------+--------+--------+--------+
| sel3| sel4| sel5| sel6| sel7| sel8|
+--------+--------+--------+--------+--------+--------+
| 0.0036 | 0.0036 | 0.0042 | 0.0048 | 0.0060 | 0.0071 |
+--------+--------+--------+--------+--------+--------+
1 row in set (0.01 sec)
--计算完成之后可以创建前缀索引
alter table test_user_copy add key(userName(6));
--注意:前缀索引是一种能使索引更小更快的有效方法,但是也包含缺点:mysql无法使用前缀索引做order by 和 group by。
- 索引扫描做排序
mysql可以使用同一个索引即满足排序,又用于查找行,如果可能的话,设计索引时应该尽可能地同时满足这两种任务。
只有当索引的列顺序和order by子句的顺序完全一致,并且所有列的排序方式都一样时,mysql才能够使用索引来对结果进行排序,如果查询需要关联多张表,则只有当order by子句引用的字段全部为第一张表时,才能使用索引做排序。order by子句和查找型查询的限制是一样的,需要满足索引的最左前缀的要求,否则,mysql都需要执行顺序操作,而无法利用索引排序。
mysql> explain select names,age,pos from staffs where names =1 and age =22 and pos = '111' order by names, age, pos ;
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
| id | select_type | table| partitions | type| possible_keys | key| key_len | ref| rows | filtered | Extra|
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
|1 | SIMPLE| staffs | NULL| index | idx_nap| idx_nap | 140| NULL |1 |100.00 | Using where;
Using index |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
1 row in set, 3 warnings (0.00 sec)mysql> explain select names,age,pos from staffs where names =1 and age =22 and pos = '111' order by add_time ;
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
| id | select_type | table| partitions | type | possible_keys | key| key_len | ref| rows | filtered | Extra|
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
|1 | SIMPLE| staffs | NULL| ALL| idx_nap| NULL | NULL| NULL |1 |100.00 | Using where;
Using filesort |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
1 row in set, 3 warnings (0.00 sec)
- nuion all/in/or如果可以选择,尽量选择in
mysql> select * from test_user_copy where id = 1 or id = 2;
+------+-------------------+----------+-------+-------------+---------+
| id| userName| userCode | phone | mail| address |
+------+-------------------+----------+-------+-------------+---------+
|1 | lili1111| wangwu1| 13892 | 1024@qq.com | 1112|
|2 | lili1111| wangwu2| 13894 | 1024@qq.com | 1113|
|1 | lili12222| wangwu1| 13892 | 1024@qq.com | 1112|
|2 | lili12222| wangwu2| 13894 | 1024@qq.com | 1113|
|1 | lili2222| wangwu1| 13892 | 1024@qq.com | 1112|
+----+-------+-----+-------+------------------------------------------+
5 rows in set (0.01 sec)mysql> select * from test_user_copy where id = 1union all select * from test_user_copy where id = 2;
+------+-------------------+----------+-------+-------------+---------+
| id| userName| userCode | phone | mail| address |
+------+-------------------+----------+-------+-------------+---------+
|1 | lili1111| wangwu1| 13892 | 1024@qq.com | 1112|
|2 | lili1111| wangwu2| 13894 | 1024@qq.com | 1113|
|1 | lili12222| wangwu1| 13892 | 1024@qq.com | 1112|
|2 | lili12222| wangwu2| 13894 | 1024@qq.com | 1113|
|1 | lili2222| wangwu1| 13892 | 1024@qq.com | 1112|
+----+-------+-----+-------+------------------------------------------+
5 rows in set (0.00 sec)mysql> select * from test_user_copy where id in( 1, 2);
+------+-------------------+----------+-------+-------------+---------+
| id| userName| userCode | phone | mail| address |
+------+-------------------+----------+-------+-------------+---------+
|1 | lili1111| wangwu1| 13892 | 1024@qq.com | 1112|
|2 | lili1111| wangwu2| 13894 | 1024@qq.com | 1113|
|1 | lili12222| wangwu1| 13892 | 1024@qq.com | 1112|
|2 | lili12222| wangwu2| 13894 | 1024@qq.com | 1113|
|1 | lili2222| wangwu1| 13892 | 1024@qq.com | 1112|
+----+-------+-----+-------+------------------------------------------+
5 rows in set (0.00 sec)-- in的效率稍微要高些,可能数据量越大,这个时间会越明显
mysql> show profiles;
+----------+------------+------------------------------------------------------------------------------------------------+
| Query_ID | Duration| Query|
+----------+------------+------------------------------------------------------------------------------------------------+
|1 | 0.00354950 | select * from test_user_copy where id = 1 or id = 2|
|2 | 0.00466700 | select * from test_user_copy where id = 1union all select * from test_user_copy where id = 2 |
|3 | 0.00163800 | select * from test_user_copy where id in( 1, 2)|
+----------+------------+------------------------------------------------------------------------------------------------+
8 rows in set, 1 warning (0.00 sec)
- 保持索引字段原始类型
mysql> explain select * from staffs where phone = '1';
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| id | select_type | table| partitions | type | possible_keys | key| key_len | ref| rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-------+
|1 | SIMPLE| staffs | NULL| ref| uk_phone| uk_phone | 768| const |1 |100.00 | NULL|
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)-- phone 是varchar类型,强制转出数值类型查询后,索引会失效。
mysql> explain select * from staffs where phone = 1;
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table| partitions | type | possible_keys | key| key_len | ref| rows | filtered | Extra|
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
|1 | SIMPLE| staffs | NULL| ALL| uk_phone| NULL | NULL| NULL |2 |50.00 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 3 warnings (0.00 sec)
- 在已知查询结果数量的时候,尽量使用limit
我们常常会误以为mysql会只返回需要的数据,实际上mysql却是先返回全部结果再进行计算,在日常的开发习惯中,经常是先用select语句查询大量的结果,然后获取前面的N行后关闭结果集。
mysql> explain select id, phone fromtest_user_copy where phone > 32766 order by phone;
+----+-------------+----------------+------------+------+---------------+------+---------+------+-------+----------+-----------------------------+
| id | select_type | table| partitions | type | possible_keys | key| key_len | ref| rows| filtered | Extra|
+----+-------------+----------------+------------+------+---------------+------+---------+------+-------+----------+-----------------------------+
|1 | SIMPLE| test_user_copy | NULL| ALL| uk_phone_id| NULL | NULL| NULL | 32868 |33.33 | Using where;
Using filesort |
+----+-------------+----------------+------------+------+---------------+------+---------+------+-------+----------+-----------------------------+
1 row in set, 2 warnings (0.00 sec)mysql> explain select id, phone fromtest_user_copy where phone > 32766 order by phone limit 1;
+----+-------------+----------------+------------+-------+---------------+-------------+---------+------+------+----------+-------------+
| id | select_type | table| partitions | type| possible_keys | key| key_len | ref| rows | filtered | Extra|
+----+-------------+----------------+------------+-------+---------------+-------------+---------+------+------+----------+-------------+
|1 | SIMPLE| test_user_copy | NULL| index | uk_phone_id| uk_phone_id | 1023| NULL |1 |33.33 | Using where |
+----+-------------+----------------+------------+-------+---------------+-------------+---------+------+------+----------+-------------+
1 row in set, 3 warnings (0.00 sec)
3、索引监控
mysql> show status like 'Handler_read%';
+-----------------------+-------+
| Variable_name| Value |
+-----------------------+-------+
| Handler_read_first| 2|
| Handler_read_key| 3|
| Handler_read_last| 0|
| Handler_read_next| 0|
| Handler_read_prev| 0|
| Handler_read_rnd| 0|
| Handler_read_rnd_next | 64|
+-----------------------+-------+
7 rows in set (0.00 sec)
Handler_read_first:读取索引第一个条目的次数
Handler_read_key:通过index获取数据的次数
Handler_read_last:读取索引最后一个条目的次数
Handler_read_next:通过索引读取下一条数据的次数
Handler_read_prev:通过索引读取上一条数据的次数
Handler_read_rnd:从固定位置读取数据的次数
Handler_read_rnd_next:从数据节点读取下一条数据的次数
重点查看第二条和最后一条的记录。
4、优化案例 表结构如下:
mysql> SET FOREIGN_KEY_CHECKS=0;
Query OK, 0 rows affected (0.00 sec)mysql> DROP TABLE IF EXISTS `itdragon_order_list`;
Query OK, 0 rows affected, 1 warning (0.00 sec)mysql> CREATE TABLE `itdragon_order_list` (
->`id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '主键id,默认自增长',
->`transaction_id` varchar(150) DEFAULT NULL COMMENT '交易号',
->`gross` double DEFAULT NULL COMMENT '毛收入(RMB)',
->`net` double DEFAULT NULL COMMENT '净收入(RMB)',
->`stock_id` int(11) DEFAULT NULL COMMENT '发货仓库',
->`order_status` int(11) DEFAULT NULL COMMENT '订单状态',
->`descript` varchar(255) DEFAULT NULL COMMENT '客服备注',
->`finance_descript` varchar(255) DEFAULT NULL COMMENT '财务备注',
->`create_type` varchar(100) DEFAULT NULL COMMENT '创建类型',
->`order_level` int(11) DEFAULT NULL COMMENT '订单级别',
->`input_user` varchar(20) DEFAULT NULL COMMENT '录入人',
->`input_date` varchar(20) DEFAULT NULL COMMENT '录入时间',
->PRIMARY KEY (`id`)
-> ) ENGINE=InnoDB AUTO_INCREMENT=10003 DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.03 sec)mysql>
mysql> INSERT INTO itdragon_order_list VALUES ('10000', '81X97310V32236260E', '6.6', '6.13', '1', '10', 'ok', 'ok', 'auto', '1', 'itdragon', '2017-08-28 17:01:49');
Query OK, 1 row affected (0.01 sec)mysql> INSERT INTO itdragon_order_list VALUES ('10001', '61525478BB371361Q', '18.88', '18.79', '1', '10', 'ok', 'ok', 'auto', '1', 'itdragon', '2017-08-18 17:01:50');
Query OK, 1 row affected (0.00 sec)mysql> INSERT INTO itdragon_order_list VALUES ('10002', '5RT64180WE555861V', '20.18', '20.17', '1', '10', 'ok', 'ok', 'auto', '1', 'itdragon', '2017-09-08 17:01:49');
Query OK, 1 row affected (0.00 sec)
第一个案例:
mysql> select * from itdragon_order_list where transaction_id = "81X97310V32236260E";
+-------+--------------------+-------+------+----------+--------------+----------+------------------+-------------+-------------+------------+---------------------+
| id| transaction_id| gross | net| stock_id | order_status | descript | finance_descript | create_type | order_level | input_user | input_date|
+-------+--------------------+-------+------+----------+--------------+----------+------------------+-------------+-------------+------------+---------------------+
| 10000 | 81X97310V32236260E |6.6 | 6.13 |1 |10 | ok| ok| auto|1 | itdragon| 2017-08-28 17:01:49 |
+-------+--------------------+-------+------+----------+--------------+----------+------------------+-------------+-------------+------------+---------------------+
1 row in set (0.00 sec)-- --通过查看执行计划发现type=all,需要进行全表扫描
mysql> explain select * from itdragon_order_list where transaction_id = "81X97310V32236260E";
+----+-------------+---------------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table| partitions | type | possible_keys | key| key_len | ref| rows | filtered | Extra|
+----+-------------+---------------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|1 | SIMPLE| itdragon_order_list | NULL| ALL| NULL| NULL | NULL| NULL |3 |33.33 | Using where |
+----+-------------+---------------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)--优化一、为transaction_id创建唯一索引
mysql>create unique index idx_order_transaID on itdragon_order_list (transaction_id);
Query OK, 0 rows affected (0.03 sec)
Records: 0Duplicates: 0Warnings: 0--当创建索引之后,唯一索引对应的type是const,通过索引一次就可以找到结果,普通索引对应的type是ref,表示非唯一性索引赛秒,找到值还要进行扫描,直到将索引文件扫描完为止,显而易见,const的性能要高于ref
mysql> explain select * from itdragon_order_list where transaction_id = "81X97310V32236260E";
+----+-------------+---------------------+------------+-------+--------------------+--------------------+---------+-------+------+----------+-------+
| id | select_type | table| partitions | type| possible_keys| key| key_len | ref| rows | filtered | Extra |
+----+-------------+---------------------+------------+-------+--------------------+--------------------+---------+-------+------+----------+-------+
|1 | SIMPLE| itdragon_order_list | NULL| const | idx_order_transaID | idx_order_transaID | 453| const |1 |100.00 | NULL|
+----+-------------+---------------------+------------+-------+--------------------+--------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)--优化二、使用覆盖索引,查询的结果变成 transaction_id,当extra出现using index,表示使用了覆盖索引
mysql> explain select transaction_id from itdragon_order_list where transaction_id = "81X97310V32236260E";
+----+-------------+---------------------+------------+-------+--------------------+--------------------+---------+-------+------+----------+-------------+
| id | select_type | table| partitions | type| possible_keys| key| key_len | ref| rows | filtered | Extra|
+----+-------------+---------------------+------------+-------+--------------------+--------------------+---------+-------+------+----------+-------------+
|1 | SIMPLE| itdragon_order_list | NULL| const | idx_order_transaID | idx_order_transaID | 453| const |1 |100.00 | Using index |
+----+-------------+---------------------+------------+-------+--------------------+--------------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
【Mysql优化|Mysql-索引优化】第二个案例:
--创建复合索引
mysql> create index idx_order_levelDate on itdragon_order_list (order_level,input_date);
Query OK, 0 rows affected (0.03 sec)
Records: 0Duplicates: 0Warnings: 0--创建索引之后发现跟没有创建索引一样,都是全表扫描,都是文件排序
mysql> explain select * from itdragon_order_list order by order_level,input_date;
+----+-------------+---------------------+------------+------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table| partitions | type | possible_keys | key| key_len | ref| rows | filtered | Extra|
+----+-------------+---------------------+------------+------+---------------+------+---------+------+------+----------+----------------+
|1 | SIMPLE| itdragon_order_list | NULL| ALL| NULL| NULL | NULL| NULL |3 |100.00 | Using filesort |
+----+-------------+---------------------+------------+------+---------------+------+---------+------+------+----------+----------------+
1 row in set, 1 warning (0.00 sec)--可以使用force index强制指定索引
mysql> explain select * from itdragon_order_list force index(idx_order_levelDate) order by order_level,input_date;
+----+-------------+---------------------+------------+-------+---------------+---------------------+---------+------+------+----------+-------+
| id | select_type | table| partitions | type| possible_keys | key| key_len | ref| rows | filtered | Extra |
+----+-------------+---------------------+------------+-------+---------------+---------------------+---------+------+------+----------+-------+
|1 | SIMPLE| itdragon_order_list | NULL| index | NULL| idx_order_levelDate | 68| NULL |3 |100.00 | NULL|
+----+-------------+---------------------+------------+-------+---------------+---------------------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)--其实给订单排序意义不大,给订单级别添加索引意义也不大,因此可以先确定order_level的值,然后再给input_date排序
mysql> explain select * from itdragon_order_list where order_level=3 order by input_date;
+----+-------------+---------------------+------------+------+---------------------+---------------------+---------+-------+------+----------+-----------------------+
| id | select_type | table| partitions | type | possible_keys| key| key_len | ref| rows | filtered | Extra|
+----+-------------+---------------------+------------+------+---------------------+---------------------+---------+-------+------+----------+-----------------------+
|1 | SIMPLE| itdragon_order_list | NULL| ref| idx_order_levelDate | idx_order_levelDate | 5| const |1 |100.00 | Using index condition |
+----+-------------+---------------------+------------+------+---------------------+---------------------+---------+-------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
推荐阅读
- java|创作365天纪念日
- 生产系统中的机器学习工程|基于 Docker 快速使用远程(云)数据库
- TiDB|构建实时数仓 - 当 TiDB 偶遇 Pravega
- redis|Redis的使用场景和注意事项
- 缓存|Redis的应用场景
- MySQL|【MySQL系列】- LIKE查询 以%开头一定会让索引失效吗
- sql|达梦SQL优化经验
- sql|达梦数据库sql优化
- sql|达梦sql优化实践5