SQL优化(二)、2.2.1如何使用索引

        b-tree索引是最常见的索引,构造类似二叉树,可以根据键值提供一行或一个行集的快速访问,通常只需要很少的读操作就可以找到正确的行。

注意:b-tree索引中的b不是代表binary二叉树,而是代表平衡树balanced。b-tree索引并不是一颗二叉树。

可以利用b-tree索引进行全关键字,关键字范围和关键字前缀查询,以下例子若没特别说明都能在mysql5.1/5.5版本通过。
mysql中能使用索引的典型场景:
(1)匹配全值(match the full value)(一个索引由三个字段组成,在where后面每个字段都按指定值匹配,即使不按照索引定义顺序,匹配全值进行查询的时候,依然会使用索引。
)eg:idx_rental_date索引由三部分组成( rental_date,inventory_id, customer_id)

mysql> explain select * from rental where rental_date='2005-05-25 17:22:10' and inventory_id=373 and customer_id=343\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: rental
         type: ref
possible_keys: idx_fk_inventory_id,idx_fk_customer_id,idx_rental_date
          key: idx_rental_date
      key_len: 10
          ref: const,const,const
         rows: 1
        Extra: NULL
1 row in set (0.00 sec)
执行下面,由key值可以看出即使不按照索引定义顺序,匹配全值进行查询的时候,优化器选择该索引进行的扫描。
mysql> explain select * from rental where  customer_id=343 and rental_date='2005-05-25 17:22:10' and  inventory_id=373 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: rental
         type: ref
possible_keys: idx_fk_inventory_id,idx_fk_customer_id,idx_rental_date
          key: idx_rental_date
      key_len: 10
          ref: const,const,const
         rows: 1
        Extra: NULL
1 row in set (0.00 sec)

在这里插入图片描述
(2)匹配值的范围查询(match a range of values),对索引字段进行范围检索。

mysql> explain select * from rental where customer_id>=373 and customer_id<400\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: rental
         type: range
possible_keys: idx_fk_customer_id
          key: idx_fk_customer_id
      key_len: 2
          ref: NULL
         rows: 717
        Extra: Using index condition
1 row in set (0.00 sec)

type=range表示优化器选择范围查询, key= idx_fk_customer_id表示优化器选择该索引加速访问,如果extra列为using where ,表示优化器除了利用索引来加速访问之外,还需要根据索引回表查询数据。本例中Extra: Using index condition,表示除了利用索引加速访问,还使用了索引作为条件回表查询数据。
(3)匹配最左前缀(match a leftmost prefix),仅仅使用索引中的最左边列和联合索引的其他列组合使用,可以使用到该联合索引,比如 a1,a2,a3字段上的联合索引可以被 包含a1,(a1+a2),(a1+a2+a3)等值查询利用到,可是查询a2,a2+a3的时候,就用不到该联合索引了。如果是a1+a3进行等着查询,执行计划也是可以用到这个联合索引的,eg:

 mysql> alter table payment add index idx_payment_date(payment_date,amount,last_update);
Query OK, 0 rows affected (0.12 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select * from payment where payment_date='2006-02-14 15:16:03' and last_update='2006-02-15 22:12:32'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: payment
         type: ref
possible_keys: idx_payment_date
          key: idx_payment_date
      key_len: 5
          ref: const
         rows: 182
        Extra: Using index condition
1 row in set (0.00 sec)

看执行计划的key值和extra可以看到优化器仍然可以使用复合索引idx_payment_date做索引条件过滤。这都是因为是以复合索引第一列组合做的查询;
         如果不是从复合索引第一列作为条件检索查询,则通过执行计划看到不会使用复合索引。eg:可以看到,并没有使用索引,且看到extra的值为using where 做回表查询。可以认为最左匹配原则是mysql中b-tree索引使用的首要原则。

mysql> explain select * from payment where amount=3.98 and last_update='2006-02-15 22:12:32'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: payment
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 16086
        Extra: Using where
1 row in set (0.00 sec)

(4) payment表存在索引`payment.index :idx_payment_date (payment_date,amount,last_update); 现在测试只对索引查询(index only query),查询的列均在索引中,查询效率会提升,比对(3)用的 全表扫描,本次查询的字段包含在索引中,可以看出查询计划不同了,extra变为 using index ,即直接访问索引就可得到需要的数据,不需要索引回表,using index 即为覆盖索引扫描。此外,通常只访问需要的数据比全表扫描效率高。eg:

mysql> explain select last_update from payment where payment_date='2006-02-14 15:16:03' and amount=3.98\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: payment
         type: ref
possible_keys: idx_payment_date
          key: idx_payment_date
      key_len: 8
          ref: const,const
         rows: 1
        Extra: Using index
1 row in set (0.00 sec)

(5)匹配列前缀(match a column prefix),仅适用索引第一列,且只包含索引第一列的开头做模糊查找。新增索引,然后执行sql,可发现用到该索引了。
Extra: Using where 表示优化器需要通过索引回表查询数据。

create index idx_title_desc_part on film_text(title(10),description(20));
mysql> EXPLAIN select title from film_text where title like 'AFRICAN%'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: film_text
         type: range
possible_keys: idx_title_desc_part,idx_title_description
          key: idx_title_desc_part
      key_len: 32
          ref: NULL
         rows: 1
        Extra: Using where
1 row in set (0.03 sec)

(6)如果使用联合索引的部分精确匹配而其他做范围匹配。type=ref ,索引key为rental_date 说明优化器选择该索引帮助查询,同时因为值查了inventory_id (为索引中一列),所以在Extra中使用了Using index,表示该查询使用了覆盖索引扫描。

mysql> explain select inventory_id from rental where rental_date='2006-02-14 15:16:03' and customer_id>=300 and customer_id<=400\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: rental
         type: ref
possible_keys: rental_date,idx_fk_customer_id
          key: rental_date
      key_len: 5
          ref: const
         rows: 1
        Extra: Using where; Using index
1 row in set (0.00 sec)

rental表此时的索引
在这里插入图片描述
(7)如果使用的列明为索引,那么使用column is null 也会使用索引(不同于oracle),eg:
在不同于这里插入图片描述

mysql> explain select  * from payment where rental_id is null\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: payment
         type: ref
possible_keys: fk_payment_rental
          key: fk_payment_rental
      key_len: 5
          ref: const
         rows: 1
        Extra: Using index condition
1 row in set (0.00 sec)

(8) mysql5.6引入index condition PUSHdown(icp)的特性,优化查询,pushdown表示操作下放,某些情况下的条件过滤操作下放到存储引擎。
下面比对5.6之前和之后的执行计划sql及物理操作对比图

mysql> select version();
+------------+
| version()  |
+------------+
| 5.5.32-log |
+------------+
1 row in set (0.02 sec)
mysql> explain select * from rental where rental_date='2006-02-14 15:16:03' and customer_id>=300 and customer_id<=400\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: rental
         type: ref
possible_keys: idx_fk_customer_id,rental_date
          key: rental_date
      key_len: 8
          ref: const
         rows: 1
        Extra: Using where
1 row in set (0.00 sec)

该5.5版本对上述sql执行计划图示:
在这里插入图片描述
在5.6版本上同样查看以上sql执行计划,发现explain的extra执行计划的extra部分从using where 变成了using index condition.

mysql> select version();
+------------+
| version()  |
+------------+
| 5.6.21-log |
+------------+
1 row in set (0.02 sec)
mysql> explain select * from rental where rental_date='2006-02-14 15:16:03' and customer_id>=300 and customer_id<=400\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: rental
         type: ref
possible_keys: rental_date,idx_fk_customer_id
          key: rental_date
      key_len: 5
          ref: const
         rows: 1
        Extra: Using index condition
1 row in set (0.00 sec)

extra的值变为using index condition表示mysql使用了icp进一步优化查询,在检索时把customer_id的条件过滤操作下推到存储引擎层完成,降低不必要的io访问。
在这里插入图片描述

番外篇:

猜你喜欢

转载自blog.csdn.net/qq_17033579/article/details/83216343
今日推荐