zoukankan      html  css  js  c++  java
  • MySql基础笔记(二)Mysql语句优化---索引

    Mysql语句优化--索引

    一、开始优化前的准备

    一)explain语句

    当MySql要执行一个查询语句的时候,它首先会对语句进行语法检查,然后生成一个QEP(Query Execution Plan)

    QEP决定了MySql从底层存储引擎获取信息的方式。

    语法:explain [extended | partitions] select ......

    或者:explain tableName (此时相当与desc语句)

    explain select * from artist where name='The Beatles'G;
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: artist
       partitions: NULL
             type: ref
    possible_keys: name
              key: name
          key_len: 257
              ref: const
             rows: 1
         filtered: 100.00
            Extra: NULL
    1 row in set, 1 warning (0.00 sec)
    
    ERROR:
    No query specified

    各列详解:

    1.key

    列出了优化器本次实际使用的索引。

    2.possible_keys

    优化器可能使用的索引。

    3.rows

    行数估算值

    4.key_len

    定义了用于SQL语句的链接条件的键的长度(表明了索引的大小)。

    key_len的值只和用在链接和where条件中的索引的列有关。

    5.partistions

    代表给定表所使用的分区。

    二)其他

    1.show table status:

    用于查看数据库表的底层大小以及表结构,其中包括存储引擎类型、版本、数据和索引大小、行的平均长度及行数。

    show table status like 'artist'G;

    这条命令返回的数据准确度取决于使用的数据库引擎。

    2.show status:

    用于查看MySql服务器的当前内部状态信息

    show global status like 'Created_tmp_%tables';
    show session status like 'Created_tmp_%tables';

    3.show variables

    查看mysql系统变量的当前值,默认在session范围内执行。

    show session variables like 'tmp_table_size';

    4.show index

    查看索引信息

    show index from artistG;

    二、单列索引

    对select语句where,order by,group by 后的条件所在列添加索引可以提高查询速度。

    一)理解索引基数

    当一个查询中使用不止一个索引时,Mysql会试图找到一个最高效的索引。

    使用到的表:

    | artist | CREATE TABLE `artist` (
      `artist_id` int(10) unsigned NOT NULL,
      `type` enum('Band','Person','Unknown','Combination') NOT NULL,
      `name` varchar(255) NOT NULL,
      `gender` enum('Male','Female') DEFAULT NULL,
      `founded` year(4) DEFAULT NULL,
      `country_id` smallint(5) unsigned DEFAULT NULL,
      PRIMARY KEY (`artist_id`),
      KEY `founded` (`founded`),
      KEY `type` (`type`)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1 |

    explain语句:

    explain select * from artist where type='Band' and founded=1980G;

    结果:

    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: artist
       partitions: NULL
             type: index_merge
    possible_keys: founded,type
              key: founded,type
          key_len: 2,1
              ref: NULL
             rows: 608
         filtered: 100.00
            Extra: Using intersect(founded,type); Using where
    1 row in set, 1 warning (0.05 sec)
    
    ERROR:
    No query specified

    此时,mysql必须在possible_keys中做出选择,优化器会根据最少工作量的估算开销来选择索引。

    我们可以使用索引基数来确定最有可能被选中的索引。

    mysql> show index from artistG;
    *************************** 1. row ***************************
            Table: artist
       Non_unique: 0
         Key_name: PRIMARY
     Seq_in_index: 1
      Column_name: artist_id
        Collation: A
      Cardinality: 581100
         Sub_part: NULL
           Packed: NULL
             Null:
       Index_type: BTREE
          Comment:
    Index_comment:
    *************************** 2. row ***************************
            Table: artist
       Non_unique: 1
         Key_name: founded
     Seq_in_index: 1
      Column_name: founded
        Collation: A
      Cardinality: 104
         Sub_part: NULL
           Packed: NULL
             Null: YES
       Index_type: BTREE
          Comment:
    Index_comment:
    *************************** 3. row ***************************
            Table: artist
       Non_unique: 1
         Key_name: type
     Seq_in_index: 1
      Column_name: type
        Collation: A
      Cardinality: 3
         Sub_part: NULL
           Packed: NULL
             Null:
       Index_type: BTREE
          Comment:
    Index_comment:
    3 rows in set (0.00 sec)
    ERROR:
    No query specified

    其中,Cardinality就是所谓的基数。其中,founded列拥有更高的基数。如果列索引的基数越高,

    就说明列中唯一的数量越多,也就可以在选用该索引时,通过更少的读操作找到需要的记录。

    当然仅仅知道索引中唯一值的数量意义不大,重要的是要知道该值与总数量的比值。理想情况下该比值为1。

    这些信息不但可以帮助我们判断索引是否高效,还可以告诉我们如何在多列索引中对列进行排序。

    二)索引与通配符

    我们可以通过通配符来做模式匹配工作,但是:

    1. 如果查找的词是以通配符开头,则MySql不会使用索引。

    mysql> explain select * from artist where name like '%the beatles'G;
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: artist
       partitions: NULL
             type: ALL
    possible_keys: NULL
              key: NULL
          key_len: NULL
              ref: NULL
             rows: 581100
         filtered: 11.11
            Extra: Using where

    解决方案:

    如果你经常需要以通配符开头的查询,常用的方法是在数据库中保存需要查询值的反序值。例如:

    想查找以.com结尾的电子邮件地址,首先定义一个reserve_email列存储电子邮件的反序值。查询时

    使用:reserve_email like reserve(%.com)

    2.MySql不支持基于索引的函数。

    mysql> explain select * from artist where upper(name) = 'QUEEN'G;
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: artist
       partitions: NULL
             type: ALL
    possible_keys: NULL
              key: NULL
          key_len: NULL
              ref: NULL
             rows: 581100
         filtered: 100.00
            Extra: Using where
    1 row in set, 1 warning (0.00 sec)

    因为在name列上使用了upper函数,所以mysql不会使用索引。

    三)唯一索引

    创建唯一索引的目的:

    1)提供数据完整性,以保证列中的任何值只出现一次

    2)告知优化器对给定的记录最多只有一行返回结果,避免额外的扫描

    四)结果排序

    索引也可对查询结果进行排序。基于索引的排序,免去了分类过程,更优。

    技巧:也就是说给order by 后面的条件列,可以加索引优化。

    五)使用索引链接表

    索引的另一个好处是可以提高关系表的链接速度

    mysql> explain select ar.name, ar.founded, al.name, al.first_released from artist ar inner join album al using (artist_id) where ar.name = 'Queen'G
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: ar
       partitions: NULL
             type: ref
    possible_keys: PRIMARY,name_index
              key: name_index
          key_len: 257
              ref: const
             rows: 1
         filtered: 100.00
            Extra: NULL
    *************************** 2. row ***************************
               id: 1
      select_type: SIMPLE
            table: al
       partitions: NULL
             type: ALL
    possible_keys: NULL
              key: NULL
          key_len: NULL
              ref: NULL
             rows: 552034
         filtered: 10.00
            Extra: Using where; Using join buffer (Block Nested Loop)
    2 rows in set, 1 warning (0.12 sec)

    为链接条件添加索引

    mysql> alter table album add index (artist_id);                                                                                                     
    Query OK, 0 rows affected (29.24 sec)                                                                                                               
    Records: 0  Duplicates: 0  Warnings: 0                                                                                                              
                                                                                                                                                        
    mysql> explain select ar.name, ar.founded, al.name, al.first_released from artist ar inner join album al using (artist_id) where ar.name = 'Queen'G
    *************************** 1. row ***************************                                                                                      
               id: 1                                                                                                                                    
      select_type: SIMPLE                                                                                                                               
            table: ar                                                                                                                                   
       partitions: NULL                                                                                                                                 
             type: ref                                                                                                                                  
    possible_keys: PRIMARY,name_index                                                                                                                   
              key: name_index                                                                                                                           
          key_len: 257                                                                                                                                  
              ref: const                                                                                                                                
             rows: 1                                                                                                                                    
         filtered: 100.00                                                                                                                               
            Extra: NULL                                                                                                                                 
    *************************** 2. row ***************************                                                                                      
               id: 1                                                                                                                                    
      select_type: SIMPLE                                                                                                                               
            table: al                                                                                                                                   
       partitions: NULL                                                                                                                                 
             type: ref                                                                                                                                  
    possible_keys: artist_id                                                                                                                            
              key: artist_id                                                                                                                            
          key_len: 4                                                                                                                                    
              ref: music.ar.artist_id                                                                                                                   
             rows: 3                                                                                                                                    
         filtered: 100.00                                                                                                                               
            Extra: NULL                                                                                                                                 
    2 rows in set, 1 warning (0.03 sec)                                                                                                                 

    二、多列索引

    根据国家和专辑类型查询专辑表中的数据,如果我们不使用多列索引,先创建单列索引:

    mysql> alter table album
        -> add index (country_id),
        -> add index (album_type_id);

    技巧:创建索引是一件非常耗时的操作,并且会阻塞其他操作。尽可能合并DML语句会获得更高的效率。

    mysql> explain select * from album where country_id = 221
        -> and album_type_id = 1G;
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: album
       partitions: NULL
             type: ref
    possible_keys: country_id,album_type_id
              key: country_id
          key_len: 3
              ref: const
             rows: 169590
         filtered: 50.00
            Extra: Using where
    1 row in set, 1 warning (0.04 sec)

    对album type使用不同值运行同一个查询,那么会使用不同的索引。

    使用多列索引,创建多列索引:

    mysql> alter table album
        -> add index m1
        -> (country_id, album_type_id);

    重新运行语句:

    mysql> explain select * from album where
        -> country_id = 221
        -> and album_type_id = 1G;
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: album
       partitions: NULL
             type: ref
    possible_keys: country_id,album_type_id,m1
              key: m1
          key_len: 7
              ref: const,const
             rows: 61784
         filtered: 100.00
            Extra: NULL
    1 row in set, 1 warning (0.06 sec)

    创建相反序列的索引:

    mysql> alter table album
        -> add index m2
        -> (album_type_id, country_id);

    再次查询,mysql还是使用m1索引。

    查找原因:

    show index from albumG;
    *************************** 1. row ***************************
            Table: album
       Non_unique: 0
         Key_name: PRIMARY
     Seq_in_index: 1
      Column_name: album_id
        Collation: A
      Cardinality: 552034
         Sub_part: NULL
           Packed: NULL
             Null:
       Index_type: BTREE
          Comment:
    Index_comment:
    *************************** 2. row ***************************
            Table: album
       Non_unique: 1
         Key_name: artist_id
     Seq_in_index: 1
      Column_name: artist_id
        Collation: A
      Cardinality: 156672
         Sub_part: NULL
           Packed: NULL
             Null:
       Index_type: BTREE
          Comment:
    Index_comment:
    *************************** 3. row ***************************
            Table: album
       Non_unique: 1
         Key_name: country_id
     Seq_in_index: 1
      Column_name: country_id
        Collation: A
      Cardinality: 202
         Sub_part: NULL
           Packed: NULL
             Null: YES
       Index_type: BTREE
          Comment:
    Index_comment:
    *************************** 4. row ***************************
            Table: album
       Non_unique: 1
         Key_name: album_type_id
     Seq_in_index: 1
      Column_name: album_type_id
        Collation: A
      Cardinality: 3
         Sub_part: NULL
           Packed: NULL
             Null:
       Index_type: BTREE
          Comment:
    Index_comment:
    *************************** 5. row ***************************
            Table: album
       Non_unique: 1
         Key_name: m1
     Seq_in_index: 1
      Column_name: country_id
        Collation: A
      Cardinality: 190
         Sub_part: NULL
           Packed: NULL
             Null: YES
       Index_type: BTREE
          Comment:
    Index_comment:
    *************************** 6. row ***************************
            Table: album
       Non_unique: 1
         Key_name: m1
     Seq_in_index: 2
      Column_name: album_type_id
        Collation: A
      Cardinality: 702
         Sub_part: NULL
           Packed: NULL
             Null:
       Index_type: BTREE
          Comment:
    Index_comment:
    *************************** 7. row ***************************
            Table: album
       Non_unique: 1
         Key_name: m2
     Seq_in_index: 1
      Column_name: album_type_id
        Collation: A
      Cardinality: 4
         Sub_part: NULL
           Packed: NULL
             Null:
       Index_type: BTREE
          Comment:
    Index_comment:
    *************************** 8. row ***************************
            Table: album
       Non_unique: 1
         Key_name: m2
     Seq_in_index: 2
      Column_name: country_id
        Collation: A
      Cardinality: 463
         Sub_part: NULL
           Packed: NULL
             Null: YES
       Index_type: BTREE
          Comment:
    Index_comment:

    技巧:创建多列索引时,交换顺序可能创建出更好的索引。

    多列索引除了优化返回的行之外还有更多用途,多列索引最左边的列可以被当作单列索引高效地使用。

    当这些列被频繁地用于聚合操作(group by)和排序操作(order by)时,最左边的列也可提升性能。

    另外,同样的查询条件下,索引的行应该尽可能短,这样可以提高效率。所以,多余的索引要去掉。

    优化器对索引的选择

    一般情况下,优化器在一个表上的一次查询只会使用一个索引。但从Mysql5.0版本开始,

    优化器可能会使用多个索引。

    1)对两个有很高基数的所有使用or操作时,优化器会执行索引的合并操作

    mysql> explain select * from artist
        -> where name='Queen' or founded=1942G;
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: artist
       partitions: NULL
             type: index_merge
    possible_keys: founded,name_index
              key: name_index,founded
          key_len: 257,2
              ref: NULL
             rows: 500
         filtered: 100.00
            Extra: Using union(name_index,founded); Using where

    2)对两个有少量唯一值的索引取交集。

    3)

    技巧:应该评估使用多列索引,还是让优化器合并索引哪个的效率更高。

    查询提示

    Mysql中少数几个查询提示会影响性能。

    1.总查询提示

    总查询提示中只有STRAIGHT_JOIN查询提示会对索引的使用有影响。该提示会让优化器

    以指定索引顺序来执行查询计划。例如:

    没有STRAIGHT_JOIN提示时:

    mysql> explain select al.name, ar.name, al.first_released from artist ar inner join album al using (artist_id) where al.name='Greatest Hits'G
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: al
       partitions: NULL
             type: ref
    possible_keys: artist_id,name,name_realease
              key: name
          key_len: 257
              ref: const
             rows: 660
         filtered: 100.00
            Extra: NULL
    *************************** 2. row ***************************
               id: 1
      select_type: SIMPLE
            table: ar
       partitions: NULL
             type: eq_ref
    possible_keys: PRIMARY
              key: PRIMARY
          key_len: 4
              ref: music.al.artist_id
             rows: 1
         filtered: 100.00
            Extra: NULL

    加入STRAIGHT_JOIN提示:

    mysql> explain select STRAIGHT_JOIN al.name, ar.name, al.first_released
        -> from artist ar inner join album al using (artist_id)
        -> where al.name='Greatest Hits'G;
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: ar
       partitions: NULL
             type: index
    possible_keys: PRIMARY
              key: name_index
          key_len: 257
              ref: NULL
             rows: 581100
         filtered: 100.00
            Extra: Using index
    *************************** 2. row ***************************
               id: 1
      select_type: SIMPLE
            table: al
       partitions: NULL
             type: ref
    possible_keys: artist_id,name,name_realease
              key: artist_id
          key_len: 4
              ref: music.ar.artist_id
             rows: 3
         filtered: 1.42
            Extra: Using where

    2.索引提示

    索引提示有三种:

    USE:使用指定的索引

    IGNORE:排除指定的索引

    FORCE:让优化器更倾向与索引扫描而不是全表扫描

    mysql> explain select * from artist where founded=1980
        -> and type='Band'G;
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: artist
       partitions: NULL
             type: ref
    possible_keys: founded,a1
              key: founded
          key_len: 2
              ref: const
             rows: 1217
         filtered: 50.00
            Extra: Using index condition; Using where
    1 row in set, 1 warning (0.03 sec)
    
    ERROR:
    No query specified
    
    mysql> explain select * from artist
        -> ignore index (founded)
        -> where founded=1980
        -> and type='Band'G;
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: artist
       partitions: NULL
             type: ref
    possible_keys: a1
              key: a1
          key_len: 1
              ref: const
             rows: 290550
         filtered: 0.96
            Extra: Using where

    技巧:数据量随着时间的变化会改变某些查询的有效性,因此在硬编码的sql语句中加入查询提示是最后考虑的方案。

    索引的负面影响

    1.影响写操作的性能

    2.占用额外的磁盘空间,从而影响备份和恢复策略

    3.低效索引占用很大的内存空间

    覆盖索引

    一个覆盖索引必须包含指定表上包括where,order by,group by以及select语句中的所有列。

    使用覆盖索引可以进一步提升查询性能。

    技巧:有多种理由可以说服用户不要使用select *。覆盖索引就是原因之一。

    另外,由于覆盖索引包含的列很可能很多,因此创建覆盖索引很可能会影响整体

    性能。覆盖索引对于那些使用很多较小长度的主码和外键约束的大型规范化模式

    来说是理想的优化方式。

    局部索引

    把一个索引剪切出开头一部分。例如:

    mysql> alter table artist
        -> drop index name_index,
        -> add index name_part(name(20));
    
    mysql> explain select * from artist
        -> where name like 'Queen%'G;
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: artist
       partitions: NULL
             type: range
    possible_keys: name_part
              key: name_part
          key_len: 22
              ref: NULL
             rows: 93
         filtered: 100.00
            Extra: Using where
    1 row in set, 1 warning (0.03 sec)
    Simple is important!
  • 相关阅读:
    结构体
    out传值
    函数
    数组
    计算公式
    MYSQLinsert速度过慢
    Centos6.4 本地yum源配置
    JProfiler解决Java服务器的性能跟踪
    Hessian Servlet和Hessian Spring的简单应用
    Jetty实战之 安装 运行 部署
  • 原文地址:https://www.cnblogs.com/Shadowplay/p/8652102.html
Copyright © 2011-2022 走看看