• mysql索引失效


    mysql索引使用总结

    1 使用索引注意

    (1)越小的数据类型通常更好:越小的数据类型通常在磁盘、内存和CPU缓存中都需要更少的空间,处理起来更快。
    (2)简单的数据类型更好:整型数据比起字符,处理开销更小,因为字符串的比较更复杂。在MySQL中,应该用内置的日期和时间数据类型,而不是用字符串来存储时间;以及用整型数据类型存储IP地址。
    (3)尽量避免NULL:应该指定列为NOT NULL,除非你想存储NULL。在MySQL中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值。
    (4)索引不会包含有NULL值的列。

    2 索引不能使用排查

    但是如果是同样的sql如果在之前能够使用到索引,那么现在使用不到索引,以下几种主要情况:

    1. 随着表的增长,where条件出来的数据太多,大于15%,使得索引失效(会导致CBO计算走索引花费大于走全表)

    2. 统计信息失效,需要重新搜集统计信息

    3. 索引本身失效,需要重建索引

    3 不会用到索引

    下面是一些不会使用到索引的原因
    1) 没有查询条件,或者查询条件没有建立索引;
    2) 在查询条件上没有使用引导列
    3) 查询的数量是大表的大部分,应该是30%以上。 
    4) 索引本身失效 
    5) 在索引列上使用mysql的内置函数
    6) 对小表查询,数据量小
    7) CBO计算走索引花费过大的情况。其实也包含了上面的情况,这里指的是表占有的block要比索引小。 
    8)隐式转换导致索引失效.这一点应当引起重视.也是开发中经常会犯的错误. 由于表的字段tu_mdn定义为varchar2(20), 
    但在查询时把该字段作为number类型以where条件传给mysql,这样会导致索引失效. 
    错误的例子:select * from test where tu_mdn=13333333333; 
    正确的例子:select * from test where tu_mdn='13333333333'; 
    9)对索引列进行运算导致索引失效,我所指的对索引列进行运算包括(+,-,*,/,! 等) 
    错误的例子:select * from test where id-1=9; 
    正确的例子:select * from test where id=10; 
    10)使用mysql内部函数导致索引失效.对于这样情况应当创建基于函数的索引. 
    错误的例子:select * from test where round(id)=10; 
    说明,此时id的索引已经不起作用了 正确的例子:首先建立函数索引, 
    create index test_id_fbi_idx on test(round(id)); 
    然后 select * from test where round(id)=10; 这时函数索引起作用了

    11)如果MySQL估计使用索引比全表扫描更慢,则不使用索引。例如如果列key_part1均匀分布在1到100之间,查询时使用索引就不是很好

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

    12)如果使用MEMORY/HEAP表并且where条件中不使用“=”进行索引列,那么不会用到索引。Heap表只有在“=”的条件下会使用索引。因为用的是哈希索引。

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

    复制代码
    mysql> show index from test1G;
    *************************** 1. row ***************************
            Table: test1
       Non_unique: 1
         Key_name: inx_id_name
     Seq_in_index: 1
      Column_name: name
        Collation: A
      Cardinality: 552589
         Sub_part: NULL
           Packed: NULL
             Null: YES
       Index_type: BTREE
          Comment: 
    Index_comment: 
    *************************** 2. row ***************************
            Table: test1
       Non_unique: 1
         Key_name: inx_id_name
     Seq_in_index: 2
      Column_name: id
        Collation: A
      Cardinality: 567855
         Sub_part: NULL
           Packed: NULL
             Null: 
       Index_type: BTREE
          Comment: 
    Index_comment: 
    2 rows in set (0.00 sec)
    
    ERROR: 
    No query specified
    
    mysql> 
    复制代码

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

    复制代码
    mysql> explain extended select * from test1 where name='name100' or dept='dept100';
    +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
    | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
    +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
    |  1 | SIMPLE      | test1 | NULL       | ALL  | inx_id_name   | NULL | NULL    | NULL | 769014 |    19.00 | Using where |
    +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
    1 row in set, 2 warnings (0.00 sec)
    
    mysql>
    复制代码

    14)如果将要使用的索引列不是复合索引列表中的第一部分,则不会使用索引

    如下例子:可见虽然在id上面建有复合索引,但是由于id不是索引的第一列,那么在查询中这个索引也不会被MySQL采用。(索引的最左匹配原则)

    复制代码
    mysql> explain select * from test1 where id=1;
    +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
    | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
    +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
    |  1 | SIMPLE      | test1 | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 787947 |    10.00 | Using where |
    +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
    1 row in set, 1 warning (0.00 sec)
    
    mysql>
    复制代码

    15)如果like是以%开始,可见虽然在name上面建有索引,但是由于where 条件中like的值的“%”在第一位了,那么MySQL也会采用这个索引。

    如果WHERE子句的查询条件里使用了比较操作符LIKE和REGEXP,MYSQL只有在搜索模板的第一个字符不是通配符的情况下才能使用索引。比如说,如果查询条件是LIKE 'abc%',MYSQL将使用索引;如果条件是LIKE '%abc',MYSQL将不使用索引。

    17)独立的列(对列变量需要计算(聚合运算、类型转换等))

    独立的列是指索引列不能是表达式的一部分,也不是是函数的参数。例如以下两个查询无法使用索引:

    1)表达式:  select actor_id from sakila.actor where actor_id+1=5;

    2)函数参数:select ... where TO_DAYS(CURRENT_DATE) - TO_DAYS(date_col)<=10;应该把列计算转换成常量计算。

    示例:

    如果列类型是字符串,但在查询时把一个数值型常量赋值给了一个字符型的列名name,那么虽然在name列上有索引,但是也没有用到。

    复制代码
    mysql> explain select * from company2 where name=294G 
    *************************** 1. row *************************** 
    id: 1 
    select_type: SIMPLE 
    table: company2 
    type: ALL 
    possible_keys: ind_company2_name 
    key: NULL 
    key_len: NULL 
    ref: NULL 
    rows: 1000 
    Extra: Using where 
    1 row in set (0.00 sec)
    复制代码

    而下面的sql语句就可以正确使用索引。

    复制代码
    mysql> explain select * from company2 where name name=‘294'G 
    *************************** 1. row *************************** 
    id: 1 
    select_type: SIMPLE 
    table: company2 
    type: ref 
    possible_keys: ind_company2_name 
    key: ind_company2_name 
    key_len: 23 
    ref: const 
    rows: 1 
    Extra: Using where 
    1 row in set (0.00 sec)
    复制代码

    18).在JOIN操作中(需要从多个数据表提取数据时),MYSQL只有在主键和外键的数据类型相同时才能使用索引,否则即使建立了 索引也不会使用

    19).在ORDER BY操作中,MYSQL只有在排序条件不是一个查询条件表达式的情况下才使用索引。尽管如此,在涉及多个数据表的查询里,即使有索引可用,那些索引在加快ORDER BY操作方面也没什么作用。

    20).不要给“性别”增加索引。如果某个数据列里包含着许多重复的值,就算为它建立了索引也不会有很好的效果。比如说,如果某个数据列里包含了净是些诸如“0/1”或“Y/N”等值,就没有必要为它创建一个索引。

    简单的说吧,不需要,因为性别,就两个值男与女(人妖不算,呵)。为这两个值建立索引是不值得的,因为无论多少条记录,建立性别的索引,最多让你的语句少检索一半。但与建立索引带来的损失比,捡芝麻丢西瓜。(可能不准确,但大意如些)。

    打个比方,数据库就好比一本新华字典,我们查数据时,可以根据拼音来查,字在字典的排序是根据拼音来排序的,我们要查一个字,可以根据拼音很快就能查到我们要查的字,这就叫作聚集索引!换句话说,聚集索引就是按照物理排序的,也因为是按物理排序的,所以一张表只能有一个聚集索引,也是最快的索引。当然,我们也可以根据部首来查,但是这种查询必须先查找到部首,然后再到检索表查到那么字,最后才能查到我们需要的字,你没办法像拼音查法一样翻翻字典就可以查到,这就叫作普通索引。普通索引可以有多个。 

    假如一本字典里全是"男"和"女"两个字,那么在检索表里也有很多个"男"和"女",这对查询帮助不大。

    21).如果对大的文本进行搜索,使用全文索引而不使用like“%...%”.

    22).如果列名是索引,使用column_name is null将使用索引。

    如下

    复制代码
    mysql> explain select * from company2 where name is nullG 
    *************************** 1. row *************************** 
    id: 1 
    select_type: SIMPLE 
    table: company2 
    type: ref 
    possible_keys: ind_company2_name 
    key: ind_company2_name 
    key_len: 11 
    ref: const 
    rows: 1 
    Extra: Using where 
    1 row in set (0.00 sec)
    复制代码

    23).不使用NOT IN和<>操作
    NOT IN和<>操作都不会使用索引将进行全表扫描。NOT IN可以NOT EXISTS代替,id<>3则可使用id>3 or id<3来代替。

    24).排序的索引问题
    mysql查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。

    25).使用短索引
    对串列进行索引,如果可能应该指定一个前缀长度。例如,如果有一个CHAR(255)的列,如果在前10个或20个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。

    26).索引不会包含有NULL值的列
    只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为NULL。

     27).使用ENUM而不是字符串

    ENUM保存的是TINYINT,别在枚举中搞一些“中国”“北京”“技术部”这样的字符串,字符串空间又大,效率又低。

    三、索引分析方法

    3.1查看索引使用情况

    如果索引正在工作,Handler_read_key的值将很高,这个值代表了一个行被索引值读的次数。

    Handler_read_rnd_next的值高则意味着查询运行低效,并且应该建立索引补救。

    复制代码
    mysql> show status like 'Handler_read%'; 
    +-----------------------+--------+
    | Variable_name         | Value  |
    +-----------------------+--------+
    | Handler_read_first    | 9      |
    | Handler_read_key      | 16     |
    | Handler_read_last     | 0      |
    | Handler_read_next     | 680908 |
    | Handler_read_prev     | 0      |
    | Handler_read_rnd      | 0      |
    | Handler_read_rnd_next | 935519 |
    +-----------------------+--------+
    7 rows in set (0.00 sec)
    
    mysql> 
    复制代码

    3.2两个简单实用的优化方法:

    分析表的语法如下:(检查一个或多个表是否有错误)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    mysql> CHECK TABLE tbl_name[,tbl_name] …[option] …option =
    { QUICK | FAST | MEDIUM| EXTENDED | CHANGED}
    mysql> check table sales;
    +--------------+-------+----------+----------+
    | Table | Op | Msg_type | Msg_text |
    +--------------+-------+----------+----------+
    | sakila.sales | check | status | OK |
    +--------------+-------+----------+----------+
    1 row in set (0.01 sec)

    优化表的语法格式:

    OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [,tbl_name]
    如果已经删除了表的一大部分,或者如果已经对含有可变长度行的表进行了很多的改动,则需要做定期优化。这个命令可以将表中的空间碎片进行合并,但是此命令只对MyISAM、BDB和InnoDB表起作用。

    1
    2
    3
    4
    5
    6
    7
    mysql> optimize table sales;
    +--------------+----------+----------+----------+
    | Table | Op | Msg_type | Msg_text |
    +--------------+----------+----------+----------+
    | sakila.sales | optimize | status | OK |
    +--------------+----------+----------+----------+
    1 row in set (0.05 sec)
  • 相关阅读:
    实战:第十二章:txt文件转xml文件
    实战:第十三章:HTTP Status 500 – Internal Server Error(解决SpringBoot架构的Web项目部署到linux系统上访问出错)
    理论:第一章:HashMap底层实现原理,红黑树,B+树,B树的结构原理,volatile关键字,CAS(比较与交换)实现原理
    【Java面试】第一章:P5级面试
    JavaSE:第十三章:一分钟了解反射
    我的收藏:第四章:八十种在线工具
    Error: Node Sass version 7.0.1 is incompatible with ^4.0.0.
    render: h => h(App)解析
    Could not find artifact org.openjfx:javafx.base:pom:11.0.0SNAPSHOT in aliyunmaven
    测试Mockjs用法的关键代码
  • 原文地址:https://www.cnblogs.com/wuchanming/p/8075840.html
Copyright © 2020-2023  润新知