zoukankan      html  css  js  c++  java
  • 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:三种方法最先考虑第一种,其次第二种,第三种是别无选择。

  • 相关阅读:
    面向对象的继承关系体现在数据结构上时,如何表示
    codeforces 584C Marina and Vasya
    codeforces 602A Two Bases
    LA 4329 PingPong
    codeforces 584B Kolya and Tanya
    codeforces 584A Olesya and Rodion
    codeforces 583B Robot's Task
    codeforces 583A Asphalting Roads
    codeforces 581C Developing Skills
    codeforces 581A Vasya the Hipster
  • 原文地址:https://www.cnblogs.com/believepd/p/9774116.html
Copyright © 2011-2022 走看看