【mysql】- 索引使用篇
回顾
- 每个索引都对应一棵
B+树,B+树分为好多层,最下边一层是叶子节点,其余的是内节点。所有用户记录都存储在B+树的叶子节点,所有目录项记录都存储在内节点。 - InnoDB 存储引擎会自动为主键(如果没有它会自动帮我们添加)建立聚簇索引,聚簇索引的叶子节点包含完整的用户记录。
- 我们可以为自己感兴趣的列列建立二级索引 ,二级索引 的叶子节点包含的用户记录由
索引列列+主键组成,所以如果想通过 二级索引来查找完整的用户记录的话,需要通过回表操作,也就是在通过二级索引找到主键值之后再到聚簇索引中查找完整的用户记录。 B+树中每层节点都是按照索引列列值从小到大的顺序排序而组成了了双向链表,而且每个页内的记录(不不论是用户记录还是目录项记录)都是按照索引列列的值从小到大的顺序而形成了了一个单链表。如果是联合索引的话,则页面和记录按照先按照联合索引前边的列列排序,如果该列列值相同,再按照联合索引后边的列列排序。- 通过索引查找记录是从
B+树的根节点开始,一层一层向下搜索。由于每个页面都按照索引列列的值建立了了Page Directory(页目录),所以在这些页面中的查找非常快。
代价
- 空间上的代价
- 这个是而易见的,每建立一个索引都为要它建立一棵
B+树,每一棵B+树的每一个节点都是一个数据页,一个页默认会占用16KB的存储空间,一棵很大的B+树由许多数据页组成,那可是很大的一片存储空间
- 这个是而易见的,每建立一个索引都为要它建立一棵
- 时间上的代价
- 每次对表中的数据进行增、删、改操作时,都需要去修改各个
B+树索引。而且我们讲过,B+树每层节点都是按照索引列的值从小到大的顺序排序而组成了了双向链表。不不论是叶子节点中的记录,还是内节点中的记录(也就是不不论是用户记录还是目录项记录)都是按照索引列列的值从小到大的顺序而形成了一个单向链表。而增、删、改操作可能会对节点和记录的排序造成破坏,所以存储引擎需要额外的时间进行一些记录移位,页面分裂、页面回收啥的操作来维护好节点和记录的排序。如果我们建了了许多索引,每个索引对应的B+树都要进行相关的维护操作,这还能不不给性能拖后腿么?
- 每次对表中的数据进行增、删、改操作时,都需要去修改各个
- 所以说,一个表上索引建的越多,就会占用越多的存储空间,在增删改记录的时候性能就越差。为了了能建立又好又少的索引,我们先得学学这些索引在哪些条件下起作用的。
适用条件
前提:
idx_name_date_age为索引全值匹配
- 搜索条件中的列列和索引列一致的话,这种情况就称为全值匹配
- 如
select * from tableName where name = 'xxx' and date = '2020-01-01';,,包含查询中的2个列,查询过程:- 因为 B+ 树的数据页和记录先是按照
name列的值进行排序的,所以先可以很快定位name列列的值是xxx的记录位置。 - 在
name列相同的记录里是按照date列的值进行排序的,所以在name列的值是xxx的记录里又可以快速定位date列的值是 '2020-01-01' 的记录。
- 因为 B+ 树的数据页和记录先是按照
- 如
上述条件的顺序不影响索引的命中,因为mysql有个查询优化器,会分析这些搜索条件并且按照可以使用的索引中列的顺序来决定使用哪个搜索条件,后使用哪个搜索条件。
- 搜索条件中的列列和索引列一致的话,这种情况就称为全值匹配
匹配左边的列(最左原则)
- 搜索语句句中也可以不用包含全部联合索引中的列列,只包含左边的就行,比方说下边的查询语句句:
select * from tableName where name = 'xxx';或者select * from tableName where name = 'xxx' and date = 'xxx';
- 对于下面的语句就用不到索引了
select * from date = 'xxx';- 原因:因为
B+树的数据页和记录先是按照name列的值排序的,在name·列的值相同的情况下才使用date列进⾏行排序,也就是说name列的值不同的记录中date的值可能是无序的。而现在你跳过name列直接根据date`的值去查找,自然是用不上索引。
- 对于下面只能用到
name索引select * from tableName where name = 'xxx' and age = 'xxx';- 原因:因为
name值相同的记录先按照date的值进行排序,date值相同的记录才按照age值进行排序。
- 搜索语句句中也可以不用包含全部联合索引中的列列,只包含左边的就行,比方说下边的查询语句句:
匹配列前缀
- 索引创建时候会按照索引列的字母开始进行排序,如
name的情况 - aaaa aaab aaac ...
- 先比较字符串串的第一个字符,第一个字符小的那个字符串就比较小。
- 如果两个字符串的第一个字符相同,那就再比较第二个字符,第二个字符比较小的那个字符串串就比较小。
- 如果两个字符串的第二个字符也相同,那就接着比较第三个字符,依此类推。
- 所以一个排好序的字符串列其实有这样的特点:
- 先按照字符串串的第一个字符进行排序。
- 如果第一个字符相同再按照第二个字符进行排序。
- 如果第二个字符相同再按照第三个字符进行排序,依此类推。
- 那么查询name为'AB'开头,可以这么写:
select * from tableName where name like 'AB%';
- 假如仅仅是后缀或者中间,如'XXAB'或者'XABX',是不用到索引的,因为这时的'AB'并未排好序。
select * from tableName where name like '%AB%'
- 索引创建时候会按照索引列的字母开始进行排序,如
匹配范围值
语句查询过程为:
select * from tableName where name > 'AA' and name < 'BB';- 找到
name值为AA的记录。 - 找到
name值为BB的记录。 - 由于所有记录都是由链表连起来的(记录之间用单链表,数据页之间用双链表),所以他们之间的记录都可以很容易易的取出来
- 找到这些记录的主键值,再到
聚簇索引中回表查找完整的记录。
- 找到
在使用联合索引进行范围查找的时候需要注意,如果对多个列同时进行范围查找的话,只有对索引最左边的那个列进行范围查找的时候才能用到
B+树索引select * from tableName where name > 'AA' AND name < 'BB' AND date > '2020-01-01';- 上边这个查询可以分成两个部分:
- 通过条件 name > 'AA' AND name <'BB'来对
name进行范围,查找的结果可能有多条name值不不同的记录;
- 通过条件 name > 'AA' AND name <'BB'来对
- 对这些
name值不不同的记录继续通过date > '2020-01-01条件继续过滤。
- 对这些
- 这样子对于联合索引
idx_name_date_age来说,只能用到name列的部分,而用不到date列的部分,因为只有name值相同的情况下才能用date列的值进行排序,而这个查询中通过name进行范围查找的记录中可能并不是按照date列进行排序的,所以在搜索条件中继续以date列进行查找时是用不到这个B+树索引的。
精确匹配某一列并范围匹配另外一列
- 对于同一个联合索引来说,虽然对多个列都进行范围查找时只能用到最左边那个索引列,但是如果左边的列是精确查找,则右边的列列可以进行范围查找
select * from tableName where name = 'AA' AND date > '2020-01-01' AND date < '2020-12-31' AND age > '10';- 这个查询的条件可以分为3个部分:
name = 'AA',对name列进行精确查找,当然可以使用 B+ 树索引了。date > '2020-01-01'AND birthday<'2020-12-31',由于name列是精确查找,所以通过name='AA'条件查找后得到的结果的name值都是相同的,它们会再按照date的值进行排序。所以此时对date列进行范围查找是可以用到B+树索引的。age > '10',通过age的范围查找的记录的date的值可能不同,所以这个条件无法再利用B+树索引了了,只能遍历上一步查询得到的记录。
- 对于同一个联合索引来说,虽然对多个列都进行范围查找时只能用到最左边那个索引列,但是如果左边的列是精确查找,则右边的列列可以进行范围查找
用于排序
select * from table order by name,date,age limit 1;- 这个查询的结果集需要先按照
name值排序,如果记录的name值相同,则需要按照date来排序,如果date的值相同,则需要按照age排序。
关于联合索引,
order by的子句句后边的列的顺序也必须按照索引列的顺序给出,如果给出order by date, age, name的顺序,那也是用不了B+树索引- 这个查询的结果集需要先按照
不可以使用索引进行排序的几种情况
- ASC、DESC混用,要么全是ASC,要么全是DESC
- WHERE子句中出现非排序使用到的索引列
- 排序列列包含非同一个索引的列
- 排序列列使用了复杂的表达式
用于分组
select * from tableName group by name,date,age;- 上述语句原理跟
order by一致,分组列的顺序也需要和索引列的顺序一致
回表的代价
- 关于使用一个二级索引
- 会使用到两个
B+树索引,一个二级索引,一个聚簇索引 - 访问二级索引使用
顺序I/O,访问聚簇索引使用随机I/O。
- 会使用到两个
- 如何减少
回表- 覆盖索引:查询的列包含索引列
- 关于使用一个二级索引
索引的挑选
- 只为用于搜索、排序或分组的列创建索引
- 考虑列的基数
- 索引列的类型尽量小
- 数据类型越小,在查询时进行的比较操作越快
- 数据类型越小,索引占用的存储空间就越少,在一个数据页内就可以放下更更多的记录,从而减少磁盘
I/O带来的性能损耗,也就意味着可以把更更多的数据页缓存在内存中,从而加快读写效率。
总结
B+树索引在空间和时间上都有代价,所以没事儿别瞎建索引。B+树索引适用于下边这些情况:- 全值匹配
- 匹配左边的列
- 匹配范围值
- 精确匹配某一列并范围匹配另外一列
- 用于排序
- 用于分组
- 在使用索引时需要注意下边这些事项:
- 只为用于搜索、排序或分组的列列创建索引
- 为列列的基数大的列创建索引
- 索引列列的类型尽量小
- 可以只对字符串值的前缀建立索引
- 只有索引列在比较表达式中单独出现才可以适用索引
- 为了了尽可能少的让 聚簇索引发生页面分裂和记录移位的情况,建议让主键拥有
AUTO_INCREMENT属性。 - 定位并删除表中的重复和冗余索引
- 尽量适用
覆盖索引进行查询,避免回表带来的性能损耗
【mysql】- 索引使用篇的更多相关文章
- 初识mysql索引 - 小白篇
:接触mysq也有两年左右的时间了,但是对该数据库的理解自认还比较初级,看过很多文章,也看过一些相关的书籍,依然小白....(这里个人总结是两点主要原因:1.对mysql的学习大部分都是源于看一些杂七 ...
- MySQL索引——总结篇
MySQL索引 MySQL索引 数据库的三范式,反模式 零碎知识 索引 索引原理 B Tree索引 B+Tree索引 B Tree 与 B+Tree的比较 聚集索引和辅助索引 聚集索引的注意事项 索引 ...
- Mysql高手系列 - 第22篇:深入理解mysql索引原理,连载中
Mysql系列的目标是:通过这个系列从入门到全面掌握一个高级开发所需要的全部技能. 欢迎大家加我微信itsoku一起交流java.算法.数据库相关技术. 这是Mysql系列第22篇. 背景 使用mys ...
- Mysql索引(一篇就够le)
我想很多人对mysql的认知可能就是CRUD(代表创建(Create).更新(Update).读取(Retrieve)和删除(Delete)操作),也不敢说自己会用和熟悉mysql,当然我就是其中一个 ...
- 「 MySQL高级篇 」MySQL索引原理,设计原则
大家好,我是melo,一名大二后台练习生,大年初三,我又来充当反内卷第一人了!!! 专栏引言 MySQL,一个熟悉又陌生的名词,早在学习Javaweb的时候,我们就用到了MySQL数据库,在那个阶段, ...
- 「MySQL高级篇」MySQL索引原理,设计原则
大家好,我是melo,一名大二后台练习生,大年初三,我又来充当反内卷第一人了!!! 专栏引言 MySQL,一个熟悉又陌生的名词,早在学习Javaweb的时候,我们就用到了MySQL数据库,在那个阶段, ...
- mysql 索引篇
一.索引优化 索引优化主要还是依赖explain命令,关于explain命令相信大家并不陌生,具体用法和字段含义可以参考官网explain-output,这里需要强调rows是核心指标,绝大部分r ...
- MySQL索引优化看这篇文章就够了!
阅读本文大概需要 5 分钟. 来源:cnblogs.com/songwenjie/p/9410009.html 本文主要讨论MySQL索引的部分知识.将会从MySQL索引基础.索引优化实战和数据库索引 ...
- MySQL 第五篇:索引原理与慢查询优化
一 介绍 为何要有索引? 一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,在生产环境中,我们遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,因此对查询语句 ...
- 讲真,MySQL索引优化看这篇文章就够了
本文主要讨论MySQL索引的部分知识.将会从MySQL索引基础.索引优化实战和数据库索引背后的数据结构三部分相关内容,下面一一展开. 一.MySQL——索引基础 首先,我们将从索引基础开始介绍一下什么 ...
随机推荐
- 5种经典的Linux桌面系统
最近一直在准备Linux相关的PPT,对于一个老码农来说Linux系统自然是比较熟悉了,随口可以说出好几种Linux的版本,然而对于计算机初学者可能就知道windows操作系统.也许你告诉他你可以安装 ...
- 利用synchronized解析死锁的一种形成方式
代码 import ... public class Test{ private static Object o1=new Object(); private static Object o2=new ...
- 【题解】[BalticOI 2014]friends
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3916 (BZOJ3916) 由题意可知 \(N\) 得为奇数,\(S\) 才存在,所以先特 ...
- vue全家桶(2.5)
3.8.动态路由匹配和路由组件传参 3.8.1.动态路由匹配 动态路由意味着不固定,具有某种模式,我们希望通过某种匹配方式,把这种不固定的路由形势映射到同一个组件,例如:一个User组件,不同的ID表 ...
- 搞懂ELK并不是一件特别难的事(ELK)
本篇文章主要介绍elk的一些框架组成,原理和实践,采用的ELK本版为7.7.0版本 一.ELK介绍 1.1.ELK简介 ELK是Elasticsearch.Logstash.Kibana三大开源框架首 ...
- TreeMap实现
- Python分析最近大火的网剧《隐秘的角落》,看看网友们有什么看法
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 估计最近很火的连续剧<隐秘的角落>大家趁着端午假期都看过了吧? ...
- Docker环境下Java应用的最大内存和堆内存的设置
Docker环境下Java应用的最大内存和堆内存的设置 1. 设置应用允许使用的最大内存 通过docker run(创建一个新的容器并运行)命令中设置-m来进行设置.案例如下所示. docker r ...
- Linux下Jmeter+nmon+nmon analyser实现性能监控及结果分析
一.概述 前段时间讲述了Jmeter利用插件PerfMon Metrics Collector来监控压测过程中服务器资源的消耗,一个偶然机会,我发现nmon这个 工具挺不错,和Jmeter插件比起来, ...
- docker 在centos7中设置 DOCKER_OPTS
不同于Ubuntu目录 /etc/default/docker. 在 CentOS7中Docker默认配置的路径在 /usr/lib/systemd/system/docker.service [例如 ...