深入理解MySQL的数据结构

前言

  • 在介绍Mysql数据结构之前先介绍下密切相关的数据结构,有助于更好的理解什么是内容。

数据结构与算法

二分查找法

  • 二分查找法也称为折半查找法,用来查找一组有序的记录数组中的某一记录,基本思想为:将记录按有序(递增或递减)排列,在这个查找过程中采用跳跃式方式查找,即先以有序序列的中点位置为比较对象,如果要找的元素小于中点位置,则将查序列缩小为左半部分,否则为右半部分,通过一次比较,将查找的区间缩小一半。
    • 如果有5,10,19,21,31,37,42,48,50,52者10个数,现要从10个数中查找48条记录,其查找过程如下图所示:
      在这里插入图片描述- 从上图中可以看出,用了3次就赵高了48这个数。如果是顺序查找,则需要8次,因此二分查找发的效率比顺序查找要好(平均地来说)。但如果说查5这条记录,顺序查找只需要1次,而二分查找需要4次。我们来看,对于上面10个数来说,平均查找数为(1+2+3+4+5+6+7+8+9+10)/10=5.5次,而二分查找分别为(4+3+2+4+3+1+4+3+2+3)/10=2.9次。在最坏的情况下,顺序查找的次数为10,而二分查找的次数为4
  • 二分查找的应用很广泛,而且他的思想易于理解,相信读者也许知道,每页page Diretory中的槽是按照主键的顺序存放的,对一具体记录的查找是通过对page Directory进行二分查找得到的。

二叉查找树

  • 在介绍B+树前,需要先了解下什么是二叉树。B树是通过二叉查找树,再由平衡二叉树,B树演化过来的。
    在这里插入图片描述
  • 图中的数字代表每个节点的键值,在二叉查找树中,左子树的键值总是小于根节点的键值,右子树的键值总是大于根节点的键值, 因此可以通过中序遍历得到键值的排序输出,图中经过中序遍历后输出: 2、3、5、6、7、8。
  • 对图中的二叉树进行查找,如查键值为5的记录,先找到根 ,其键值是6,6大于5, 因此查找6的左子树,找到3; 而5大于3,在找右子树,一共找了3次.二叉查找树的平均查找速度比顺序查找速度更快
二叉树的不足
  • 二叉查找树可以任意地构造,同样是2、3、5、7、8这五个数字,可以按照下图的方式建立二叉查找树.
    在这里插入图片描述
  • 图中的查找次数为(1+2+3+4+5+5)/6=3.16次,和顺序查找差不多。显然这颗二叉树的查询效率就低了。因此若想最大性能的构造一棵二叉查找树,需要这颗二叉查找树是平衡的,从而引出新的定义----平衡二叉树或称为AVL树.
  • 当数值是自增情况下,二叉树会形成一个链表,查询的数据需要一个个往下找,导致磁盘io次数不可控,对于数据自增的情况下,查找效率很低

平衡二叉树

  • 符合二叉查找树的定义,其次必须满足任意节点的两颗子树的高度最大差为1。显然,图 5-3 不满足平衡二叉树的定义,而图 5-2 是一颗平衡二叉树.平衡二叉树的查找新性能是比较高的.
  • 平衡二叉树的查询速度的确很快,但是维护一颗平衡二叉树的代价是非常大的。通常来说,需要一次或多次左旋和右旋来得到插入或更新后的树的平衡性。对于图 5-2所示的平衡树,当用户需要插入一个新的键值为9的节点时,需做如图 5-4所示的变动。
  • 这里通过一次左旋操作就将插入后的树重新变为平衡的了,但是有的情况可能需要多次,如图 5-5 所示。
    在这里插入图片描述
红黑树的不足
  • 如 5-4 和 图 5-5 中,列举了向一颗平衡二叉树插入一个新的节点后,平衡二叉树需要做旋转操作。除了插入操作,还有更新 和 删除操作,不过这和插入没有本质的区别,都是通过左旋或右旋来完成。因此对一颗平衡树的维护需要一定的开销, 不过平衡二叉树多用于内存结构的对象中,因此维护的开销较小。
  • 当mysql表中的记录,越来越多的情况下,红黑树的高度也越来越高,那么随着树的高度越大,索引查找的次数增多,那么磁盘io也会增多这样查找效率是比较低的。

B+ 树

  • B+ 树和二叉树、平衡二叉树一样都是经典的数据结构。B+树是从B树的索引顺序访问方法演化过来的,但是显示使用过程中几乎已经没有使用B树的情况了。
  • 由于B树定义非常复杂,具体可以取任何一本数据结构书中找,如果都罗列,会让读者感到困惑,我这边对B+ 树做个精简介绍: B+ 树是为磁盘或其他直接存取辅助设备设计的一种平衡二叉树。在 B+ 树中,所有记录节点都是按键值得大小顺序存放在同一层得叶子节点上,由各个叶子节点指针进行连接.
  • 先来看一个B+树,其高度为2,每页可存放4条记录,扇出为5,如5-6图所示
  • 从图 5-6 可以看出,所有记录都在叶子节点,并且时顺序存放得,如果用户从最左边得叶子节点开始顺序遍历,可以得到所有键值得顺序排序:5、10、15、20、25、30、50、55、60、65、75、80、85、90.
    在这里插入图片描述

索引

  • 索引(在Mysql中也叫做“键(key)”)是存储引擎用于快速找到记录的一种数据结构。
  • 索引对于良好的性能非常关键,尤其是当表中的数据量越来越大的时,索引对性能的影响愈发重要,在数据量较小的却负载较低时,不恰当的的索引对性能的影响还不明显,但数据量逐渐增大时,性能胡急剧下降。

索引基础

在这里插入图片描述

  • 如果在actor_id列上建有索引,则Mysql将使用该索引找到actor_id为5的行,也就是说,Mysql先在索引上按值进行查找,然后返回所有包含该值得数据行。

索引类型(B-Tree索引、哈希索引)

B-Tree索引
  • 当我们谈论索引的时候,如果没有特别指明类型,那么多半说的是B-Tree索引,他使用B-Tree数据结构来存储数据,大多数Mysql引擎支持这种索引。
  • 实际上很多存储引擎使用B+Tree,即每一个叶子节点都包含指向下一个叶子节点的指针,从而方便叶子节点的范围遍历。
  • 存储引擎以不同的方式使用B-Tree索引,性能也各有不同,各有优势。例如MyISAM使用前缀压缩技术使得索引更小,但InnoDB则按照原有数据格式进行存储。再如MyISAM索引通过数据的物理位置引用被索引的行而InnoDB则根据主键引用被索引的行
  • B-Tree通常意味着所有的值都是按顺序存储的,并且每一个叶子页到根的距离相同。下图展示了B-Tree的抽象表示,大致反映了InnoDB是如何工作的,MyISAM使用的结构有所不同,但基本思想是类似的。
    在这里插入图片描述
  • B-Tree索引能够加快访问数据的速度,因为存储引擎不再需要进行全表扫描来获取需要的数据,取而代之的是从索引的根节点(图示并且画出)开始进行搜索。根节点的槽中存放了指向子节点的指针存储引擎根据这些指针向下查找。通过比较节点页的值和要查找的值可以找到合适的指针进人下层子节点,这些指针实际上定义了子节点页中的值得上限和下限。最终存储引擎要么是找到对应的值,要么改记录不存在。
  • 叶子节点比较特别,他们的指针指向的是被索引的数据,而不是其他的节点页(不同的引擎的“指针”类型不同)。上图5-1仅绘制了一个节点和其对应叶子节点,其实在节点和叶子节点之间可能有很多层节点页。树的深度和表的大小直接相关。
  • B-Tree对索引列时顺序组织存储的,所以适合查找范围数据。例如,在一个基于文本域的索引树上,按字母顺序传递连续的值进行查找是非常合适的,所以像“找出I到k开头的名字”这样的查找效率很高。
  • 假设有如下数据表:
		CREATE TABLE People(
			last_name varchar(50) not null,
			first_name varvhar(50) not null,
			dob  date not null,
			gender enum('m','f') not null,
			key(last_name,first_name,dob)
		);
  • 对表中每一行都包含了last_name,first_name,dob列的值,下图5-2中显示了该索引是如何组织数据存储的。
    在这里插入图片描述
  • 有个细节,索引对多个值进行排序的依据是CREATE TABLE 语句中定义索引时的顺序。看一下最后两个人的姓和名都一样。则根据他们的出生日期来排列顺序。
  • 可以使用B-Tree索引的查询类型。B-Tree索引适用于全键值、键值范围或键前缀查找。其中键前缀查找只适用于根据最左前缀的查找。前面所述的索引对如下的类型的查询有效。
    • 全值匹配
      • 全值匹配指的是和索引中的所有列进行匹配,例如前面提到的索引可用于查找姓名为Cuba Allen、出生于1960-01-01的人。
    • 匹配最左前缀
      • 前面提到的索引可用于查找所有姓名为Allen的人,即只使用索引的第一列
    • 匹配列前缀
      • 也可以职匹配某一列的值的开头部分.例如前面提到的索引可用于查找所有以J开头的姓的人,这里也只使用索引的第一列。
  • 匹配范围值
    • 例如前面提到的索引可用于查找性在Allen和Barrymore之间的人。这也只使用了索引的第一列。
  • 精确匹配某一列并范围匹配另外一列
    • 前面提到的索引也可用于查找所有姓为Allen,并且名字是字母K开头(比如Kim、Karl等)的人。即第一列last_name全匹配,第二列first_name范围匹配
  • 只访问索引的查询
    • B-Tree通常支持“只访问索引的查询”,即查询只需要访问索引,而无须访问数据行。后面我会单独讨论这种“覆盖索引”的优化
  • 因为索引树中的节点是有序的,所以除了按值查找之外,索引还可以用于查询OEDER BY操作(按顺序查找)。一般来说,如果B-Tree可以按照某种方式找到值,那么也可以按照这种方式用于排序。所以,如果,ORDER BY子句满足前面列出的几种查询类型,则这个索引也可以满足对应的排序需求。
  • 不能跳过索引中的列。也就是说,前面所述的索引无法用于查找姓为Smith并且在某个特定日期出生的人。如果不指定(first_name),则MySQL只能使用索引的第一列。
  • 如果查询中的某个列的范围查询,则右边所有列都无法使用索引优化查找。例如有查询 WHERE last_name=‘Smith’ AND first_name LIKE ‘J%’ AND dob=‘1976-12-23’,这个查询只能使用索引的前两列,因为这里LIKE是一个范围条件
哈希索引
  • 哈希索引(hash index)基于哈希实现。只有精确匹配索引所有列的查询才有效。对于每一行数据,存储引擎都会对所有索引列计算一个哈希码(hash code),哈希码是一个较小的值,并且不同键值的行计算出来的哈希码也不一样。哈希索引将所有的哈希码存储在索引中,同时在哈希表保存指向每个数据的指针。
	CREATE TABLE testhash(
	  fname VARCHAR(50) NOT NULL,
	  lname VARCHAR(50) NOT NULL,
	  KEY USING HASH(fname)
	)ENGING=MEMORY;

在这里插入图片描述

  • 注意每个槽的编号是顺序的,但是数据行不是。现在,来看如下查询:
    在这里插入图片描述
  • MySQL先计算’Peter’的哈希值,并使用该值寻找对应的记录指针。因为f(‘Peter’)=8784,所以MySQL在索引中查找8784,可以找到指向第3行的指针,最后一步是比较第3行的值是否为’Peter’,以确保就是要查找的行。
哈希索引的缺点
  • 因为索引自身只需存储对应的哈希值,所以索引的结构十分紧凑,这也让哈希索引查找的速度非常快。然而,哈希索引也有他的限制:
    • 哈希索引只包含哈希值和行指针,而不存储字段值,所以不能使用索引中的值来毕淼读取行。不过访问内存中的行的速度也很快,所以大部分情况下这一定对性能影响并不明显
    • 哈希索引数据并不是按照索引值顺序存储的,所以也就无法用于排序
    • 哈希索引页不支持部分索引列匹配查找。因为哈希索引始终是使用索引列的全部内容来计算哈希值的。例如与,在数据列(A,B)上建立哈希索引,如果查询只有数据列A,则无法使用该索引。
    • 哈希索引只支持等值比较查询,包括 = 、IN()、也不支持任何范围查询,例如 WHERE price>100;
    • 访问哈希索引的数据非常快,除非有很多哈希冲突(不同的索引列却有相同的哈希值)。当出现哈希冲突的时候,存储引擎必须遍历链表的所有行指针,逐行进行比较,直到找到符合条件的行。
    • 如果哈希冲突很多的话,一些索引维护操作的代价就很高。例如,如果在某个选择性很低(哈希冲突很多)的列上建立哈希索引,那么当从表中删除一行时,存储引擎需要遍历对应哈希值的链表中的每一行,找到并删除对应行的引用,冲突越大,代价越大。
  • 综上所述,因为这些限制,哈希索引只适用于某些特定的场合,而一旦适合哈希索引,则他带来的性能替身将非常显著。举个例子,在数据仓库应用中有一种经典的“星型”schema,需要关联很多查找表,哈希索引就非常适合查找表的需求。
    • InnoDB引擎有一个特殊的功能更叫做“自适应哈希索引(adaptive hash index)”。当InnoBD注意到某些引值被使用得非常频繁时,他会在内存中基于B-Tree索引之上再创建一个哈希索引,这样就让B-Tree索引也具备了哈希索引的一些优点,比如快速的哈希查找。这是一个完全自动的、内部的行为,用户无法控制或者配置,不过如果没有必要,完全可以关闭该功能。
全文索引
  • 全文索引是一种特殊类型的索引,他查找的是文本中的关键词,而不是直接比较索引中的值。全文搜索和其他几类索引的匹配方式完全不一样。他有许多需要注意的细节,如停用词、词干和复数、布尔搜索等。全文索引更类似于搜索引擎做的事情,而不是简单的WHERE条件匹配。

索引的优点

  • 索引总结起来有如下三个优点
    • 索引大大减少了服务器需要扫描的数据量
    • 索引可以帮助服务器避免排序和临时表
    • 索引可以将随机I/O变为顺序I/O

高性能的索引策略

  • 正确地创建和使用索引是实现高性能查询的基础。前面已经介绍了几种类型的索引以及其对应的优缺点。现在我们一起来看看如何真正的发挥这些索引的优势。
  • 高效的选择和使用索引有很多方式,其中有些针对特殊案例的优化方法,有些则是针对行为的优化。使用哪个索引,以及如何评估选择不同索引的性能影响的技巧,则需要持续不断的学习。接下来的几个小节将帮助读者理解如何高效的使用索引

独立的列

  • 我们通常会看到一些查询不当地使用索引,或者使得Mysql无法使用已有的索引。如果查询中的列不是独立的,则MySQL就不会使用索引。独立的列是指索引列不能是表达式的一部分,也不能是函数的参数
    • 例如,下面的查询无法使用actor_id列的索引:

         mysql> SELECT actor_id FROM sakila.actor WHERE actor_id+1=5;
      

多列索引

  • 很多人对多列索引的理解不够。一个常见的错我是,为每个列创建独立的索引,或者按照错误的顺序创建多列索引。
  • 我会在下一节单独讨论索引列的顺序问题。先来看一个问题,为每个列创建独立的索引,从SHOW CREATE TABLE中很容易看到这种情况:
	CREATE TABLE t(
		c1 INT,
		C2 INT,
		C3 INT,
		KEY(c1),
		KEY(c2),
		KEY(c3)
	);
  • 这种索引策略,一般是由于人们听到一些专家诸如“把WHERE条件里面的列都建上索引”这样模糊的建议导致的。实际上这个建议是非常错误的。这一样依赖最好的情况下也只能“一星”索引。其性能比起真正最优的索引可能差几个数量级。有时如果无法设计一个“三星”索引,那么不如忽略掉WHERE子句,集中精力优化索引列的顺序,或者创建一个全覆盖索引。

选择合适的索引顺序

  • 我们遇到的最容易引起困惑的问题就是索引的顺序。正确顺序依赖于使用该索引的查询,并且同时需要考虑如何更好的满足排序和分组的需要。
  • 在一个多列B-Tree索引中,索引列的顺序意味着索引首先按照最左列进行排序,其次是第二列,等等。所以,索引可以按照升序或者降序进行扫描,以满足精确符合列顺序的ORDER BY、GROUP BY和DISTINCT等子句的查询需求 。

聚簇索引

  • 聚簇索引并不是一种单独的索引类型,而是一种数据存储方式。具体的细节依赖于其实现方式,但InnoDB的聚簇索引实际上在同一个结构中保存了B-Tree索引和数据行。
  • 当表有聚簇索引时,他的数据行实际上存放在索引的叶子页(left page)中。术语“聚簇”表示数据行和相邻的键值紧凑地存储在一起。因为无法同时把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。
  • 因为是存储引擎负责实现索引,因此不是所有的存储引擎都支持聚簇索引
  • 下图展示了聚簇索引中的记录是如何存放的,注意到,叶子页包含了行的全部数据,但是节点只包含了索引列。
    在这里插入图片描述
  • 如果没有定义主键,InnoDB会选择一个唯一的非空索引代替。如果没有这样的索引,InnoDB会隐式定义一个主键作为聚簇索引。
聚簇索引的优点
  • 可以把相关数据保存在一起。例如实现电子邮箱时,可以根据用户ID来聚集数据,这样只需要从磁盘中读取少数的数据也就能获取某个用户的全部邮件。如果没有聚簇索引,则每封邮件都可能导致一次磁盘I/O.
  • 数据块访问更快。聚簇索引将索引和数据保存在同一个B-Tree中,因此从聚簇索引中获取数据通常比在非聚簇索引中查找更快。
  • 使用覆盖索引扫描的查询可以直接使用叶节点中的主键值
  • 如果在设计表和查询时能重复利用上面的优点,能极大的提升性能
聚簇索引的缺点
  • 聚簇数据最大限度地提高了I/O密集型应用的性能,但如果数据全部都放在内存中,则访问的顺序就没有那么重要了,聚簇索引也就没什么优势了。‘’
  • 插入速度严重依赖插入顺序。按照主键的顺序插入是加载数据到InnoDB表中速度最快的方式。但如果不是按照主键顺序加载数据,那么在加载完成后最好使用OPTIMIZE TABLE命令重新组织一下表。
  • 更新聚簇索引的代价很高,因为会强制InnoDB将每个被更新的行移动到新的位置。
  • 基于聚簇索引的表在插入新行,或者主键被更新导致需要移动行的时候,可能面临页分裂的问题。当行的主键要求必须是这一行插入到某个已满的页中时,存储引擎会将该页分裂成两个页面来容纳该行,这就是一次页分裂操作。页分裂操作会导致占用更多的磁盘空间。
  • 聚簇索引可能导致全表扫描。尤其是行比较稀疏,或者由于页分裂导致数据存储不连续的时候。
  • 二级索引(非聚簇索引)可能比想象的要更大,因为在二级索引的叶子节点包含了引用行的主键列
  • 二级索引访问需要两次索引查找,而不是一次
  • 解释: 为什么二级索引需要两次索引查找? 答案在于二级索引中保存的“行指针”的实质,要记住,二级索引叶子节点保存的不是指向行的物理位置的指针,而是行的主键值。这就意味着通过二级索引查找行,存储引擎需要找到二级索引的叶子节点获得对应的主键值,然后根据这个值去聚簇索引中查找对应的行。这里做了重复的工作:两次B-Tree查找而不是一次。
InnoDB 和 MyISAM的数据分布对比
  • 聚簇索引和非聚簇索引的数据分布有区别,以及对应的主键索引和二级索引的数据分布也有区别,通常会让人感到困惑和意外,来看看InnoDB和MyISAM是如何存储下面这个表的:
	CREATE TABLE layout_test(
		col1 int NOT NULL,
		col2 int NOT NULL,
		PRIMARY KEY(col1),
		KEY(col2)
	);
  • 假设该表的主键值为1~10000,按照碎银顺序插入并使用OPTIMIZE TABLE命令做了优化。换句话说,数据在磁盘上的存储方式已经最优,但是行的顺序是随机的。列col2的值是从1~100之间的随机复制,所有有很多重复对的值。
  • MyISAM的数据分布。MyISAM的数据分布非常简单,MyISAM按照数据插入的顺序存储在磁盘上。
  • 在行的旁边显示了行号,从0开始递增。因为行是定长的,所以MyISAM可以从表的开头跳过所需的字节找到需要的行。
    在这里插入图片描述
  • 这种分布方式很容易创建索引,下图显示的一系列图,隐藏了页的物理细节,只显示索引中的"节点",索引中的每个叶子节点包含“行号”
    在这里插入图片描述
  • 这里忽略了一些细节,例如前一个B-Tree节点有多少个内部节点,不过这并不影响对非聚簇索引引擎的基本数据分布的理解。
  • 那么col2列上的索引又会如何?他和其他索引没有什么区别。下图显示了col2的索引。
    在这里插入图片描述
  • 事实上,MyISAM中主键索引和其他索引在结构上没有什么不同,主键索引就是一个名为PRIMARY的唯一非空索引。
  • InnoDB的数据分布。因为InnoDB支持聚簇索引,所以使用非常不同的方式存储同样的数据。InnoDB以下图所示的方式存储数据
    在这里插入图片描述
  • 大家注意看细节,该图显示了整个表,而不是只有索引。因为在InnoDB中,聚簇索引“就是表”,所以不像MyISAM那样需要独立的行存储。
  • 聚簇索引的每一个叶子节点包含了主键值事务ID用于事务和MVCC的回滚指针以及所有剩余列(在这个例子中col2)。如果主键是一个列前缀索引,InnoDB也会包含完整的主键列和剩下的其他列。
  • 还有一点和MyISAM不同是,InnoDB的二级索引和聚簇索引很不相同。InnoDB二级索引的叶子节点存储的不是行指针,而是主键值,并以此作为指向行的“指针”。这样的策略减少了当出现行移动或者数据页分裂时二级索引的维护工作。使用主键值挡住指针会让二级索引占用更多的空间,换来的好处是,InnoDB在移动时,无需更新二级索引中的这个“指针”。
  • 下图中显示表的col2索引。每一个叶子节点都包含了索引项(这里是col2),紧接着是主键(col)。图中也展示了B-Tree的叶子节点结构,但我们故意省略了非叶子节点这样的细节。InnoDB的飞非叶子节点包含了索引项和写一个指向下级节点的指针(下一级节点可以是非叶子节点,也可以是叶子节点)。这对聚餐索引和二级索引都适用。
    在这里插入图片描述
  • 下图描述InnoDB和MyISAM如何存放表的抽象图。从图中可以很容易看出InnoDB和MyISAM保存数据和索引的区别。
    在这里插入图片描述
在InnoDB表中按主键顺序插入行
  • 如果正在使用InnoDB表并没有什么数据需要聚集,那么可以定义一个代理键作为主键,这种主键的数据应该和应用无关,最简单的方式使用AUTO_INCREMENT自增列.这样说可以保证数据行是按照顺序写入,对于根据主键做关联操作性能会更好.
  • 最好避免随机(不连续且值得分布范围非常大)的聚簇索引,特别是对于I/O密集型的应用.例如,从性能的角度考虑,使用UUID来作为聚簇索引则会很糟糕,他使得聚簇索引的插入变得完全随机,这是最坏的情况,使得数据没有任何聚集特性.
  • 为了演示这一点,我们做了如下两个基准测试.第一个使用整数ID插入userinfo表:
    在这里插入图片描述
  • 第二个例子是userinfo_uuid表.除了主键改为UUID,其余和前面得userinfo表完全相同.
    在这里插入图片描述
  • 我们测试了这两个表得设计. 首先,我们在一个有足够内存得容纳的服务器上向这两个表各插入100万条记录.然后向这两个表继续插入300万条记录,使索引的大小超过服务器的内存容量.下图中对测试结果做了比较
    在这里插入图片描述- 注意到向UUID主键插入行不仅花费时间更长,而且索引占用的空间也更大.这一方面由于主键字段更长;另一方面毫无疑问是由于页分裂和碎片化导致的.
  • 为了明白为什么会这样.来看看往第一个表插入数据时,索引发生了什么变化. 下图中显示插满一个页面后继续插入相邻的下一个页面的场景.
    在这里插入图片描述
  • 因为主键的值是顺序的,所以InnoDB把每一条记录都存储在上一条记录的后面. 当达到最大填充因子时(InnoDB默认的最大填充因子是页大小得15/16,留出部分空间用于以后修改),下一条记录就会写入到新的页中.一旦数据按照这种顺序得方式加载,主键页就会近似于被顺序得记录填满,这也正是所期望得结果.
  • 对比一下向第二个适用了UUID聚簇索引的表插入数据,看看有什么不同,下图显示了结果
    在这里插入图片描述
  • 因为新行的主键值不一定比之前插入的大索引InnoDB无法简单地总是把新行插入到索引的最后,而是需要为新行寻找合适的位置。通常是已有数据的中间位置,并分配空间。这会增加很多的额外工作,并导致数据分布不够优化。下面是总结的一些缺点:
    • 写入的目标页可能已经刷到磁盘上并从缓存中移除,或者是还没有被加载到缓存中,InnoDB在插入之前不得不先找到并从磁盘读取目标页到内存中,这将导致大量的随机I/O.
    • 因为写入是乱序的,InnoDB不得不频繁地做页分裂操作,以便为新行分配空间。页分裂会导致移动大量数据,一次插入最少需要修改三个页而不是一个页。
    • 由于频繁的页分裂,页会变得稀疏并不规则的填充,所以最终的数据会有碎片。
  • 总结起来: 使用InnoDB时,应该尽可能的按主键地顺序插入数据,并且尽可能地使用单调增加的举出键的值来插入新行。

覆盖索引

  • 索引是一种查找数据的高效方式,但是MySQL也可以使用索引来直接获得列的数据,这样就不再需要读取数据行。如果一个索引包含了所有需要查询的字段的值,我们就称之为“覆盖索引”
  • 覆盖索引能够极大的提高性能。考虑一下如果查询只需要扫描索引,而无需回表,会带来所少好处。
    • 索引条目通常远小于数据行的大小,所以如果只需要读取索引,那么MySQL就会极大的减少数据访问量。这对缓存的负载非常重要,因为这种情况下响应时间大部分花费在数据拷贝上,覆盖索引对于I/O密集型的应用也有帮助,因为索引比数据更小,更容易放入内存中。
    • 因为索引是按照列值顺序存储的,所以对于I/O密集型的范围查询会比随机从磁盘读取每一行的数据的I/O要少的多。
    • 一些存储引擎如MyISAM在内存中只缓存索引,数据则依赖于操作系统来缓存,因为要访问的数据需要一次系统调用。这可能会导致严重的性能问题,尤其是那些系统调用占了数据访问的最大开销的场景。
    • 由于InnoDB的聚簇索引,覆盖索引对InnnoDB表特别有用。InnoDB的二级索引在叶子节点保存了行的主键值,所以如果二级索引能够覆盖查询,则可以避免对主键的二次查找
  • 当发起一次索引覆盖的查询。在EXPLAIN的Extra类可以看到“Using index”的信息 。例如,表sakila.inventory有一个多列索引(store_id,film_id)。MySQL如果只需要访问这两列,就可以使用这个索引做覆盖索引。如下图所示:
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44624375/article/details/114198069