• 【mysql篇-进阶篇】索引


    索引概述

    概念

    • 是帮助mysql高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的树结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。

    优点:

    • (1)提高查询效率:提高数据检索的效率,降低数据库的IO成本。
    • (2)提高排序效率:通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗。

    缺点:

    • (1)索引列占用空间;
    • (2)降低更新表的速度,如对表进行insert、update、delete时,效率降低。

    索引结构:

    • mysql的索引是在存储引擎层实现的,不同的存储引擎有不同的结构,主要包含以下几种:
    • (1)B+Tree索引:最常见的索引类型,大部分引擎都支持B+Tree索引。
    • (2)hash索引:底层数据结构使用哈希表实现的,只有精确匹配索引列的查询才有效,不支持范围查询。
    • (3)R-Tree索引:空间索引是MyIsam引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少。
    • (4)Full-Text索引:是一种通过建立倒排索引,快速匹配文档的方式,类似于Lucene、Solr、ES。
      在这里插入图片描述

    二叉树

    • 特点:左侧比根节点的数值小,右侧比根节点的数值大。
    • 缺点:
      (1)顺序插入时,会形成一个链表,查询性能大大降低。
      (2)二叉树一个节点下面,最多只能包含两个子节点;大数据情况下,层级较深,检索速度慢。

    红黑树

    • 特点:自平衡的二叉树。
    • 解决了二叉树顺序插入时,形成链表的缺点。
    • 红黑树是自平衡的二叉树,所以也会存在,大数据情况下,层级较深,检索速度慢。

    Btree

    • 又称多路平衡查找树。
    • 特点:
      (1) 在根节点、叶子节点、非叶子节点下面都有数据。
      (2)在进行分裂后,向上分裂的数据,在叶子节点不会存在。
    • 分裂演变地址:https://www.cs.usfca.edu/~galles/visualization/BTree.html

    B+Tree

    • 特点:
      (1)非叶子节点只起到索引的作用,叶子节点用来存放数据。
      (2)在进行分裂后,向上分裂的数据,在叶子节点也会存在。(所有的元素都会出现在叶子节点)
      (3)叶子节点形成一个单向链表。
    • mysql索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加了一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能。(即双向链表)
    • 分裂演变地址:https://www.cs.usfca.edu/~galles/visualization/BTree.html

    hash

    • 哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。
    • 会出现哈希冲突,哈希冲突通过链表来解决,向链表追加元素即可。
    • 特点:
      (1)hash索引只能用于对等比较(=、in);不支持范围查询(between、>、<…);
      (2)无法利用索引完成排序操作;
      (3)查询效率高,通常只需要检索一次就可以了,效率通常高于B+Tree索引。

    面试题:

    • 为什么innodb存储引擎选择使用B+Tree索引结构:

    (1)相对于二叉树,层级更少,搜索效率高;
    (2)对于BTree,无论是叶子节点爱是非叶子节点,都会保存数据,这样导致一页中存储的键值减少,指针跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低。
    (3)相对于hash索引,B+Tree支持范围匹配及排序操作。
    (4)B+Tree,只在叶子节点存放数据,搜索效率稳定;叶子节点形成双向链表结构,便于范围搜索和排序。
    (一页是16K)

    索引分类

    • 在mysql数据库,将索引的具体类型主要分为以下几类:主键索引、唯一索引、常规索引、全文索引。
      在这里插入图片描述
    • 而在InnoDB存储引擎中,根据索引的存储形式,又可以分为以下两种:
      在这里插入图片描述
    • 聚集索引选取规则:

    (1)如果存在主键,主键索引就是聚集索引;
    (2)如果不存在主键,将使用第一个唯一索引作为聚集索引;
    (3)如果表没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowId作为隐藏的聚集索引。

    • 聚集索引的叶子节点下是这一行的数据。
    • 二级索引的叶子节点下是该字段值对应的主键值。
    • 根据二级索引查找的时候会进行回表查询。
    • 回表查询:
    • 先到二级索引中查找数据,找到主键值,然后再到聚集索引中根据主键值,获取数据的方式,称之为回表查询。
    • 索引的语法:
    • 创建索引:
      CREATE {UNIQUE | FULLTEXT} INDEX index_name ON table_name(index_col_name,…);
    • 查看索引:
      SHOW INDEX FROM table_name;
    • 删除索引:
      DROP INDEX index_name ON table_name;

    SQL性能分析

    SQL执行频率:
    • 通过show [session/global] status;命令可以查看当前数据库insert、update、delete的访问频次。(session是查看当前会话;global是查看全局数据)
      在这里插入图片描述
    • 通过上面的命令,可以查看当前数据库是以查询为主,还是以增删改为主。
    慢查询日志
    • 慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志;
    • 查看慢查询日志的开关是否开启的命令:slow_query_log;
      在这里插入图片描述
    • 若要开启慢查询日志,需要在mysql的配置文件(Linux环境下的位置如下:/etc/my.cnf)中配置如下信息:(Windows环境下找到my.ini文件)

    #开启mysql慢查询日志开关
    slow_query_log=1
    #设置慢查询日志的时间为0.002秒,SQL语句执行时间超过0.002秒,就会视为慢查询,记录慢查询日志
    long_query_time=0.002

    • Linux环境查看慢查询日志信息:/var/lib/mysql/localhost-slow.log
    • 配置文件添加完成之后,执行重新启动mysql
      (Windows环境考验直接从服务中重启)
      在这里插入图片描述
    • 重启之后再次查看是否打开慢查询:
      在这里插入图片描述
    profile
    • show profiles 能够在做SQL优化时,帮助我们了解时间都耗费到了哪里。
    • 通过命令 select @@have_profiling;查看是否支持profile
      在这里插入图片描述
    • 通过命令 select @@profiling;查看profile开关是否开启(0是未开启,1是已开启。如未开启,就执行命令:SET profiling = 1;进行开启)
      在这里插入图片描述
    • 查看每一条SQL的耗时基本情况:show profiles;
      在这里插入图片描述
    • 查看指定query_id的SQL语句各个阶段的耗时情况:show profile for query query_id; ---->show profile for query 13;
      在这里插入图片描述
    • 查看指定query_id的SQL语句CPU的使用情况:show profile cpu for query query_id; ---->show profile cpu for query 13;
      在这里插入图片描述
    explain
    • 直接在select语句前面加上explain关键字
      ![在这里插入图片描述](https://img-blog.csdnimg.cn/5ae2704707194e9bab148cc7bd2466bb.png
    • 各个参数的含义:
      在这里插入图片描述

    索引使用规则

    验证索引效率
    • 根据如下例子,可以看出;使用索引进行查询的性能大大提升。
    • 根据主键索引id来进行查询
      在这里插入图片描述
    • 根据普通字段来进行查询
      在这里插入图片描述
    • 将普通字段创建为索引
      在这里插入图片描述
    • 再通过刚刚建立的二级索引进行查询
      在这里插入图片描述
    最左前缀原则
    • 联合索引,要遵守最左前缀法则。
    • 概念:

    查询从索引的最左列开始,并且不跳过索引中的列。如果跳跃某一列,则后面的字段索引失效。

    • 演示:
    • 建立联合索引:idx_name_age_job
      在这里插入图片描述
    • 使用name、age、job进行查询:
      在这里插入图片描述
    • 使用name、age进行查询:
      在这里插入图片描述
    • 使用name、job进行查询:
      在这里插入图片描述
    • 使用age、job进行查询:
      在这里插入图片描述
    范围查询
    • 联合索引,出现范围查询(>,<),范围查询右侧的索引失效。
    • 下面演示的例子中,根据key_len来判断是否所有字段都是走了索引。
    • 在业务允许的情况下,尽可能使用>=或<=这类的范围查询,避免使用>或<
    • 演示:
    • EXPLAIN SELECT * FROM emp WHERE NAME = ‘金庸’ AND age > ‘66’ AND job = ‘总裁’
      在这里插入图片描述
    • EXPLAIN SELECT * FROM emp WHERE NAME = ‘金庸’ AND age >= ‘66’ AND job = ‘总裁’
      在这里插入图片描述
    索引失效情况
    • emp表中的索引如下:
      在这里插入图片描述
    • 索引列运算:不要在索引列上进行运算操作,索引会失效。

    (1)未对索引列进行运算操作,索引未失效
    在这里插入图片描述

    (2)对索引列进行运算操作,索引失效
    在这里插入图片描述

    • 字符串不加引号:字符串类型的字段在使用时,不加引号,索引将失效。

    (1)字符串类型的字段在使用时,加引号,索引未失效。
    在这里插入图片描述

    (2)字符串类型的字段在使用时,不加引号,索引失效。
    在这里插入图片描述

    • 模糊查询:头部模糊匹配,索引失效;尾部模糊匹配,索引不失效。

    (1)头部模糊匹配,索引失效
    在这里插入图片描述

    (2)尾部模糊匹配,索引不失效
    在这里插入图片描述
    (3)头尾部均模糊匹配,索引失效
    在这里插入图片描述

    • or连接条件查询:用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。
      在这里插入图片描述
    • 数据分布影响:如果MySQL评估使用索引比全表更慢,则不使用索引。
      (如果mysql评估使用索引比全表扫描更慢,则会放弃使用索引)
      在这里插入图片描述
    SQL提示
    • 在SQL语句中加入一些人为的提示来达到优化操作的目的。
    • use index:建议mysql使用哪一个索引完成此次查询
    • 语法:explain select 查询字段 from 表名 use index(索引名) where 条件;
    • ignore index :忽略指定的索引
    • 语法:explain select 查询字段 from 表名 ignore index(索引名) where 条件;
    • force index:强制使用索引
    • 语法:explain select 查询字段 from 表名 force index(索引名) where 条件;
    覆盖索引
    • 现在emp表的索引情况:
      在这里插入图片描述
    • 概念:查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到。
    • 演示:

    (1)查询字段未超出联合索引范围,未进行回表查询
    >

    (2)查询字段未超出联合索引范围,未进行回表查询
    在这里插入图片描述

    (3)查询字段超出联合索引范围,进行了回表查询

    (4)查询字段超出联合索引范围,进行了回表查询

    • 以上四个图片的列extra字段值解读,前两个一样,后两个一样
    • Using where; Using Index ------>查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据。
    • Using index condition ---------->查找使用了索引,但是需要回表查询数据。
    前缀索引
    • 概念:

    当字段类型为字符串(varchar,text,longtext等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO, 影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。

    • 语法:
    • create index 索引名 on 表名(column(阿拉伯数字)) ;
      在这里插入图片描述
    • 前缀长度
    • 可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高, 唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。
    单列索引和联合索引
    • 单列索引:

    一个索引只包含单个列。

    • 联合索引:

    一个索引包含了多个列。

    • user表的索引现状:
      在这里插入图片描述
    • 根据两个单列索引的列进行查询,发现只走了其中一个索引
      在这里插入图片描述
    • 对两个单列索引建立联合索引

    CREATE INDEX idx_name ON USER(NAME);

    • 再次进行查询,走了联合索引
      在这里插入图片描述

    索引设计原则

    • (1)数据多,查询频繁
    • 针对于数据量较大,且查询比较频繁的表建立索引;
    • (2)常作为查询条件的字段
    • 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引;
    • (3)区分度高的列(例如:性别的区分度较低,不适合做索引)
    • 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高;
    • (4)根据字段特点建立相应的索引
    • 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引;
    • (5)联合索引可以避免回表,提高查询效率
    • 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率;
    • (6)控制索引数量
    • 要控制索引的数量,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。
    • (7)尽量在创建索引时建立相应约束
    • 如果索引不能存储null值,在创建表时使用not null约束它。当优化器知道每列是否包含null值时,它可以更好的确定哪个索引最有效的用于查询。
  • 相关阅读:
    记忆法——第一节 记忆的本质
    以前端视角,漫谈「云端」
    面试2:通用能力
    2.NoSQL之Redis配置与优化
    Linux 在线解压jar包
    数据结构概念部分
    【面试经典150 | 数组】轮转数组
    C++类的成员函数作为回调函数
    【全开源】JAVA打车小程序APP打车顺风车滴滴车跑腿源码微信小程序打车源码
    智能交通和自动驾驶技术
  • 原文地址:https://blog.csdn.net/xiaoxiaoxiang1/article/details/125773971