zoukankan      html  css  js  c++  java
  • 索引优化策略有哪些

    一、前言

      本文基于mysql8.0的innodb测试,建表在做对应的优化策略测试时记得加索引,由于文中太多查询例子不一一针对建立索引了,只挑几个建索引举例。

    CREATE TABLE `user` (
      `id` int(11) NOT NULL,
      `name` varchar(20) DEFAULT NULL,
      `sex` varchar(5) DEFAULT NULL,
      `address` varchar(255) DEFAULT NULL,
      `birthday` timestamp(6) NULL DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    二、优化策略

      下面演示均对过滤列字段建立单索引不一一建立了,联合索引我会特别建立。

      1.不要在索引列上加函数或运算

    -- 全表扫描
    explain select * from user where year(birthday) < 2020
    -- 走索引
    explain select * from user where birthday < '2020-03-17'
    -- 全表扫描
    explain select * from user where id + 1 = 2
    -- 走索引
    explain select * from user where name = 'lihua'

      2.隐式类型转换

      以address字段举例,address为varchar类型,我们为其建立索引

    -- 全表扫描
    explain select * from user where address = 2
    -- 走索引
    explain select * from user where address = '2'

     

       通过对比可以发现第一次没有使用索引,第二次是使用了索引idx_address的,为什么会这样呢?因为第一种发生了隐式转换,即:

    explain select * from user where address = 2
    -- 等价于
    explain select * from user where CAST(address AS signed int) = 2

      隐式转换在索引字段上做了函数处理,因此会全表扫描。

       3.前导模糊查询不会使用索引

    -- 全表扫描
    explain select * from user where name like '%li'
    -- 使用索引
    explain select * from user where name like 'li%'

     

       对比结果可以发现非前导模糊查询可以使用索引(%li%也不能使用索引,不做单独演示了)

      4.联合索引最左匹配原则

      mysql从左到右匹配,知道遇到范围查询(>、<、between、like)就停止匹配(这个停止匹配是指不会在接着向右匹配,当前范围查询还是可以匹配到的),举个例:

    select * from user where name = 'lihua' and sex = 'm' and birthday < '2020-03-17' and address = '1'

      建立(`name`, `sex`, `birthday`, `address`)顺序的索引,address使用不到索引的,而建立(`name`, `sex`, `address`, `birthday`)顺序的索引,则都可以使用到索引。

      5.查询时=可以乱序

    -- 可以乱序
    select * from user where name = 'lihua' and sex = 'm'
    select * from user where sex = 'm'and name = 'lihua'

      mysql查询时会将查询顺序优化成和联合索引顺序一致。

      6.避免filesort排序

      mysql中无法利用索引完成的排序称为“文件排序”,执行计划Extra中若出现Using filesort, 说明mysql会对数据使用一个外部的索引排序,而不是按照表内索引顺序进行读取,最左匹配原则也适用于排序,我们建立(`age`, `name`, `sex`)的联合索引,看下面具体5个例子及执行结果:

    -- 不带过滤条件
    explain select * from user order by age

    -- 排序使用索引
    explain select * from user where age = 22 order by age

    -- 最左匹配原则,排序条件未走索引,文件排序
    explain select * from user where age > 22 order by name

    -- 当前筛选条件与排序条件使用的字段顺序与索引不一致,文件排序
    explain select * from user where age = 22 order by sex,name

    -- 排序字段方向一个升序一个降序,文件排序
    explain select * from user where age = 22 order by name asc,sex desc

       7.union、in、or均可命中索引

    -- 使用索引
    explain 
    select name from user where name = 'lihua'
    union all
    select name from user where name = 'limei'
    -- 使用索引
    explain select name from user where name in ('lihua','limei')
    -- 使用索引
    explain select name from user where name = 'lihua' or name = 'limei'

      三个关键字使用后的查询效率比较:

      1)对于索引列来说,最好使用union all因为复杂的查询(包含运算等),将使or、in放弃索引而全表扫描,除非你能确定or、in会使用索引;

      2)对于只有非索引字段来说,就老是用or、in,因为非索引字段本来就要全表扫描union all只会成倍数增加表扫描次数;

      3)对于非索引字段及索引字段(索引字段有效)都有的情况来说,union all、or、in都可以理论上;

      8.负向条件不会使用索引(不绝对,有时会走范围索引,取决于范围大小)

      负向条件有!=、<>、not in、not like、not exists等,多数介绍sql优化的文章都会提到避免使用!=,因为不走索引,我们用实例验证一下,只对name建立但索引,库中数据只有三条,此时使用!=,查看执行计划:

    -- 使用范围索引
    explain select * from user where name != 'lihua'

       观察执行计划,可以发现!=确确实实是走了name的范围索引的,分析原因实际应用中很可能是因为不等于的数据占比很高,走索引不如全表扫描效率高。

      9.分页查询优化

      mysql的分页并不是跳过offset行,而是取offset+n行,然后放弃前offset行取后面n行,当offset很大时效率就很低,利用覆盖索引,避开回表

      解决方案一:书签,记录上次访问位置,下次直接从书签位置开始

    select id from table limit 10000 20
    -- 改成
    select id from table where id>10000 limit 20

      解决方案二:关联(或者join),根据覆盖索引查询需要的主键,再根据主键关联原表获得需要的数据

    select id from table,
    (select id from table limit 10000,20) tmp where table.id = tmp.id

      查询所有数据

    select * from table
    where id> =(select id from table limit 10000, 1) limit 20
    select * from table,
    (select id from table limit 10000,20) tmp where table.id = tmp.id

      一个关于limit的小经验:当使用limit时不使用order by ,查询id走的是索引,按索引存储位置取数据,*是查全表按表记录位置取结果,所以得出结论直接select index from table查询,不管是单索引还是组合索引都会返回索引位置的数据,如果select中包含其他非索引列就会返回顺序记录结果。

    三、结语

      1.本文测试均基于mysql8.0innodb,不同版本可能有所不同,实际开发中还是要具体问题具体分析多查看执行计划,一切以提升效率为前提不用过于在意条条框框,毕竟索引优化也是为了效率服务,本文主要记录mysql学习过程,如有错误请指正,一起学习一起进步。

      2.本文中提到的覆盖索引,回表可以查看 https://www.cnblogs.com/ghoster/p/12509611.html

      还有建立单索引还是联合索引问题,推荐看这篇博客 https://blog.csdn.net/Abysscarry/article/details/80792876

  • 相关阅读:
    DNS正向反向解析
    varnish-4.0.5的反向代理 缓存测试 负载均衡实验
    DS8700 硬盘更换略
    ftp虚拟用户
    maven mvn 命令行 编译打包
    linux下的springboot项目启动文件
    linux系统安装gcc
    linux系统下安装两个或多个tomcat
    linux系统的磁盘挂载
    常用linux命令
  • 原文地址:https://www.cnblogs.com/ghoster/p/12512110.html
Copyright © 2011-2022 走看看