• MySQL之索引以及正确使用索引


    一、MySQL中常见索引类型

    • 普通索引:仅加速查询
    • 主键索引:加速查询、列值唯一、表中只有一个(不可有null)
    • 唯一索引:加速查询、列值唯一(可以有null)
    • 组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并

    索引合并:使用多个单列索引组合搜索。

    覆盖索引:select的数据列只用从索引中就能够取得,不必读取数据行;换句话说,查询列要被所建的索引覆盖。

    普通索引

    -- 创建表同时添加name字段为普通索引
    create table tb(
       id int not null auto_increment primary key,
       name char(32) not null,
       index idx_name(name)
    );
    
    -- 单独为表指定普通索引
    create index idx_name on tb(name);
    
    -- 删除索引
    drop index idx_name on tb;
    
    -- 查看索引
    show index from tb;
    View Code
    Table         # 表的名称
    
    Non_unique    # 如果索引为唯一索引,则为0,如果可以则为1
    
    Key_name     # 索引的名称
    
    Seq_in_index  # 索引中的列序列号,从1开始
    
    Column_name   # 列名称
    
    Collation     # 列以什么方式存储在索引中。在MySQL中,有值‘A’(升序)或NULL(无分类)
    
    Cardinality   # 索引中唯一值的数目的估计值
    
    Sub_part     # 如果列只是被部分地编入索引,则为被编入索引的字符的数目。如果整列被编入索引,则为NULL
    
    Packed      #指示关键字如何被压缩。如果没有被压缩,则为NULL
    
    Null       # 如果列含有NULL,则含有YES。如果没有,则该列含有NO
    
    Index_type   # 用过的索引方法(BTREE, FULLTEXT, HASH, RTREE)
    
    Comment     # 多种评注
    查看索引 --> 列介绍

    主键索引

     主键有两个功能:加速查询 和 唯一约束(不可含null)

     注意:一个表中最多只能有一个主键索引

    -- 创建表同时添加id字段为主键索引
    -- 方式一
    create table tb(
       id int not null auto_increment primary key,
       name char(4) not null
    );
    
    -- 方式二
    create table tb(
       id int not null auto_increment,
       name char(4) not null,
       primary key(id)
    );
    
    -- 给某个已经存在的表增加主键
    alter table tb add primary key(id);
    
    -- 删除主键
    -- 方式一
    alter table tb drop primary key;
    
    -- 方式二
    -- 如果当前主键为自增主键,则不能直接删除,需要先修改自增属性,再删除
    alter table tb modify id int,drop primary key;
    View Code

    唯一索引

     唯一索引有两个功能:加速查找 唯一约束(可含一个null 值)

    create table tb(
      id int not null auto_increment primary key,
      name char(4) not null,
      age int not null,
      unique index idx_age (age)
    );
    
    -- 给某个已经存在的表创建唯一索引
    create unique index idx_age on tb(age);
    View Code

    组合索引

    组合索引是将n个列组合成一个索引
    应用场景:频繁的同时使用n列来进行查询,如:select * from tb where name="pd" and id=888;

    create table tb(
        id int not null,
        name char(4) not null,
        age int not null,
        index idx_name_age (name,age)
    );
    
    -- 给某个已经存在的表创建组合索引
    create index idx_name_age on tb(name,age);
    View Code

     二、聚集索引和非聚集索引(辅助索引)

     数据库中的 B+tree 索引可以分为:聚集索引和非聚集索引

     聚集索引:innodb表/索引组织表,即表中数据按主键B+树存放,叶子节点直接存放整条数据,每张表只能有一个聚集索引。

    ①当你定义一个主键时,innodb 存储引擎则把它当做聚集索引;

    ②如果你没有定义一个主键,则 innodb 定位到第一个唯一索引,且该索引的所有列值均飞空的,则将其当做聚集索引;

    ③如果表没有主键或合适的唯一索引,innodb 会产生一个隐藏的行ID值6字节的行ID聚集索引。

    补充:由于实际的数据页只能按照一颗B+tree进行排序,因此每张表只能有一个聚集索引,聚集索引对于主键的排序和范围查找非常有利。

    非聚集索引(辅助索引):指叶节点不包含行的全部数据,叶节点除了包含键值之外,还包含一个书签连接,通过该书签再去找相应的行数据。

    innodb存储引擎辅助索引获得数据的查找方式如下:

    从上图中可以看出,辅助索引叶节点存放的是主键值,获得主键值后,再从聚集索引中查找整行数据。
    举个例子,如果在一颗高度为3的辅助索引中查找数据,首先从辅助索引中获得主键值(3次IO),接着从高度为3的聚集索引中查找以获得整行数据(3次IO),总共需6次IO。一个表上可以存在多个辅助索引。

    聚集索引与辅助索引区别:

    • 相同的是:不管是聚集索引还是辅助索引,其内部都是 B+tree 形式,即高度是平衡的,叶子结点存放着所有的数据。
    • 不同的是:聚集索引叶子结点存放的是一整行的信息,而辅助索引叶子结点存放的是单个索引字段信息。

    何时使用聚集索引或非聚集索引(重要!!!):

    三、测试索引

    1、创建表

    create table userinfo(
        id int not null,
        name varchar(16) default null,
        age int,
        gender char(1) not null,
        email varchar(32) default null
    )engine=myisam default charset=utf8;
    View Code

    注意:MYISAM存储引擎不产生引擎事务,数据插入速度极快,为方便快速插入测试数据,等我们插完数据,再把存储类型修改为InnoDB。

    2、创建存储过程

    create procedure insert_userinfo(in num int)
    begin
        declare i int default 0;
        declare n int default 1;
        -- 循环进行数据插入
        while n<=num do
            set i=rand()*50;
            insert into userinfo(id,name,age,gender,email) values(n,concat("pink",i),rand()*50,if(i%2=0,"女","男"),concat("pink",n,"@qq.com"));
            set n=n+1;
        end while;
    end;
    View Code

    3、调用存储过程,插入500万条数据

    call insert_userinfo(5000000);

    4、修改引擎为INNODB

    alter table userinfo engine=innodb;

    5、测试索引

    ①在没有索引的前提下测试查询速度

    select * from userinfo where id=4567890;

    注意:无索引情况,mysql根本就不知道id等于4567890的记录在哪里,只能把数据表从头到尾扫描一遍,此时有多少个磁盘块就需要进行多少IO操作,所以查询速度很慢。

    ②在表中已经存在大量数据的前提下,为某个字段段建立索引,建立速度会很慢

    create index idx_id on userinfo(id);

    ③在索引建立完毕后,以该字段为查询条件时,查询速度提升明显

    select * from userinfo where id=4567890;

    注意:

    1. mysql先去索引表里根据 b+树 的搜索原理很快搜索到id为4567890的数据,IO大大降低,因而速度明显提升
    2. 我们可以去mysql的data目录下找到该表,可以看到添加索引后该表占用的硬盘空间多了
    3. 如果使用没有添加索引的字段进行条件查询,速度依旧会很慢(如下图)

    四、正确使用索引

    数据库表中添加索引后确实会让查询速度起飞,但前提必须是正确的使用索引来查询,如果以错误的方式使用,则即使建立索引也会不奏效。
    即使建立索引,索引也不会生效,例如:

    -- 范围查询(>、>=、<、<=、!= 、between...and)
        -- = 等号
        select count(*) from userinfo where id=1000-- 执行索引,索引效率高
        
        -- >、>=、<、<=、between...and 区间查询
        select count(*) from userinfo where id<100;   -- 执行索引,区间范围越小,索引效率越高
        select count(*) from userinfo where id>100;   -- 执行索引,区间范围越大,索引效率越低
        select count(*) from userinfo where id between 10 and 500000; -- 执行索引,区间范围越大,索引效率越低
        
        -- != 不等于
        select count(*) from userinfo where id!=1000; -- 索引范围大,索引效率低
       
     
    -- like "%xx%"
        -- 为name字段添加索引
        create index idx_name on userinfo(name);
        
        select count(*) from userinfo where name like "%xxxx%";  -- 全模糊查询,索引效率低
        select count(*) from userinfo where name like "%xxxx";   -- 以什么结尾模糊查询,索引效率低
         -- 例外:当like使用以什么开头,索引效率高
        select * from userinfo where name like "xxxx%";
    
    
    -- or select count(*) from userinfo where id=1000 or email="xx"; -- email不是索引字段,索引此查询全表扫描 -- 例外:当or条件中有未建立索引的列才失效,以下会走索引 select count(*) from userinfo where id=1000 or name="pink3"; -- id和name都为索引字段时,or条件也会执行索引
    -- 使用函数 select count(*) from userinfo where reverse(name)="1knip"; -- name索引字段,使用函数时,索引失效 -- 例外:索引字段对应的值可以使用函数,我们可以改为以下形式 select count(*) from userinfo where name=reverse("1knip");
    -- 类型不一致 -- 如果列是字符串类型,传入条件是必须用引号引起来 select count(*) from userinfo where name=123; -- -- 类型一致 select count(*) from userinfo where name="123"; --
    -- order by -- 排序条件为索引,则select字段必须也是索引字段,否则无法命中 select email from userinfo order by name desc; -- 无法命中索引 select name from userinfo order by name desc; -- 命中索引

    五、组合索引

    组合索引:是指对表上的多个列组合起来做一个索引。

    组合索引好处,简单的说有两个主要原因:

    • 一个顶三个;建了一个(a,b,c)的组合索引,那么实际等于建了(a),(a,b),(a,b,c)三个索引,因为每多一个索引,都会增加写操作的开销和磁盘空间的开销。对于大量数据的表,这可是不小的开销!
    • 索引列越多,通过索引筛选出的数据越少。有1000W条数据的表,有如下sql:select * from table where a=1 and b=2 and c=3,假设每个条件可以筛选出10%的数据,如果只有单值索引,那么通过该索引能筛选出1000W*10%=100w条数据,然后再回表从100w条数据中找到符合 b=2 and c= 3 的数据,然后再排序,再分页;如果是组合索引,通过索引筛选出1000w*10%*10%*10%=1w,然后再排序、分页,哪个更高效,一眼便知 。

    最左匹配原则:从左往右依次使用生效,如果中间某个索引没有使用,那么断点前面的索引部分起作用,断点后面的索引没有起作用。

    select * from tb where a=1 and b=2 and c=3;
    -- abc三个索引都在where条件里面用到了,而且都发挥了作用
    
    select * from tb where c=3 and b=2 and a=1;
    -- 这条语句列出来只想说明mysql没有那么笨,where里面的条件顺序在查询之前会被mysql自动优化,效果跟上一句一样
    
    select * from tb where a=1 and c=3;
    -- a用到索引,b没有用,所以c是没有用到索引效果的
    
    select * from tb where a=1 and b>2 and c=3;
    -- a用到了,b也用到了,c没有用到,这个地方b是范围值,也算断点,只不过自身用到了索引
    
    select * from tb where a>1 and b=2 and c=3;
    -- a用到了,b没有使用,c没有使用
    
    select * from tb where a=1 order by b;
    -- a用到了索引,b在结果排序中也用到了索引的效果
    
    select * from tb where a=1 order by c;
    -- a用到了索引,但是c没有发挥排序效果,因为中间断点了
    
    select * from tb where b=2 order by a;
    -- b没有用到索引,排序中a也没有发挥索引效果

    、注意事项(重要)

    1、避免使用select *
    2、其他数据库中使用count(1)或count(列)代替count(*),而mysql数据库中count(*)经过优化后,效率与前两种基本一样
    3、创建表时尽量时 char 代替 varchar
    4、表的字段顺序固定长度的字段优先
    5、组合索引代替多个单列索引(经常使用多个条件查询时)
    6、使用连接(JOIN)来代替子查询(Sub-Queries)
    7、不要有超过4个以上的表连接(JOIN)
    8、优先执行那些能够大量减少结果的连接
    9、连表时注意条件类型需一致
    10、索引散列值不适合建索引,例:性别不适合

    七、慢查询日志

    慢查询日志:将mysql服务器中影响数据库性能的相关SQL语句记录到日志文件,通过对这些特殊的SQL语句分析,改进以达到提高数据库性能的目的。

    慢查询日志参数:

    long_query_time               # 设定慢查询的阀值,超出设定值的SQL即被记录到慢查询日志,缺省值为10s
    slow_query_log                # 指定是否开启慢查询日志
    log_slow_queries              # 指定是否开启慢查询日志(该参数已经被slow_query_log取代,做兼容性保留)
    slow_query_log_file           # 指定慢日志文件存放位置,可以为空,系统会给一个缺省的文件host_name-slow.log
    log_queries_not_using_indexes # 如果值设置为ON,则会记录所有没有利用索引的查询

    查看MySQL慢日志信息:

    -- 查询慢日志配置信息
    show variables like "%query%";
    -- 修改配置信息
    set global slow_query_log = on;

    查看不使用索引参数状态:

    -- 显示参数  
    show variables like "%log_queries_not_using_indexes";
    -- 开启状态
    set global log_queries_not_using_indexes = on;

    查看慢日志显示的方式:

    -- 查看慢日志记录的方式
    show variables like "%log_output%";
    -- 设置慢日志在文件和表中同时记录
    set global log_output="FILE,TABLE";

    测试慢查询日志:

    -- 查询时间超过10秒就会记录到慢查询日志中
    select sleep(3) from userinfo;
    -- 查看表中的日志
    select * from mysql.slow_log;

    八、执行计划

    explain + 查询SQL:用于显示SQL执行信息参数,根据参考信息可以进行SQL优化。

    explain  select count(*) from userinfo where  id=1;

    执行计划:让mysql预估执行操作(一般正确)。
    
    type:查询计划的连接类型, 有多个参数,先从最佳类型到最差类型介绍
    性能:null > system/const > eq_ref > ref > ref_or_null > index_merge >  range > index >  all 
    
    慢:
        explain select * from userinfo where email="pink";
        type: ALL(全表扫描)
        特别的: select * from userinfo limit 1;
        
    快:
        explain select * from userinfo where name="alex";
        type: ref(走索引)

    EXPLAIN 参数详解

    九、大数据量分页优化

    执行此段代码:

    select * from userinfo limit 3000000,10;

    优化方案:

    1、简单粗暴,不允许查看这么靠后的数据。比如百度就是这样的,最多翻到76页就不让你翻了,这种方式就是从业务上解决。

    2、在查询下一页时把上一页的行id作为参数传递给客户端程序,然后sql就改成了

    select * from userinfo where id>3000000 limit 10;

    这条语句执行也是在毫秒级完成的,id>300w 其实就是让mysql直接跳到这里了,不用依次在扫描全面所有的行。

    3、延迟关联

    分析一下这条语句为什么慢,慢在哪里:

    select * from userinfo limit 3000000,10;

    慢就慢在这个 * 里面,这个表除了id主键肯定还有其他字段,比如 name、age 之类的,因为select  *  所以mysql在沿着id主键走的时候要回行拿数据,走一下拿一下数据;如果把语句改成:

    select id from userinfo limit 3000000,10;

    你会发现时间缩短了很多;然后我们在拿id分别去取10条数据就行了。语句就改成了这样:

    select userinfo.* from userinfo inner join
    (select id from userinfo limit 3000000,10) as tmp
    on tmp.id=userinfo.id;

    PS:三种方法最先考虑第一种,其次第二种,第三种是别无选择。

  • 相关阅读:
    Django笔记(2)Json字段处理
    jvm 启动参数设置(转载)
    消息中间件及WebSphere MQ入门(转载)
    Ubuntu下Tomcat绑定80端口(zz)
    idea+tomcat 端口占用
    内存溢出和内存泄漏的区别(ZZ)
    Ubuntu上搭建Hadoop环境(单机模式+伪分布模式) (转载)
    ubuntu 安装jdk7小结(转载)
    ubuntu下安装maven(转载)
    CXF wsdl2java (转载)
  • 原文地址:https://www.cnblogs.com/believepd/p/9774116.html
Copyright © 2020-2023  润新知