zoukankan      html  css  js  c++  java
  • MySQL索引基础-索引的创建以及设计原则

    MySQL索引基础-索引的创建以及设计原则

    一、MySQL索引基础

         首先,我们将从索引基础开始介绍一下什么是索引,分析索引的几种类型,并探讨一下如何创建索引以及索引设计的基本原则。
         为了测试索引,我们创建一个user表并插入几条数据,建立几个索引:

     1 CREATE TABLE `user` (
     2   `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
     3   `name` varchar(20) NOT NULL COMMENT '姓名',
     4   `gender` int(1) NOT NULL DEFAULT '0' COMMENT '性别',
     5   `age` int(3) NOT NULL COMMENT '年龄',
     6   `status` int(1) NOT NULL COMMENT '状态',
     7   `remark` varchar(255) DEFAULT NULL COMMENT '备注',
     8   PRIMARY KEY (`id`)
     9 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
    10 
    11 # 插入几条测试数据
    12 INSERT INTO `user` (`name`, `gender`, `age`, `status`, `remark`) VALUES ('lily','0','16','0','test1');
    13 INSERT INTO `user` (`name`, `gender`, `age`, `status`, `remark`) VALUES ('tom','0','20','1','test2');
    14 INSERT INTO `user` (`name`, `gender`, `age`, `status`, `remark`) VALUES ('kate','1','21','2','12');
    15 INSERT INTO `user` (`name`, `gender`, `age`, `status`, `remark`) VALUES ('jack','0','22','3','test3');
    16 INSERT INTO `user` (`name`, `gender`, `age`, `status`, `remark`) VALUES ('john','0','23','4',null);
    17 
    18 # 添加索引
    19 ALTER TABLE `user` ADD UNIQUE ( `name`);
    20 ALTER TABLE `user` ADD INDEX  index_name( `name`);
    21 ALTER TABLE `user` ADD INDEX  index_gender( `gender`);

        执行完上面sql语句,再来查看下user表结构,执行命令:desc user,结果如下图:

    1、什么是索引

    索引是帮助MySQL高效获取数据的数据结构。

    2、索引类型

    查看索引详情:show index from  table_name;

     

    1) 主键索引

        主键索引 PRIMARY KEY:它是一种特殊的唯一索引,不允许有空值。一般是在建表的时候同时创建主键索引。

        注意:一个表只有一个主键

    2) 唯一索引

        唯一索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。

        创建索引:

        ALTER TABLE table_name ADD UNIQUE (column);

        创建唯一组合索引:

        ALTER TABLE table_name ADD UNIQUE (column1,column2);

    3)普通索引

         这是最基本的索引,它没有任何限制

         创建普通索引: ALTER TABLE table_name ADD INDEX index_name (column);

         创建组合索引: ALTER TABLE table_name ADD INDEX index_name(column1,column2, column3);

    4)全文索引 (FULLTEXT)

         ALTER TABLE table_name ADD FULLTEXT (column);

         说明:索引一经创建不能修改,如果要修改索引,只能删除重建。

         删除索引:DROP INDEX index_name ON table_name;

    3、索引设计的原则

    1)适合索引的列是出现在where子句中的列,或者连接子句中指定的列

    2)基数较小的列,索引效果较差,没有必要在此列建立索引。

    3)使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间。

    4)不要过度索引。索引需要额外的磁盘空间,并降低写操作的性能。在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长。因此只保存需要的索引有利于查询即可。

    4、MySQL索引优化实战

         在介绍索引优化实战之前,首先要介绍两个与索引相关的重要概念,这两个概念对于索引优化至关重要:基数和回表。

         1)基数

         基数是数据列所包含的不同值的数量。例如,某个数据列包含值1、3、7、4、7、3,那么它的基数就是4。

         select count(distinct name),count(distinct gender) from user;

         

         user表的总行数是5,gender列的基数是2,说明gender列里面有大量重复值,name列的基数等于总行数,说明name列没有重复值,相当于主键。

         

         现在问题来了,假设name、gender列都有索引,那么下面的语句1和语句2都能命中索引吗?

        语句1:SELECT * FROM user WHERE gender = 0;

        语句2:SELECT * FROM user WHERE name = 'kate';

        我们用explain/desc来分析测试下,如下图:

        

        可以看到,语句1没有命中索引,注意filtered的值就是上面我们计算的返回记录的比例数。 语句2命中了索引index_name,因为走索引直接就能找到要查询的记录,所以filtered的值为100。

        说明:

       一般惯用的百分比界线是"30%"。也就是说,返回表中30%内的数据会走索引,返回超过30%数据就使用全表扫描。当然这个结论太绝对了,也并不是绝对的30%,只是一个大概的范围

        2)回表

        当对一个列创建索引之后,索引会包含该列的键值及键值对应行所在rowid。通过索引中记录的 rowid 访问表中的数据就叫回表。回表次数太多会严重影响SQL性能,如果回表次数太多,就不应该走索引扫描,应该直接走全表扫描。

       Explain 命令结果中的Using Index意味着不会回表,通过索引就可以获得主要的数据。Using Where则意味着需要回表取数据。

    5. 索引优化实战

    有些时候虽然数据库有索引,但是并不被优化器选择使用。

    查看索引使用的情况:

    show status like  'Handler_read%';

    Handler_read_key:如果索引正在工作,Handler_read_key的值将很高。

    Handler_read_rnd_next:数据文件中读取下一行的请求数,如果正在进行大量的表扫描,值将较高,则说明索引利用不理想。

     

    索引优化规则:

    1)如果MySQL估计使用索引比全表扫描还慢,则不会使用索引。

         返回数据的比例是重要的指标,比例越低越容易命中索引,后面所讲的关于索引优化都是建立在返回数据的比例在30%以内的基础上。

    2)前导模糊查询不能命中索引

         explain select  * from user where name like '%s%';

         可优化为使用非前导模糊查询:explain select * from user where name like 's%';

         

    3)数据类型出现隐式转换的时候不会命中索引,特别是当列类型是字符串,一定要将字符常量值引号引起来。

         如下图:

        

    4)复合索引的情况下,查询条件不包含索引列最左部分(不满足最左原则),不会命中索引。

    注意:最左原则并不是说是查询条件的顺序,而是查询条件中是否包含索引最左列字段。优化器会自动调整。

    5) union、in、or都能够命中索引,建议使用in

         查询的CPU消耗: or>in>union

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

    7)负向条件查询不能使用索引,可以优化为in查询。

         负向条件有:!=、<>、not in、not exists、not like等。

    8)范围条件查询可以命中索引。范围条件有:<、<=、>、>=、between等。

         a.  范围条件查询可以命中索引:

        

        b. 范围列可以用到索引(联合索引必须是最左前缀),但是范围列后面的列无法用到索引,索引最多用于一个范围列,如果查询条件中有两个范围列则无法全用到索引:

        

        c. 如果是范围查询和等值查询同时存在,优先匹配等值查询列的索引:

        

    9)数据库执行计算不会命中索引。

         

    10)利用覆盖索引进行查询,避免回表。

        被查询的列,数据能从索引中取得,而不用通过定位符row-locator再到row上获取,即被查询列要被所建的索引覆盖,这能够加速查询速度。

        为了方便测试,我们给status列添加上索引:ALTER TABLE `user` ADD INDEX  index_status( `status`);

        因为status字段是索引列,所以直接从索引中就可以获取值,不必回表查询:

        
         其中,Extra中的 Using Index 代从索引中查询
         当查询其他列时,就需要回表查询,这也是为什么要避免SELECT*的原因之一,如下图:

          

    11) 建立索引的列,不允许为null

        单列索引不存null值,复合索引不存全为null的值,如果列允许为null,可能会得到“不符合预期”的结果集,所以,请使用not null约束以及默认值。

        为了方便测试,我们给remark列添加上索引:ALTER TABLE `user` ADD INDEX  index_remark( `remark`);

        如下图:

         

        说明: 虽然 is null 可以命中索引,但是 null 本身就不是一种好的数据库设计,应该使用 not null 约束以及默认值。

    总结:

        a.  更新十分频繁的字段上不宜建立索引:因为更新操作会变更B+树,重建索引。这个过程是十分消耗数据库性能的。

        b.  区分度不大的字段上不宜建立索引:类似于性别、地址、类型等这种区分度不大的字段,建立索引的意义不大。因为不能有效过滤数据,性能和全表扫描相当。另外返回数据的比例在30%以外的情况下,优化器不会选择使用索引。

        c.  业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。虽然唯一索引会影响insert速度,但是对于查询的速度提升是非常明显的。另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,在并发的情况下,依然有脏数据产生。

        d.  多表关联时,要保证关联字段上一定有索引。

        e.  创建索引时避免以下错误观念索引越多越好,认为一个查询就需要建一个索引;宁缺勿滥,认为索引会消耗空间、严重拖慢更新和新增速度;抵制唯一索引,认为业务的唯一性一律需要在应用层通过“先查后插”方式解决;过早优化,在不了解系统的情况下就开始优化。

    参考链接:

    https://zhuanlan.zhihu.com/p/61687047

  • 相关阅读:
    makeObjectsPerformSelector 方法的用法
    UISearchBar控件
    iOS应用崩溃日志分析
    IOS崩溃日志分析
    iOS 中捕获程序崩溃日志
    iOS疯狂详解之imageIO完成渐进加载图片
    iOS中nil,NULL,Nil,NSNull的区别
    IOS 8 UITableView cell lines不能靠左解决方法
    Android小玩意儿-- 从头开发一个正经的MusicPlayer(三)
    Android小玩意儿-- 从头开发一个正经的MusicPlayer(二)
  • 原文地址:https://www.cnblogs.com/hld123/p/14738190.html
Copyright © 2011-2022 走看看