zoukankan      html  css  js  c++  java
  • MySQL 之 索引原理与慢查询优化

    当考虑到数据多的时候,并且要加速查询时候,就不得不 用到索引了。

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

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

    索引方法:

    1.B+TREE 索引

    B+树是一种经典的数据结构,由平衡树二叉查找树结合产生,它是为磁盘或其它直接存取辅助设备而设计的一种平衡查找树,在B+树中,所有的记录节点都是按键值大小顺序存放在同一层的叶节点中,叶节点间用指针相连,构成双向循环链表,非叶节点(根节点、枝节点)只存放键值,不存放实际数据。下面看一个2层B+树的例子:

     系统从磁盘读取数据到内存时是以磁盘块(block)为基本单位的,位于同一磁盘块中的数据会被一次性读取出来,而不是按需读取。InnoDB 存储引擎使用页作为数据读取单位,页是其磁盘管理的最小单位,默认 page 大小是 16kB。

    注意:磁盘块存储的索引要尽量多,这样可以节省时间;

    索引的类型:

    1.普通索引,起加速查询

    #创建表同时添加name字段为普通索引
    create table tb1(
       id int not null auto_increment primary key,
       name varchar(100) not null,
       index idx_name(name)  # index  索引的名字(添加索引的字段)  
    );
    
    #删除索引
    drop index 索引的名字 on 表(加索引的字段)
    #查询索引
    show index from 表;
    

    2.唯一索引 ,加速查询和唯一约束(可含一个Null值)

    create table tb2(
      id int not null auto_increment primary key,
      name varchar(50) not null,
      age int not null,
      unique index idx_age (age)   
    )
    
    create unique index idx_age on tb2(age);
    

    3.主键索引 , 加速查询和唯一约束(不可含Null)  

     

    #创建表的时候添加主键索引
    
    #方式一:
    create table tb3(
       id int not null auto_increment primary key,
       name varchar(50) not null,
       age int default 0 
    );
    
    #方式二:
    create table tb3(
       id int not null auto_increment,
       name varchar(50) not null,
       age int default 0 ,
       primary key(id)
    );
    
    
    #已经创建表后添加索引
    alter table tb3 add primary key(id);
    
    #删除索引
    alter table tb3 drop  primary key
    #如果当前主键为自增主键,则不能直接删除.需要先修改自增属性,再删除
    
    alter table tb3 modify id int ,drop primary key;
    4.组合索引

    组合索引是将n个列组合成一个索引;

    create table tb4(
      id int not null ,
      name varchar(50) not null,
      age int not null,
      index idx_name_age (name,age)   
    )
    
    
    create index idx_name_age on tb4(name,age);
    

      

    4.聚合索引和辅助索引 

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

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

    辅助索引叶节点存放的是主键值,获得主键值后,再从聚集索引中查找整行数据

    总结二者区别:

      相同的是:不管是聚集索引还是辅助索引,其内部都是B+树的形式,即高度是平衡的,叶子结点存放着所有的数据。

      不同的是:聚集索引叶子结点存放的是一整行的信息,而辅助索引叶子结点存放的是单个索引列信息.

    即 聚集索引 的查询速度要比辅助索引快;

    正确使用索引

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

    #1. 范围查询(>、>=、<、<=、!= 、between...and)
        #1. = 等号
        select count(*) from userinfo where id = 1000 -- 执行索引,索引效率高
        
        #2. > >= < <= 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; -- 执行索引,区间范围越大,索引效率越低
        
       #3. != 不等于
       select count(*) from userinfo where id != 1000;  -- 索引范围大,索引效率低
       
       
    #2.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%'; 
    
    #3. or 
        select count(*) from userinfo where id = 12334 or email ='xxxx'; -- email不是索引字段,索引此查询全表扫描
        
        #例外:当or条件中有未建立索引的列才失效,以下会走索引
        select count(*) from userinfo where id = 12334 or name = 'alex3'; -- id 和 name 都为索引字段时, or条件也会执行索引
    
    #4.使用函数
        select count(*) from userinfo where reverse(name) = '5xela'; -- name索引字段,使用函数时,索引失效
        
        #例外:索引字段对应的值可以使用函数,我们可以改为一下形式
        select count(*) from userinfo where name = reverse('5xela');
    
    #5.类型不一致
        #如果列是字符串类型,传入条件是必须用引号引起来,不然...
        select count(*) from userinfo where name = 454;
            
        #类型一致
        select count(*) from userinfo where name = '454';
    
    #6.order by
        #排序条件为索引,则select字段必须也是索引字段,否则无法命中  
        select email from userinfo ORDER BY name DESC; -- 无法命中索引
    
        select name from userinfo ORDER BY name DESC;  -- 命中索引
            
        #特别的:如果对主键排序,则还是速度很快:
        select id from userinfo order by id desc;
    

     

    组合索引

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

    组合索引的好处:1."一个顶三个"。建了一个(a,b,c)的组合索引,那么实际等于建了(a),(a,b),(a,b,c)三个索引,

    因为每多一个索引,都会增加写操作的开销和磁盘空间的开销。

    2.索引列越多,通过索引筛选出的数据越少,即查询的速度就越快。

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

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

      

    注意事项

     

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

      

    查询计划

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

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

      

    慢日志查询

     将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,则会记录所有没有利用索引的查询.
    

      

    #.查询慢日志配置信息 :
    show variables LIKE '%query%'
    #.修改配置信息
    SET GLOBAL slow_query_log  = on;
    
    #查看慢日志记录的方式
    show variables like '%log_output%';
    #设置慢日志在文件和表中同时记录
    set global log_output='FILE,TABLE';
    
    
    #查询时间超过10秒就会记录到慢查询日志中
    SELECT sleep(10)
    #查看表中的日志
    select * from mysql.slow_log;
    

      

    大数据量分页优化 

     执行此段代码:

    select * from userinfo limit 3000000,10;
    

      优化方案:

    1.简单粗暴,就像百度一样,就是不允许查看非常靠后的数据,百度就只能查到72页,72页以后的数据就不能让你查看了,这种方式是从业务上解决了。

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

    select * from userinfo where id>3000000 limit 10;
    

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

    3.最后第三种方法:延迟关联

    因为是*使得数据查询的慢,因此变为具体的字段名,因为select  *  所以mysql在沿着id主键走的时候要回行拿数据,走一下拿一下数据;

    select id from userinfo limit 3000000,10;
    

    你会发现时间缩短了一半;然后我们在拿id分别去取10条数据就行了;

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

      

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

  • 相关阅读:
    ETCD集群部署 和flanne网络插件通信原理介绍
    prometheus02 nodeexporter部署及使用
    docker容器的存储资源(volume)
    ActionScript 3.0 事件机制小结
    ActionScript 3.0 装饰器模式实例
    固定头和底,中间部分自适应布局
    ActionScript 3.0 MVC模式小实例
    A*算法的Actionscript3.0实例
    [Database]sql server 2008 不允许保存更改,您所做的更改要求删除并重新创建以下表 的解决办法
    在phpstorm中svn的使用
  • 原文地址:https://www.cnblogs.com/zenghui-python/p/10590358.html
Copyright © 2011-2022 走看看