MySQL是如何存储索引和数据的
索引和数据存放位置是哪?
首先问下MySQL的表、数据、索引是放到哪里的?
磁盘=》默认是安装目录的data文件里(不同版本可能有所不同),每个数据库对应data文件夹里的一个文件夹
我们打开一个walking_mybatis数据库看一下有一个user表,再打开对应的文件夹看一下,里面的文件名和表名有关系,然后有不同的后缀,这里面的不同的放法和 MySQL 的存储引擎有关,和你选择的哪种存储引擎有关。
存储引擎是修饰什么的?
大家都知道,MySQL 常见的存储引擎有InnoDB存储引擎,MYISAM存储引擎,那存储引擎是形容MySQL 数据库的还是某一张表的?
是表,尽管数据库级别也有存储引擎选项,但最终还是以表的存储引擎为主的。
如果你用Navicat工具去建表,也许你最多就用了“字段”这一栏去增加字段,你可以点一下“选项”看一下,可以选择存储引擎。
我这边又新建一个order表,然后选择为MYISAM存储引擎
在表上右键选择『对象信息』->『DDL』可看到ENGINE=MyISAM
看一下user表的
索引和数据文件
再来看一下这个数据库文件夹下这俩表的数据文件。
我们会发现,user表(InnoDB存储引擎)对应两个文件,order表(MYISAM存储引擎)对应3个文件。其中.frm文件是存储的是表结构,两个存储引擎都一样,而InnoDB的.ibd文件是索引+数据,MYISAM的.MYI(I:index)和.MYD(D:data)文件分别是索引字段的索引结构和数据文件,也就是说MYISAM存储引擎的索引和数据是分开的,而InnoDB存储引擎的数据和索引是在一个文件里的。
InnoDB和MYISAM的一些不同
MYISAM存储引擎
MYISAM索引实现(非聚集)
- • 索引文件和数据文件是分离的(非聚集)
数据、行记录是存储在MYD文件,假如col1是索引字段那么这一列是存储在MYI文件里以 B+Tree 的结构来组织的,然后他的叶子节点的data部分存储的是索引所在行记录的磁盘文件地址,根据磁盘文件地址指针就可以从MYD文件里快速的找到我们的这一行记录。
查找过程
所以MYISAM这个存储引擎他的查找的一个大致过程就是,先看条件字段有没有用到索引,是索引字段就先去到索引文件去查找这个索引所在的那一行的磁盘文件地址,就借助B+Tree的特点从根节点顺藤摸瓜找到磁盘文件地址指针,然后从MYD文件一次性定位到所找的数据,也就是说MYISAM会垮两个文件。
InnoDB存储引擎
InnoDB索引实现(聚集)
- • 表数据文件本身就是按B+Tree组织的一个索引结构文件
- • 聚集索引-叶子节点包含了完整的数据记录
- • 为什么InnoDB表必须有主键,并且推荐使用整型的自增主键?
- • 为什么非主键索引结构叶子节点存储的是主键值?(一致性和节省存储空间)
用的最多的InnoDB存储引擎是什么样子的呢?我们可以看到,它只有两个文件。.rm文件和MYISAM一样都是表结构文件,.ibd文件就是MYISAM的MYI和MYD文件的合并,索引文件和数据文件都存储到一个文件。
InnoDB存储引擎索引存储结构大概是下图这样的,它也是一个B+Tree,但是它的叶子节点和MYISAM有点区别,它存储的是索引所在行的所有字段。
这个好处是什么?不用回表了,性能应该比MYISAM高,你看MYISAM查找到索引所在行记录的磁盘地址后还要回MYD文件读取一次。
聚集索引/非聚集索引
聚集索引/聚簇索引,叶子节点包含了完整的数据记录,InnoDB的主键索引就是一个聚集索引,他的索引和数据是在同一个文件,MYISAM的是非聚集索引,索引和数据是分开存储的。InnoDB的主键索引我们叫做聚集索引。
为什么InnoDB表必须有主键,并且推荐使用整型的自增主键?
我们看一下这个问题
为什么InnoDB表必须有主键,并且推荐使用整型的自增主键?
为何 InnoDB 表建议要有自增的主键,尽量建主键,建整形自增的?其实很简单,设计如此,MySQL 设计的就是 InnoDB 把你的数据和主键索引用 B+Tree 来组织的,没有主键他的数据就没有一个结构来存储。
建 InnoDB 表的时候没有建主键,表也能建成功,为什么?
不建主键不代表没有主键,没有建主键 InnoDB 会帮你选一个字段,一个可以标识唯一的字段,选为默认字段,如果这个字段唯一的话,不重复,可一键唯一索引的话,就会作为类似于唯一索引,用这个字段来作为唯一索引来维护整个表的数据。如果没有,MySQL 会生成一个唯一的列,类似于 RowId,只不过你看不到,他会用生成的这个唯一列,维护 B+Tree 的结构,查数据的时候还是用 B+Tree 的结构去查找。
为什么推荐整型呢?
我们想象一下查找过程,是把节点加载到内存然后在内存里去比较大小,也就是在查找的过程中要不断地去进行数据的比对。假设UUID,既不自增也不是整形。问一下,是整形的 1 < 2 比较的效率高还是字符串的“abc”和“abe”比较的效率高呢?显然是前者,因为字符串的比较是转换成 ASICI 一位一位的比,如果最后一位不一样,比到最后才比较出大小,就比整形比较慢多了,存储空间来说,整形更小。索引越节约资源越好。
为什么是自增的呢?
我们可以看一下 B-Tree 的叶子节点之间是没有指针的,B+Tree 优化后增加了叶子节点之间的指针,如果我们遍历数据,从当前节点遍历完之后,就可以根据节点间的指针快速找到下一个节点去遍历。讲到这,穿插一下 B+Tree 为什么要比 B-Tree 多一个节点间指针呢?那就讲一下索引的另一种数据结构就是 HASH。
HASH索引
99.99%的情况都是用 B+Tree,也有些情况用HASH。假设我们的索引选的是HASH的数据结构,每插入一个元素会把我们的索引字段做一次HASH计算,把运算的到的结果值和这一行的所在磁盘地址做一个映射。
对索引元素的值做一次HASH运算就可以在hash映射表里快速找到这一行的磁盘文件地址,经过一次HASH就可以快速定位到索引所在行的磁盘文件地址,HASH这么快,表有一亿个数据按这种算法,那也就可能经历一次HASH运算就可以快速找到某页任意一行数据元素的所在的磁盘文件地址,那比B+Tree快的多啊!那为什么99.99%的都是B+Tree不是HASH呢?
HASH的等值查询比B+Tree快,上亿依然很快,为啥很快却不使用?最主要的原因是什么?因为如果使用范围查找,HASH就没有用武之地了,范围查找也是很常用的吧,所以基本就不怎么用HASH这种数据结构。那B+Tree就很好的支撑范围查找吗?
当然,B+Tree可以很好的支撑。
看一下这个B+Tree的结构
刚才我们说了B+Tree的任一叶子节点内部是从左到右都是递增的,且节点之间有一个指针(双向的,图不标准),
假设我们查大于20的记录,MySQL 内部是怎么查找的?先从根节点,定位到大于20的元素,然后依次从左到右找到30,然后这个节点遍历完了,就可以根据指针找到下一个节点的位置,因为B+Tree的特点,后面的元素全都大于20,就这样顺藤摸瓜把后面的元素全弄出来。
那B-Tree没有这个指针的话查找大于 20 的元素那得多麻烦,先找出第一个节点中大于20的全部元素,因为还有别的节点,所以又要从根节点去遍历找下一个叶子节点,是不是非常慢。没有这个指针每次都要从根节点开始查找然后合并,那是非常慢的。
为什么非主键索引结构叶子节点存储的是主键值?
为了一致性和节省存储空间。已经维护了一套主键索引+数据的B+Tree结构,如果再有其他的非主键索引的话,索引的叶子节点存储的是主键,这是为了节省空间,因为继续存数据的话,那就会导致一份数据存了多份,空间占用就会翻倍。另一方面也是一致性的考虑,都通过主键索引来找到最终的数据,避免维护多份数据导致不一致的情况。
联合索引
尽量建联合索引,少建单值索引 。刚讲的都是单值索引
联合索引的底层数据结构是什么样的?
多个列逐个字段去比较,(a,b,c)
多个索引有多个B+树结构,非主键索引叶子节点存储的不是数据,而是主键(一致性和节省空间)