zoukankan      html  css  js  c++  java
  • Mysql ==》 索引原理与慢查询优化

    一:介绍

    为何要有索引?

    回答:

    一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,

    在生产环境中,我们遇到最多的,也就是最容易出问题的,还是一些负责的查询操作,

    因此对查询语句的优化显然是重中之重。说起加速查询,就不得不提到索引了。

    什么是索引:

    回答:

    这里的索引,形象的可以理解为一本数中,里面的目录就索引。我们可以通过索引,快速查询到我们需要的数据。

    专业说法:

    索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构。索引对于良好的性能
    非常关键,尤其是当表中的数据量越来越大时,索引对于性能的影响愈发重要。
    索引优化应该是对查询性能优化最有效的手段了。索引能够轻易将查询性能提高好几个数量级。
    索引相当于字典的音序表,如果要查某个字,如果不使用音序表,则需要从几百页中逐页去查。

    二:索引的原理

    索引的原理:

    1.索引的目的在于提高查询效率。

    与我们查阅图书所用的目录是一个道理:先定位到章,然后定位到该章下的一个小节,然后找到页数。

    相似的例子还有:查字典,查火车车次,飞机航班等

    本质都是:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,

    也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。

    数据库也是一样,但显然要复杂的多,因为不仅面临着等值查询,还有范围查询(>、<、between、in)、模糊查询(like)、并集查询(or)等等。

    数据库应该选择怎么样的方式来应对所有的问题呢?

    我们回想字典的例子,能不能把数据分成段,然后分段查询呢?

    最简单的如果1000条数据,1到100分成第一段,101到200分成第二段,201到300分成第三段......这样查第250条数据,只要找第三段就可以了,一下子去除了90%的无效数据。

    但如果是1千万的记录呢,分成几段比较好?

    稍有算法基础的同学会想到搜索树,其平均复杂度是lgN,具有不错的查询性能。

    但这里我们忽略了一个关键的问题,复杂度模型是基于每次相同的操作成本来考虑的。

    而数据库实现比较复杂,一方面数据是保存在磁盘上的,另外一方面为了提高性能,

    每次又可以把部分数据读入内存来计算,因为我们知道访问磁盘的成本大概是访问内存的十万倍左右,

    所以简单的搜索树难以满足复杂的应用场景。

    三:索引的数据结构

    以上是: b+树性质

    1.索引字段要尽量的小

    2.索引的最左匹配特性

    三:mysql 索引管理

    1.功能

    1.索引的功能就是加速查找

    2.mysql中的primary key,unique, 联合唯一也都是索引

    2.mysql的索引分类

    1.普通索引:

      index: 加速查找

    2.唯一索引:

      主键索引  primary key : 加速查找+约束(不为空、不能重复)

      唯一索引 unique : 加速查找+约束 (不能重复)

    3.联合索引:

      primary key(id,name)  -----联合主键索引

      unique(id,name)          -----联合唯一索引

      index(id,name)             -----联合普通索引

    各个索引的运用场景:

    举个例子来说,比如你在为某商场做一个会员卡的系统。
    
    这个系统有一个会员表
    有下列字段:
    会员编号 INT
    会员姓名 VARCHAR(10)
    会员身份证号码 VARCHAR(18)
    会员电话 VARCHAR(10)
    会员住址 VARCHAR(50)
    会员备注信息 TEXT
    
    那么这个 会员编号,作为主键,使用 PRIMARY
    会员姓名 如果要建索引的话,那么就是普通的 INDEX
    会员身份证号码 如果要建索引的话,那么可以选择 UNIQUE (唯一的,不允许重复)
    
    #除此之外还有全文索引,即FULLTEXT
    会员备注信息 , 如果需要建索引的话,可以选择全文搜索。
    用于搜索很长一篇文章的时候,效果最好。
    用在比较短的文本,如果就一两行字的,普通的 INDEX 也可以。
    但其实对于全文搜索,我们并不会使用MySQL自带的该索引,而是会选择第三方软件如Sphinx,专门来做全文搜索。
    
    #其他的如空间索引SPATIAL,了解即可,几乎不用
    

    3.索引的两大类型 hash 与 btree

    #我们可以在创建上述索引的时候,为其指定索引类型,分两类
    hash类型的索引:查询单条快,范围查询慢
    btree类型的索引:b+树,层数越多,数据量指数级增长(我们就用它,因为innodb默认支持它)
    
    #不同的存储引擎支持的索引类型也不一样
    InnoDB 支持事务,支持行级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
    MyISAM 不支持事务,支持表级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
    Memory 不支持事务,支持表级别锁定,支持 B-tree、Hash 等索引,不支持 Full-text 索引;
    NDB 支持事务,支持行级别锁定,支持 Hash 索引,不支持 B-tree、Full-text 等索引;
    Archive 不支持事务,支持表级别锁定,不支持 B-tree、Hash、Full-text 等索引;
    

    4.创建/删除索引的语法

    方法一:创建表时
    
    create table 表名 ( 
        字段1 数据类型 [完整性约束条件...],
        字段2 数据类型 [完整性约束条件...],
        [unique | fulltext | spatial ] index | key
        [索引名]  (字段名[(长度)] [asc | desc])
        );
    
    
    方法二:create 在已存在的表上创建索引
    
    create  [unique | fulltext | spatial ] index 索引名
        on 表名 (字段名[(长度)] [asc | desc]);
    
    方法三:alter table 在已存在的表上创建索引
    
    alter table 表名 add  [unique | fulltest |spatial ] index
        索引名 (字段名[(长度)]  [asc |desc] );
    
    
    删除索引: drop  index  索引名 on 表名字;
    

    四:测试索引

    1.准备

    #1. 准备表
    create table s1(
    id int,
    name varchar(20),
    gender char(6),
    email varchar(50)
    );
    
    #2. 创建存储过程,实现批量插入记录
    delimiter $$ #声明存储过程的结束符号为$$
    create procedure auto_insert1()
    BEGIN
        declare i int default 1;
        while(i<3000000)do
            insert into s1 values(i,concat('egon',i),'male',concat('egon',i,'@oldboy'));
            set i=i+1;
        end while;
    END$$ #$$结束
    delimiter ; #重新声明分号为结束符号
    
    #3. 查看存储过程
    show create procedure auto_insert1G 
    
    #4. 调用存储过程
    call auto_insert1();
    

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

    3.加上索引之后

    五:正确使用索引

     1.并不是索引建立了就会提升速度的,索引也要依照各种情况而定。如下索引就未命中

    2.覆盖索引 与 索引合并(两个字段单独建立索引)

    #覆盖索引:
        - 在索引文件中直接获取数据
        http://blog.itpub.net/22664653/viewspace-774667/
    
    #分析
    select * from s1 where id=123;
    该sql命中了索引,但未覆盖索引。
    利用id=123到索引的数据结构中定位到该id在硬盘中的位置,或者说再数据表中的位置。
    但是我们select的字段为*,除了id以外还需要其他字段,这就意味着,我们通过索引结构取到id还不够,还需要利用该id再去找到该id所在行的其他字段值,这是需要时间的,很明显,如果我们只select id,就减去了这份苦恼,如下
    select id from s1 where id=123;
    这条就是覆盖索引了,命中索引,且从索引的数据结构直接就取到了id在硬盘的地址,速度很快
    
    #索引合并:把多个单列索引合并使用
    
    #分析:
    组合索引能做到的事情,我们都可以用索引合并去解决,比如
    create index ne on s1(name,email);#组合索引
    我们完全可以单独为name和email创建索引
    
    组合索引可以命中:
    select * from s1 where name='egon' ;
    select * from s1 where name='egon' and email='adf';
    
    索引合并可以命中:
    select * from s1 where name='egon' ;
    select * from s1 where email='adf';
    select * from s1 where name='egon' and email='adf';
    
    乍一看好像索引合并更好了:可以命中更多的情况,但其实要分情况去看,如果是name='egon' and email='adf',那么组合索引的效率要高于索引合并,如果是单条件查,那么还是用索引合并比较合理
    

      

  • 相关阅读:
    ssh登录很慢的问题
    Y480&Y580 刷slic2.1全自动教程
    re正则表达式5_*
    linux下查看内存使用情况
    检查linux网络的状况
    Linux Load average负载详细解释
    查看Linux磁盘空间大小
    Linux 批量重命名文件
    Linux 网卡丢包严重
    linux 下vi /vim 中文汉字乱码解决
  • 原文地址:https://www.cnblogs.com/zhongbokun/p/7512043.html
Copyright © 2011-2022 走看看