zoukankan      html  css  js  c++  java
  • 索引,索引优化,mysql索引失效场景

    索引

    保证数据完整性。

    关注索引的两个点:树和有序(树可以定位索引的起点,有序可以定位索引的终点)

    • 1.每个表都是一个索引组织表(集群表)
      • 以主键来组织的一个表
      • 主键索引
    • 2.其他索引都是二级索引
      • 每个二级索引上都有主键列
    • 3.对于每个唯一约束,系统会自动在这个约束上建一个唯一索引!
      • 建立外键时,也会自动建立外键索引!

    eg:建立一张表:

    > create table  t2(id int,name varchar(20),bir_th data, constraint primary key (id),constraint unique (name));
    #对id列主键约束/id列作为主键,name列作唯一约束
    > show create table t2 G   #看t2表建立时的语法
    > show index from t2;   #显示t2表的索引
    > insert into t2 values(1,'skj','2011-11-12');
    如果插入的新数据id列或者name列与已有的值相同,就不允许建立!!
    > create table t3(id int,name varchar(20),bir_th data, constraint primary key (id),constraint foreign key (name) references t2 (name));
    #给新建的t3表的name列建立外键索引,到t2表的name列。

    给外表t3插入数据时,插入的name列的值必须是主表t2里已经存在的!!
    对主表做delete时,外表是有影响的;
    对主表update时,外表也是有影响的。

    PS:级联删除:
    删除主表的数据时,关联的从表数据也删除,则需要在建立外键约束的后面增加on delete cascade 或on delete set null,前者是级联删除,后者是将从表的关联列的值设置为null。
    在主表删除一行数据并且从表有对主表的引用时:

    • ①restrict:系统不允许这个删除操作。
    • ②cascade:顺带着也会删除从表上面引用的那些数据行。
    • ③set null:主表删除时,从表上面引用的那些数据行外键值会置空。
    • ④no action:主表随便删,对从表不作任何访问。

    一般将外键消灭,因为批量导入数据的时候要大量访问主表,而且会锁主表。


    索引的功能:提高访问的速度

    • 通过索引访问表的特点:
      • 1.通过扫描索引,找到我们要访问的行
      • 2.需要几行,访问几行
    • 全表扫描:
      • 1.访问所有的行,使用条件过滤掉不满足条件的行
      • 2.过滤的数据,就是额外消耗的资源

    customer表引用了district表(区域表)和warehouse表(仓库):
    这里写图片描述

    select c.c_first,c.c_last,c.c_city,o.o_id,o_entry_d
    from customer c,orders o
    where o.o_d_id=c.c_d_id and o.o_w_id=c.c_w_id and o.o_c_id=c.c_id and c.c_id=1 and c_w_id=1;

    select …
    from 主表,外表
    where 主表.主键=外表.外键(主外键关联条件)
    and 主表的约束条件 | 外表的约束条件

    没加 c_w_id=1 时 SQL的执行计划:
    这里写图片描述
    可以看到,走customer表时,没有走索引,而且访问的行巨大,必然执行起来慢。
    explain 中id大的先执行,相同的按顺序执行
    这里写图片描述
    (因为customer表是三个列作主键索引。而我们只用了一个列,所以,再加一个试试)
    加了 c_w_id=1 后:
    这里写图片描述
    很明显rows的值变小了很多,再执行,发现执行速度变快了好多!


    使用索引的几个最经典的场合

    • 1.有where条件(select、update、delete)
    • 2.表连接(主键本身有索引,外键本身有索引)
    • 3.去除排序

    表连接模型

    其中一个表上总有严格的约束条件,这个表作为整个SQL的起点
    针对这个模型,我们的索引优化规则:

    • 1.严格约束条件( where )要走索引
    • 2.主外键走索引

    多列索引使用规则

    • 1.前导列必须被使用,出现在where条件中
    • 2.后面的列要起作用,前面的列必须出现在where条件中(空列,5.5和5.6不一样,5.6不害怕有空列)

    多列索引的致命弱点

    • where… and条件中,中间不能有空列(不然后面的条件不会成为索引条件,而是成为前面条件索引出的表的过滤条件)
    • 低效索引执行起来很糟糕,建立的时候尽量将选择性高(cardinality)的列放到前面(5.6)

    多表连接书写方法

    select …
    from …
    where 主外键关系 and 限制条件 //交易系统来说,总是访问少量的数据
    group by
    having
    order by
    注意:一个sql,先执行from…where !!


    作业:写出下面的SQL,审核索引,建立合适的索引
    1、某一个用户所有的订单信息

    SELECT c.c_first,c.c_last,c.c_cicy,c.c_credit,o.o_id,o.o_entry_id 
    FROM customer c,orders o
    WHERE c.c_id=o.o_c_id AND c.c_d_id=o.o_d_id AND c.c_w_id=o.o_w_id AND c.c_first='5QFf7iBCexBVLSad' AND c.c_last='BARBARBAR';

    然后explain看执行计划
    这里写图片描述
    发现改善了很多。
    2、某一个用户所有的订单详情
    3、某一个用户在某个仓库所有的订单以及订单详情
    4、某一个地区的用户所有的订单和订单详情
    5、某一个商品的库存信息
    6、某一商品在某一仓库的商品信息
    7、某一个物品在某一仓库的库存信息
    8、某一个用户购买某一个商品的订单信息


    建索引的前提:列的唯一值(基数 cardinality)数量足够高

    在一个列上建立索引的几个必备条件:

    • 1.主键,唯一键,外键 必须建立索引。
    • 2.列上有where条件。
    • 3.列的选择性很高,基数很高,唯一值的数量很高。
    • 4.参考执行计划。

    MySQL统计信息有哪些

    • ①表的行数
    • ②索引列的基数

    建索引:

    alter table customer add index i_customer_first on customer(c_first);

    处理糟糕的SQL流程

    • 1.抓取SQL,格式化SQL(美化),关注from where条件,主外键关系
    • 2.看执行计划,主外键关联(!!!),严格限制条件,是否有全表扫描和低效索引
    • 3.查看索引建立情况

    SQL执行过程

    • 1.io:io资源
      • 糟糕的SQL
    • 2.内存读写:占cpu资源,cpu会高
      • 糟糕的SQL
    • 3.用户线程空间内排序,分组聚合:io,一般很小
      • 配置参数过低
    • 4.网络传送:网络资源

    糟糕的SQL会导致发生的事情

    • 1.全表扫描
    • 2.低效索引

    多列索引

    假如有三个列,按第一个列排序,第一列值相同的按第二列排序,第二列相同的按第三列排序,所以多列索引只有第一列是有序的!索引将选择性高的放到第一列!

    多列索引的高效使用

    • 1.where条件中必须有多列索引的第一个列;
    • 2.尽量将cardinality(基数)高的放到前面,否则索引本身的效率不高,但是不影响表的访问效率;
    • 3.索引的where条件必须是and.

    多列索引的误区

    有时使用a列作为where条件,有时用b列,有时用c列,有时用ac,有时用bc…问:怎样建索引?

    错误方法:建立abc索引,这样只是对a做了索引!!
    正确做法:ac建一个,bc建一个,c建一个,建立这三个即可。

    索引访问表的效率:索引访问的效率+表的访问效率

    使用索引去除排序例子

    对下面的sql添加了一条索引,大大提高了查询速度:

    alter table customer add index (c_id);

    这里写图片描述

    使用索引去除排序:

    • 1.改写SQL,改写order by 列(与开发商量)
    • 2.增加索引,配合 force hints 强制走排序索引

    使用索引去除排序的应用场景

    • 1.产生极大的数据量的排序,造成严重sort_merge_passes
    • 2.不能通过增加sort_buffer_size来解决
    • 3.尽量不要使用索引解决排序问题(有坑,可以不进行排序,但是过滤数据时可能会大大降低效率,产生全表扫描),而要使用索引来解决过滤数据的问题

    聚合操作

    在group by 分组列上怎样来消除分组聚合产生的排序?
    答:在分组的列上建立索引,来消除分组聚合产生的排序行为
    这里写图片描述
    这里写图片描述
    这里写图片描述

    (重点!难点!)加order by null!!
    这里写图片描述


    这里写图片描述

    • use index() #建议走哪个索引
    • force index() #强制走哪个索引
    • ignore index() #忽略走哪个索引

    新建索引列里面,包括主键列。


    覆盖索引

    覆盖索引是select的数据列只用从索引中就能够取得,不必读取数据行,换句话说查询列要被所建的索引覆盖。
    这里写图片描述
    explain 时,最后的那列 useing index 说明使用了覆盖索引

    使用覆盖索引的情形:
    select、group by、order by、having、where 的列都在索引里面,不会回表,资源消耗小。

    高效使用覆盖索引

    同时满足过滤和覆盖索引:where条件中要包括索引的第一个列!(大幅提升性能!)

    覆盖索引一个很大的适用场合(技巧)

    • 从一个表中取5%以下的数据时,走索引比较好。因为通过索引要回表,效果不好。
    • 从一个列非常多的大表中取大量行,但是少数列。走索引会回表,消耗大量资源,全表扫描和走索引类似,消耗大量资源,取少量数据优先走索引。
    • 覆盖索引的索引很小,覆盖索引不回表。

    索引的功能

    • 1.where 条件过滤数据
    • 2.表连接
    • 3.解决order by
    • 4.解决group by
    • 5.使用覆盖索引,解决回表和大表的问题

    useing where ,usering index condition ,useing index

    • (1)useing index 说明使用覆盖索引
    • (2)useing where 说明访问了表

    注意:一二同时出现,说明使用了覆盖索引,useing where不起作用!

    • (3)usering index condition:本来在server层过滤的索引条件,下压到引擎层过滤,引擎优化了对表的访问,提高性能
    • (4)icp(index condition pushdown,索引条件下压)的使用场合:
      • ①二级索引
      • ②where条件中包含二级索引的多个列条件,但一定包含二级索引的前导列
      • ③这是5.6新特性,效果明显(不害怕有空列)

    select distinct e.c_d_id from customer e;

    DISTINCT去重,访问全表,删除相同的行,隐含着排序,消耗资源。

    解释5.6在多列索引中为什么不害怕中间有空列,5.5版本中害怕有空列。

    5.5和5.6多列索引中,5.6不害怕中间有空列,5.5不仅仅害怕空列,还害怕非=号出现在前面的列中。

    select * from customer where c_w_id<2 and c_id<5 and c_first like 'ab%';//只能将第一个列压入
    select * from customer where c_w_id=1 and c_id<5 and c_first like 'ab%';//可以压入两个列

    5.5中多列索引使用的限制很多,5.6解决了这个问题(icp)
    5.5中最好将选择性最强的列放在最前面,但是要注意这个列必须被经常使用到!


    索引失效的情况

    • 1)没有查询条件,或者查询条件没有建立索引
    • 2)在查询条件上没有使用引导列
    • 3)查询的数量是大表的大部分,应该是30%以上
    • 4)索引本身失效
    • 5)查询条件使用函数在索引列上,或者对索引列进行运算,运算包括(+-*/!等)。
    explain select * from customer where upper(c_first)='ABC';  #全表扫描
    explain select * from customer where c_w_id-1<2;    
    #全表扫描
    • 6)对小表查询
    • 7)提示不使用索引
    • 8)统计数据不真实
    • 9)CBO计算走索引花费过大的情况;访问的表过小,使用全表扫描的消耗小于使用索引。
    • 10)隐式转换导致索引失效。这一点应当引起重视,也是开发中经常会犯的错误。由于表的字段tu_mdn定义为varchar
    • 11)①<> 不等于,特别容易不走索引 ②单独的>,< (有时会用到,有时不会)
    • 12)like “% ” 百分号在前
      [大于小于like都属于 模糊匹配 ~]
    • 13)表没分析
    • 14)单独引用复合索引里非第一位置的索引列
    • 15)字符型字段为数字时在where条件里不添加引号
    • 16)对索引列进行运算,需要建立函数索引
    • 17)not in,not exist
    • 18)当变量采用的是times变量,而表的字段采用的是date变量时,或相反情况

    索引失效出现的坑

    • 1、select/delete/update,没有where条件只能全表扫描

      往往是误写

    delete from customer;
    update customer set c_first='abc';
    • 2、where条件上没有索引

      • show index
      • explain
    • 3、多列索引没有被正确使用,因为where条件中没有写前导列(前缀列、引导列)

    • 4、通过索引访问的数据量过大,超过30%(主键索引除外,主键不怕访问大数量)
    • 5、索引本身失效
    • 6、where条件中列名字上不能有任何的计算:函数、+-*/等等,列名字上必须非常干净
    explain select * from customer where upper(c_first)='ABC';  #全表扫描
    explain select * from customer where c_w_id-1<2;    
    #全表扫描
    • 7、提示忽略索引
    explain select * from t2 ignore index (i_t2_name) where name like 'xkj'; 
    • 8、统计信息
      mysql针对应用发送过来的SQL需要进行解析(parse)
      解析步骤

      • 1、将这个SQL所有的执行路径列出来
      • 2、挑选一个合适执行路径,这个执行路径的挑选有两种方式
        • ①RBO(rule base optimizer),基于规则的挑选方式(对开发的要求较高,对SQL的书写要求较高)
        • ②CBO(cost base optimizer),对于开发的要求不高,主要依靠统计信息

    ===RBO:

    select ...
    from customer c,orders o,order_line ol
    where ...
    and c like c.c_first = 'abc'
    and o.status like 'valid';

    ①表的连接顺序,按照书写顺序;
    ②对于有严格限制条件的表,顺序提到最前面;
    ③对于有多个限制条件的表,第一个表的限制条件为准;
    限制条件优先考虑=,like放在=号的后面
    表的连接顺序:o c ol

    ===对于CBO:
    ①先根据规则库,过滤掉肯定不行的路径;
    ②对于可能行的执行路径,计算每一个执行路径的成本;
    ③选择一个最小的成本的路径作为执行计划。

    ===cbo解析的时候会用到:
    ①数据字典
    ②统计信息:表的行数、索引列的选择性(card)
    ③(不好的地方)mysql没有记录列值的倾斜程度(列值分布 直方图)(平均每个值占多少行)

    =====手工修改rows和card统计信息,引导mysql走索引
    ①手工收集

    analyze table tpcc1000.customer;

    ②没有手工收集则会自动收集
    ③自动存储:

    innodb_stats_persistent=on

    ④变化量大的情况下,自动收集

    innodb_stats_auto_recalc=on #则是开启了自动收集
    innodb_stats_persistent_sample_pages
    innodb_stats_sample_pages

    ⑤手工修改统计信息的rows和card(不建议)
    计算一个列上的唯一值的数量以及数值

    mysql5.6版本面对数据倾斜:
    这里写图片描述
    图中,发生了严重的数据倾斜。会宕机。


    计算表和索引的大小:
    这里写图片描述

    如上图, 对于pt_c 表,stat_name里的size=1代表只占一个数据页;
    对于customer表,stat_name 里面的size=11440个数据页,1440*16K =表的大小;
    n_leaf_pages是叶子节点,size-n_leaf_pages=树高。

    • 9、隐式转换导致索引失效

    这一点应当引起重视,也是开发中经常会犯的错误。
    由于表的字段tu_mdn定义为varchar2(20),但在查询时把该字段作为number类型以where条件传给Oracle,这样会导致索引失效。

    错误的例子:select * from test where tu_mdn=13333333333;
    正确的例子:select * from test where tu_mdn=’13333333333’;

    数字列,不害怕隐式类型转换
    字符列,非常害怕隐式类型转换

    explain select * from customer where c_first = 123;

    易踩的坑:
    ①存储数字的列被定义成了字符串列
    ②存储日期时间的列被定义成了字符串列

    • 10、
      ① <>,绝大部分情况下索引失效
    explain select * from t1 where s_data  <> 'yes';
    explain  select * from customer where c_id <> 10; 

    ② 单独的>,<,(有时会用到,有时不会)

    explain  select * from customer where c_id > 10;
    explain  select * from customer where c_id > 10000;
    • 11、like “%_” 百分号在前.
    explain select * from t1 where s_data  like 'no%';

    not like效果不好,不管是%放在后面还是前面

    • 12、表没分析
    • 13、not in ,not exist.很多时候不走索引
    explain select * from customer where c_id  not in (1,2,3,4); 
    explain select * from customer where c_id  in (1,2,3,4);
    explain select * from t1 where s_data  not in ('yes');
    explain select * from t1 where s_data  not in ('no');
    • 14、当变量采用的是times变量,而表的字段采用的是date变量时.或相反情况。
    explain select * from orders where o_entry_d = cast('2011-01-01 12:30:30' as time);
    explain select * from orders where o_entry_d = cast('2011-01-01 12:30:30' as date);
    • 15、B-tree索引 is null , is not null,处理的不是很好,因此要注意非空这个坑
    explain select * from stock where s_dist_01 is null;
    explain select * from stock where s_dist_01 is not null;

  • 相关阅读:
    Linux防火墙:iptables禁IP与解封IP常用命令
    php7安装 event扩展
    laravel5.5 调用系统自带登陆认证auth
    linux 搭建rap记录
    微信小程序silk格式转码成mp3格式
    抓包软件
    laravel 微信小程序登录 加密解密扩展包
    mysql索引
    sphinx-for-chinese在windows下安装与使用方法
    sphinx增量索引和主索引来实现索引的实时更新
  • 原文地址:https://www.cnblogs.com/lpeng94/p/12546455.html
Copyright © 2011-2022 走看看