深入理解Mysql索引底层原理

慢SQL优化手段:1.优化sql语句;2.添加调整索引(可以提升几十倍)

索引的本质

帮助MySQL高校获取数据的排好序的数据结构

索引的数据结构

可以是:二叉树、红黑树、hash表、B-Tree
比如查询语句select * from xxx where t.col=89,需要从表中扫描数据去查找,就需要把数据从磁盘加载到内存,与磁盘进行IO操作---->慢,占内存;需要我们减少查找表的次数---->给col列建立索引,把col这一列的数据存储到数据结构中去,查2次便可以找到。
假如索引的数据结构是二叉树,二叉树的节点key-value分别存储89和89索引所在行磁盘文件的地址。每插入一条数据就得维护索引的相关数据,如果插入的数据是顺序增大那么树的结构就会是单边增长的,就成了链表,查询效率与全表扫描没有区别。------》B-Tree
假如索引的数据结构是红黑树,数据左右两边不平衡的时候,红黑树会去调整平衡,改动大;而且数据量越大,树越高,假如要找的数据在叶子节点,查找的次数多。
作为存储引擎,树的高度越小越好,最好在h<=4就可以存储上千万条数据。----->多叉树
B-Tree
特点:
1.叶子节点具有相同的高度,叶子节点的指针为空
2.节点中数据索引从左到右递增排序
3.索引元素不重复
image.png
MySQL真正的底层数据结构是B+Tree
B+Tree(B-Tree的变种)
特点:
1.非叶子节点不存储data,只存储冗余索引(冗余的中间索引,叶子节点的范围),可以放更多的索引
2.叶子节点包含所有索引字段
3.叶子节点从左到右递增,且有指针连接,提高区间访问性能
image.png
分配索引时,一个节点的存储空间大一些就可以存储更多的索引,一次性查找的数据多,从而减少磁盘的IO操作。每个节点的大小默认为16kb,存储一个bigint类型的索引数据大概需要8byte,索引之间的空白存储分叉文件的地址大概需要6byte,那么一个节点大概可以存储16kb/14byte=1170个索引节点,树的高度为3,叶子节点就可以存储2千多万条数据!
如果数据量太大的话,建议使用分库分表。

查找的过程:
假如要查找col=30的数据,从根节点一次查找,第一次把根节点所有的元素从磁盘加载到内存,在内存里面进行比对,定位某一元素,30在15和56之间,中间空白存储的分叉文件地址,根据地址把节点加载到内存,根据30在内存中再做二分查找,。。。。直到定位到30。
其中一个节点的数据可以称之为一页数据,一页数据默认16384字节(16kb),cpu从磁盘读取数据列到内存一般以页为单位。查找过程中最费时间的就是从磁盘load一页的数据到内存。

data元素有可能存储所在磁盘文件的地址,也有可能存储这条数据的其他列,这个由存储引擎区分。

存储引擎

存储引擎是形容表的,数据文件在MySQL安装下的data下,每个数据库都对应一个文件夹,每个表都会对应几个文件。
存储引擎有两种:MyISAM、Innodb。MyISAM不支持事务,已经很少使用了,用Innodb更多。

MyISAM存储引擎索引实现

索引文件和数据文件是分离的(非聚集索引)
存储引擎为MyISAM的表对应三个文件 .frm; .MYI ; .MYD (在mysql安装目录的data文件夹下)
.frm文件存储表结构;主键索引结构在 .MYI文件中;表里的数据存储在 .MYD文件中;
假如查找语句select * from xxx where t.col=89,查找时先判断col是不是索引字段,如果是,就去索引文件中定位到所在行的磁盘文件地址,再根据地址去MYD文件中去定位查找。
image.png

Innodb存储引擎索引实现

存储引擎为Innodb的表对应两个文件 .frm; .ibd
.frm文件存储表结构;主键索引结构和表的数据在 .ibd文件中
1.ibd文件本身即是按照B+tree组织的一个索引结构文件
2.聚集索引,叶节点包含了完整的数据结构
image.png
区别:
MyISAM叶子节点存储的是数据所在的磁盘文件地址;Innodb叶子节点存储的是索引所在行的其他所有字段;
MyISAM的索引文件和数据文件分离–非聚集索引;Innodb聚集索引。
面试题
为什么建议Innodb表必须建主键,并且推荐使用整型的自增主键??
1.根据主键建索引,如果没有主键的话,MySQL会在表中寻找一列没有重复数据的列作为B+树的节点来维护整张表的数据;如果没有找到这一列,MySQL就会后台加一个隐藏列rowid维护唯一性。增加了MySQL的开销。
2.整型?不建议使用UUID作为主键,原因是UUID既不是整型,也不自增。整型的数据便于比较,索引查找的过程中要进行大量的比较操作,整型快;整型占用的空间小;
3.自增?索引的叶子节点从左到右依次递增,新增一条数据维护起来比较简单,如果插入的数据不大不小,B+Tree需要进行平衡、分裂,效率低,影响数据的写入性能。
索引的方式除了BTree还有hash,为什么不用hash呢?
hash的过程就是根据索引的key进行一次哈希计算可以定位出数据存储的位置
很多时候hash索引要比B+树更高效,但是hash仅能满足"=","IN"的操作,不能支持范围查询;还可能会发生少量的hash冲突,所以不适用。
image.png
B树和B+树的区别?
B树非叶子节点也存储data数据,16kb大小存储的索引元素少,存储同样多索引的情况下,B树高;
B树叶子节点没有双向指针,不支持范围查找。
普通索引(非主键索引)和主键索引的区别?
为什么非主键索引结构叶子节点存储的是主键值?(一致性和节省存储空间)
Innodb整张表只能有一个聚集索引,如果建了主键,就会自动建主键索引(聚集索引),如果没有建主键,就会使用mysql隐藏列rowid建聚集索引。
非主键索引结构叶子节点存储的是主键值(聚集索引的key),为什么呢?
因为假如这张表有好几个索引,data值存储多个索引列的话比较占空间;假如插入一条记录,主键索引和非主键索引都要维护整张表数据,那么要等待这两个索引都插入成功之后才能确定数据插入完成,这样设计可以避免这个情况,只需要等主键索引更新成功。其他索引随后根据主键索引更新值就可以了。
image.png

联合索引底层数据结构

工作中使用到最多的索引。
联合主键的情况下,三个字段怎么建立索引?
假如是按照字段(name,age,position),按照字段建立的先后顺序。
索引最左前缀原理:(左列)
以下图中的例子是一个 联合主键索引,不存在name,age,position三个字段都相等的情况。
如果是辅助索引的话,data里存的主键是不一样的,然后再根据这些主键 回表去查找真正需要的数据。
image.png
select * from xx where name=“” and age=12;---->走索引
select * from xx where position=“” and age=12;---->不走索引
select * from xx where position=“” ;---->不走索引
联合索引的B+Tree结构,只有第一列数据是叶子节点递增的,抛去第一列name不看,age和position是没有顺序的,即使找到的age=3的节点,它的前面和后面的叶子节点依然有比他大或者小的数据,想要找到age>3的数据还是需要做全表扫描。
但是在定死第一列相等的情况下,第二列的数据是有序的,比如where name=Bill and age=12的情况下,name确定了,age就是按照递增排序的。

猜你喜欢

转载自blog.csdn.net/weixin_44006731/article/details/129357980