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

    一、前言

      本文基于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

  • 相关阅读:
    5.19 省选模拟赛 T1 小B的棋盘 双指针 性质
    5.15 省选模拟赛 容斥 生成函数 dp
    5.15 省选模拟赛 T1 点分治 FFT
    5.15 牛客挑战赛40 B 小V的序列 关于随机均摊分析 二进制
    luogu P4929 【模板】舞蹈链 DLX
    CF 878E Numbers on the blackboard 并查集 离线 贪心
    5.10 省选模拟赛 拍卖 博弈 dp
    5.12 省选模拟赛 T2 贪心 dp 搜索 差分
    5.10 省选模拟赛 tree 树形dp 逆元
    luogu P6088 [JSOI2015]字符串树 可持久化trie 线段树合并 树链剖分 trie树
  • 原文地址:https://www.cnblogs.com/ExMan/p/14452805.html
Copyright © 2011-2022 走看看