这里主要内容有如下:
- 索引的使用场景
- 索引的使用原则
1.索引的使用场景
在索引基础篇(https://blog.csdn.net/zhang_referee/article/details/83045903)说过,索引能极大加快访问速度,这里说下索引的主要使用场景。
首先看一下,案例表索引情况:
mysql> show index from dye_production_schedules;
+--------------------------+------------+--------------------------+--------------+---------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------------------------+------------+--------------------------+--------------+---------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| dye_production_schedules | 0 | PRIMARY | 1 | id | A | 1371967 | NULL | NULL | | BTREE | | |
| dye_production_schedules | 1 | order_id_order_detail_id | 1 | order_id | A | 19625 | NULL | NULL | | BTREE | | |
| dye_production_schedules | 1 | order_id_order_detail_id | 2 | order_detail_id | A | 1521058 | NULL | NULL | | BTREE | | |
| dye_production_schedules | 1 | dye_factory_id | 1 | dye_factory_id | A | 29238 | NULL | NULL | | BTREE | | |
| dye_production_schedules | 1 | dye_code | 1 | dye_code | A | 1521058 | NULL | NULL | | BTREE | | |
| dye_production_schedules | 1 | order_detail_id | 1 | order_detail_id | A | 192921 | NULL | NULL | | BTREE | | |
| dye_production_schedules | 1 | order_id_batch_num | 1 | order_id | A | 18622 | NULL | NULL | | BTREE | | |
| dye_production_schedules | 1 | order_id_batch_num | 2 | batch_num | A | 1521058 | NULL | NULL | | BTREE | | |
| dye_production_schedules | 1 | customer_color_name | 1 | customer_color_name | NULL | 1521058 | NULL | NULL | | FULLTEXT | | |
+--------------------------+------------+--------------------------+--------------+---------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1.1 索引检索
mysql> explain select sql_no_cache id ,dye_factory_id ,order_id ,order_detail_id ,dye_code,product_id,batch_num ,customer_color_name from dye_production_schedules where order_id = 4538 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: dye_production_schedules
partitions: NULL
type: ref
possible_keys: order_id_order_detail_id,order_id_batch_num
key: order_id_order_detail_id
key_len: 4
ref: const
rows: 213
filtered: 100.00
Extra: NULL
通过 explain 分析可得知,数据访问方式使用了 索引访问( type:ref ),explain 可参考:https://blog.csdn.net/zhang_referee/article/details/83041301
1.2 索引排序
这里我们假设在order_detail_id 字段上进行排序
mysql> explain select sql_no_cache id ,dye_factory_id,order_id ,order_detail_id ,dye_code ,batch_num,customer_color_name from dye_production_schedules order by order_detail_id desc limit 10;
+----+-------------+--------------------------+------------+-------+---------------+-----------------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------------+------------+-------+---------------+-----------------+---------+------+------+----------+-------+
| 1 | SIMPLE | dye_production_schedules | NULL | index | NULL | order_detail_id | 4 | NULL | 10 | 100.00 | NULL |
+----+-------------+--------------------------+------------+-------+---------------+-----------------+---------+------+------+----------+-------+
之前在索引基础篇(https://blog.csdn.net/zhang_referee/article/details/83045903),举栗子的时候,可以看到,当没有在order_detail_id 这个字段上建立索引同样的查询,explain 会在extra 列上提示:Using filesort ,表示查询使用了文件排序。而同样的sql 加上索引后,能省去这一步骤(说省去也不准确,因为还是做了排序--索引排序,不过却没有显示在extra 列)。
1.3 索引覆盖
所谓索引覆盖,指的是索引的内容包含了查询所需全部数据,在explain 中 ,extra 列上提示Using index ,表示使用到了索引覆盖。
mysql> explain select order_id,batch_num from dye_production_schedules ;
+----+-------------+--------------------------+------------+-------+---------------+--------------------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------------+------------+-------+---------------+--------------------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | dye_production_schedules | NULL | index | NULL | order_id_batch_num | 198 | NULL | 1521058 | 100.00 | Using index |
+----+-------------+--------------------------+------------+-------+---------------+--------------------+---------+------+---------+----------+-------------+
2.索引的使用原则
2.1 列独立
mysql> explain select id,dye_factory_id,order_id ,order_detail_id ,customer_color_name from dye_production_schedules where order_detail_id = 10000 ;
+----+-------------+--------------------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | dye_production_schedules | NULL | ref | order_detail_id | order_detail_id | 4 | const | 18 | 100.00 | NULL |
+----+-------------+--------------------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.08 sec)
mysql> explain select id,dye_factory_id,order_id ,order_detail_id ,customer_color_name from dye_production_schedules where order_detail_id = 10000 - 1 ;
+----+-------------+--------------------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | dye_production_schedules | NULL | ref | order_detail_id | order_detail_id | 4 | const | 16 | 100.00 | NULL |
+----+-------------+--------------------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)
mysql> explain select id,dye_factory_id,order_id ,order_detail_id ,customer_color_name from dye_production_schedules where order_detail_id - 1 = 10000 ;
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | dye_production_schedules | NULL | ALL | NULL | NULL | NULL | NULL | 1581424 | 100.00 | Using where |
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.02 sec)
mysql>
可以看到第三条查询使用了全表扫描,没有用到索引。在查询中我们应该养成简化where 条件的习惯,始终将索引列单独放在比较符号的一侧。
2.2 左原则
Like:匹配模式必须要左边确定不能以通配符开头
mysql> explain select id,order_id,dye_code ,customer_color_name from dye_production_schedules where dye_code like 'b8ff%';
+----+-------------+--------------------------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | dye_production_schedules | NULL | range | dye_code | dye_code | 194 | NULL | 33 | 100.00 | Using index condition |
+----+-------------+--------------------------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select id,order_id,dye_code ,customer_color_name from dye_production_schedules where dye_code like '%b8ff';
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | dye_production_schedules | NULL | ALL | NULL | NULL | NULL | NULL | 1581424 | 11.11 | Using where |
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select id,order_id,dye_code ,customer_color_name from dye_production_schedules where dye_code like '%b8ff%';
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | dye_production_schedules | NULL | ALL | NULL | NULL | NULL | NULL | 1581424 | 11.11 | Using where |
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
通过explain 语句,可以看到第二条和第三条查询,由于索引列字段左边不确定,因此没能使用到索引。在项目中,出现双百分号的查询一般使用全文索引来加快数据访问速度!
2.3 复合索引
复合索引仅仅针对左边第一个索引有效。
在这个表中,`order_id_batch_num` (`order_id`,`batch_num`) 是一个复合索引,其中第一个索引是order_id。
mysql> explain select * from dye_production_schedules where order_id = 8780;
+----+-------------+--------------------------+------------+------+---------------------------------------------+--------------------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------------+------------+------+---------------------------------------------+--------------------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | dye_production_schedules | NULL | ref | order_id_order_detail_id,order_id_batch_num | order_id_order_detail_id | 4 | const | 205 | 100.00 | NULL |
+----+-------------+--------------------------+------------+------+---------------------------------------------+--------------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from dye_production_schedules where batch_num like '5c5e9534d%';
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | dye_production_schedules | NULL | ALL | NULL | NULL | NULL | NULL | 1581424 | 11.11 | Using where |
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from dye_production_schedules where order_id = 8780 and batch_num like '5c5e9534d%';
+----+-------------+--------------------------+------------+-------+---------------------------------------------+--------------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------------+------------+-------+---------------------------------------------+--------------------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | dye_production_schedules | NULL | range | order_id_order_detail_id,order_id_batch_num | order_id_batch_num | 198 | NULL | 1 | 100.00 | Using index condition |
+----+-------------+--------------------------+------------+-------+---------------------------------------------+--------------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.06 sec)
通过上面栗子explain 的分析可以看到,检索条件只有batch_num时,索引无效。
2.4 OR 原则
在查询中使用到了 or 语句时,必须确保 or 两端都能使用到索引才能使用索引,否则索引无效。
mysql> explain select * from dye_production_schedules where order_id = 8780 or dye_code like 'b8ff%';
+----+-------------+--------------------------+------------+-------------+------------------------------------------------------+-----------------------------------+---------+------+------+----------+------------------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------------+------------+-------------+------------------------------------------------------+-----------------------------------+---------+------+------+----------+------------------------------------------------------------------+
| 1 | SIMPLE | dye_production_schedules | NULL | index_merge | order_id_order_detail_id,dye_code,order_id_batch_num | order_id_order_detail_id,dye_code | 4,194 | NULL | 238 | 100.00 | Using sort_union(order_id_order_detail_id,dye_code); Using where |
+----+-------------+--------------------------+------------+-------------+------------------------------------------------------+-----------------------------------+---------+------+------+----------+------------------------------------------------------------------+
在上面的查询中,order_id 和 dye_code 列上均有索引,且索引有效,所以可以使用到索引。
mysql> explain select * from dye_production_schedules where order_id = 8780 or dye_code like '%b8ff%';
+----+-------------+--------------------------+------------+------+---------------------------------------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------------+------------+------+---------------------------------------------+------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | dye_production_schedules | NULL | ALL | order_id_order_detail_id,order_id_batch_num | NULL | NULL | NULL | 1581424 | 11.11 | Using where |
+----+-------------+--------------------------+------------+------+---------------------------------------------+------+---------+------+---------+----------+-------------+
在同样的列上,由于dye_code 无法使用到索引(不满足左原则),导致整个查询均无法使用到索引(key 列值为 NULL)。
上面是主要原则,有时候,我们即使建立了索引,满足了上述所有条件。然而查询条件不同,mysql 也会自动选择弃用索引。
mysql> explain select * from dye_production_schedules where order_detail_id > 30000;
+----+-------------+--------------------------+------------+------+-----------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------------+------------+------+-----------------+------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | dye_production_schedules | NULL | ALL | order_detail_id | NULL | NULL | NULL | 1581424 | 50.00 | Using where |
+----+-------------+--------------------------+------------+------+-----------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from dye_production_schedules where order_detail_id< 30000;
+----+-------------+--------------------------+------------+------+-----------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------------+------------+------+-----------------+------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | dye_production_schedules | NULL | ALL | order_detail_id | NULL | NULL | NULL | 1581424 | 50.00 | Using where |
+----+-------------+--------------------------+------------+------+-----------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.04 sec)
mysql> explain select * from dye_production_schedules where order_detail_id< 3000;
+----+-------------+--------------------------+------------+-------+-----------------+-----------------+---------+------+--------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------------+------------+-------+-----------------+-----------------+---------+------+--------+----------+-----------------------+
| 1 | SIMPLE | dye_production_schedules | NULL | range | order_detail_id | order_detail_id | 4 | NULL | 125092 | 100.00 | Using index condition |
+----+-------------+--------------------------+------------+-------+-----------------+-----------------+---------+------+--------+----------+-----------------------+
可以看到mysql 会根据条件,会智能地启用或弃用可选索引。这让我想起,建立索引时,尽量不要建在标识性很低的列上,例如性别,是否删除(is_delete 两个值0或1)等列上,即便建了索引,mysql 也会弃用索引,采用扫表的方式(顺序IO)访问数据。