【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——索引基础 首先,我们将从索引基础开始介绍一下什么 ...
随机推荐
- python动态柱状图图表可视化:历年软科中国大学排行
本来想参照:https://mp.weixin.qq.com/s/e7Wd7aEatcLFGgJUDkg-EQ搞一个往年编程语言动态图的,奈何找不到数据,有数据来源的欢迎在评论区留言. 这里找到了一个 ...
- twaver html5 如何设置节点不可拖动
解决思路: 1.创建一个不可移动的图层 : layer 2.设置不可拖动的节点node 的图层为 layer 见代码: var box = new twaver.ElementBox(); var ...
- java 虚拟机指令重新排序
指令重排序是JVM为了优化指令,提高程序运行效率,在不影响单线程程序执行结果的前提下,尽可能地提高并行度.编译器.处理器也遵循这样一个目标.注意是单线程.多线程的情况下指令重排序就会给程序员带来问题. ...
- 阿里巴巴开源canal 工具数据同步异常CanalParseException:parse row data failed,column size is not match for table......
一.异常现象截图 二.解决方式: 1.背景 早期的canal版本(<=1.0.24),在处理表结构的DDL变更时采用了一种简单的策略,在内存里维护了一个当前数据库内表结构的镜像(通过desc ...
- 计算机网络之DNS协议
DNS( Domain Name System)是“域名系统”的英文缩写,是一种组织成域层次结构的计算机和网络服务命名系统,它用于TCP/IP网络,它所提供的服务是用来将主机名和域名转换为IP地址的工 ...
- Java中Map的4种遍历方式
第一种方式:这是平常用的最多也最可取的一种遍历方式. for (Map.Entry<String, Object> entry : map.entrySet()) { System.out ...
- postman无法正常启动
想请教下各位大神,我电脑的postman打开之后就一直转,没法启动是怎么回事?重装了不同版本的也是同样的情况,重启电脑也没用...同样的安装包,在别的电脑上就能正常打开!有什么办法解决吗? 0 20 ...
- 集合类List底层数据结构总结
数组: 1. 不安全 ArrayList 2. 安全 Vector链表LinkedList不安全 3.2.1 ArrayList 1. 适合随机查找和遍历,不适合删除和增加 2. 大小不足时,需要将已 ...
- Python3笔记017 - 4.2 列表
第4章 序列的应用 python的数据类型分为:空类型.布尔类型.数字类型.字节类型.字符串类型.元组类型.列表类型.字典类型.集合类型 在python中序列是一块用于存放多个值的连续内存空间. py ...
- 什么是DevOps?该如何正确的在企业内进行实践
传统IT技术团队中通常都有多个独立的组织-开发团队.测试团队和运维团队.开发团队进行软件开发.测试团队进行软件测试,运维团队致力于部署,负载平衡和发布管理. 他们之间的职能有时重叠.有时依赖.有时候会 ...