mysql优化二(索引问题)

索引问题

索引是数据库优化中最常用也是最重要的手段之一,通过索引通常可以帮助用户解决大多数的 SQL 性能问题。 

 

一、索引的存储分类

1、存储

  myisam 存储的表数据和索引是自动分开存储的,各自独占一个文件;

  innodb 存储引擎的表数据和索引是存在一个表空间里面的,但可以多个文件组成。

2、分类

  mysql 中索引的存储类型目前只有两种(btree 和 hash);myisam和innodb存储引擎只支持btree索引;memory/heap 存储引擎可以支持hash和btree索引。

 

mysql 目前不支持函数索引,但是能对列的前面某一部分进行索引,例如name字段,可以只取 name 的前4个字符进行索引,这样可以大大的缩小索引文件的大小。下面创建一个前缀索引的例子:

mysql> create index ind_company_name on company(name(4));

Query OK, 0 rows affected (0.02 sec)

 

 

二、mysql如何使用索引

索引用于快速找出在某个列中有一特定值的行。对相关列使用索引是提高 SELECT 操作性能的最佳途径。

查询要使用索引最主要的条件是查询条件中需要使用索引关键字,如果是多列索引,那 么只有查询条件使用了多列关键字最左边的前缀时,才可以使用索引,否则将不能使用索引。 

 

1、使用索引

在mysql中,下列几种情况下有可能使用到索引。

(1)对于创建的多列索引,只要查询的条件中用到了最左边的列,索引一般就会被使用,举例如下:

 

首先按 company_id 进行表查询,如下:

mysql> create index ind_sales_companyid_monesy on sales(company_id,moneys);

Query OK, 0 rows affected (0.15 sec)

Records: 0  Duplicates: 0  Warnings: 0

 

然后按company_id 进行表查询,具体如下:

mysql> explain select * from sales where company_id=3\G;

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: sales

         type: ref

possible_keys: ind_sales_companyid_monesy

          key: ind_sales_companyid_monesy

      key_len: 4

          ref: const

         rows: 1

        Extra: NULL

1 row in set (0.01 sec)

 

可以发现即便 where 条件中不是使用的 company_id 与 moneys 的组合条件,索引仍然能用到,这就是索引的前缀特性。但是如果只按 moneys 条件查询表,那么索引就不会被用到,具体如下:

mysql> explain select * from sales where moneys=9999999999\G;

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: sales

         type: ALL

possible_keys: NULL

          key: NULL

      key_len: NULL

          ref: NULL

         rows: 61635

        Extra: Using where

1 row in set (0.00 sec)

 

(2) 对于使用like的查询,后面如果是常量并且只有%号不在第一个字符,索引才可能会被使用,看下面两个执行计划:

name字段已经被创建索引

mysql> explain select * from company where name like '%3'\G;

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: company

         type: ALL

possible_keys: NULL

          key: NULL

      key_len: NULL

          ref: NULL

         rows: 64

        Extra: Using where

1 row in set (0.00 sec)

 

mysql> explain select * from company where name like '3%'\G;

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: company

         type: range

possible_keys: ind_company_name

          key: ind_company_name

      key_len: 14

          ref: NULL

         rows: 1

        Extra: Using where

1 row in set (0.00 sec)

可以发现第一个例子没有使用索引,而第二例子就能够使用索引,区别就在于“%”的位置 不同,前者把“%”放到第一位就不能用到索引,而后者没有放到第一位就使用了索引。 另外,如果如果 like 后面跟的是一个列的名字,那么索引也不会被使用。 

 

(3)如果对大的文本进行搜索,使用全文索引而不用使用 like ‘%...%’。 

 

(4)如果列名是索引,使用column_name is null将使用索引。如下例中查询name为null 的记录就用到了索引: 

mysql> explain select * from company where name is null\G;

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: company

         type: ref

possible_keys: ind_company_name

          key: ind_company_name

      key_len: 15

          ref: const

         rows: 1

        Extra: Using where

1 row in set (0.00 sec)

 

 

2、存在索引但不使用索引

在下列情况下,虽然存在索引,但mysql并不会使用相应的索引。

(1)如果mysql估计使用索引比全表扫描更慢,则不使用索引。例如如果

 select * from table_name where key_part1 > 1 and key_part1 < 90;

 

(2)如果使用memory/heap 表并且 where 条件中不使用 ‘=’ 进行索引列,那么不会使用到索引。heap 表只有在 ‘=’ 的条件下才会使用索引。

 

(3)用 or 分割开的条件,如果 or 前的条件中的列有索引,而后面的列中没有索引,那么涉及到的索引都不会被用到,例如:

mysql> show index from sales\G;

*************************** 1. row ***************************

        Table: sales

   Non_unique: 0

     Key_name: PRIMARY

 Seq_in_index: 1

  Column_name: id

    Collation: A

  Cardinality: 65743

     Sub_part: NULL

       Packed: NULL

         Null: 

   Index_type: BTREE

      Comment: 

Index_comment: 

 

从上面可以发现只有id列上面有索引,来看如下的执行计划:

mysql> explain select * from sales where id = 1 or name = '邓邓'\G;

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: sales

         type: ALL

possible_keys: PRIMARY

          key: NULL

      key_len: NULL

          ref: NULL

         rows: 65743

        Extra: Using where

1 row in set (0.00 sec)

可见虽然在 id 这个列上存在索引 primary,但是这个 sql 语句并没有用到这个索引,原因就是 or 中有一个条件中的列没有索引。

 

(4)如果不是索引列的第一部分,如下例子:

mysql> explain select * from sales where moneys = 1\G;

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: sales

         type: ALL

possible_keys: NULL

          key: NULL

      key_len: NULL

          ref: NULL

         rows: 65743

        Extra: Using where

1 row in set (0.00 sec)

可见虽然在 moneys 上面建有复合索引,但是由于 money 不是索引的第一列,那么在查询中这个索引也不会被 mysql 采用。

 
(5)如果 like 是以%开始,例如:

mysql> explain select * from company where name like '%3'\G;

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: company

         type: ALL

possible_keys: NULL

          key: NULL

      key_len: NULL

          ref: NULL

         rows: 64

        Extra: Using where

1 row in set (0.00 sec)

可见虽然在name上建有索引,但是由于 where 条件中 like 的值的 ‘%’ 在第一位了,那么 mysql 也不会采用这个索引。

 

(6)如果列类型是字符串,那么一定记得在 where 条件中把字符常量值用引号引起来,否则的话即便是这个列上有索引,mysql 也不会用到,因为 mysql 默认把输入的常量值进行转换以后才进行检索。举例:

mysql> explain select * from company where name = 123\G;

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: company

         type: ALL

possible_keys: ind_company_name

          key: NULL

      key_len: NULL

          ref: NULL

         rows: 64

        Extra: Using where

1 row in set (0.00 sec)

 

mysql> explain select * from company where name = '123'\G;

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: company

         type: ref

possible_keys: ind_company_name

          key: ind_company_name

      key_len: 15

          ref: const

         rows: 1

        Extra: Using where

1 row in set (0.00 sec)

 

 

三、查看索引使用情况

如果索引正在工作,Handler_read_key 的值将很高,这个值代表了一个行被索引值读的 次数,很低的值表明增加索引得到的性能改善不高,因为索引并不经常使用。

Handler_read_rnd_next 的值高则意味着查询运行低效,并且应该建立索引补救。这个值 的含义是在数据文件中读下一行的请求数。如果正进行大量的表扫描, Handler_read_rnd_next 的值较高,则通常说明表索引不正确或写入的查询没有利用索引,具 体如下。 

mysql> show status like 'Handler_read%';

+-----------------------+-------+

| Variable_name                      | Value     |

+-----------------------+-------+

| Handler_read_first                 | 1           |

| Handler_read_key                  | 1           |

| Handler_read_last                  | 0          |

| Handler_read_next                 | 0          |

| Handler_read_prev                 | 0          |

| Handler_read_rnd                   | 0          |

| Handler_read_rnd_next           | 119        |

+-----------------------+-------+

7 rows in set (0.00 sec)

 

从上面的例子中可以看出,目前使用的 MySQL 数据库的索引情况并不理想。 

 

 

posted @ 2017-12-18 14:18  outstandingJie  阅读(115)  评论(0编辑  收藏  举报