内容简介:MySql官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。InnoDB存储引擎支持一下几种常见的索引:B+Tree索引是最为常见,也是在数据库中使用最为频繁的一种索引。B+Tree是为磁盘或其他直接存取辅助设备设计的一种平衡查找树。在B+Tree中,所有记录节点都是按键值的大小顺序存放在同一层的叶子结点上,由各叶子结点指针进行连接。先来看一个B+Tree,其高度为2,每页可存放4条记录,扇出(fan out)为5,如图所示
MySql官方对索引的定义为:索引(Index)是帮助 MySQL 高效获取数据的数据结构。InnoDB存储引擎支持一下几种常见的索引:
-
B+树索引
-
哈希索引
-
全文索引
1、B+树索引
B+Tree索引是最为常见,也是在数据库中使用最为频繁的一种索引。
B+Tree是为磁盘或其他直接存取辅助设备设计的一种平衡查找树。在B+Tree中,所有记录节点都是按键值的大小顺序存放在同一层的叶子结点上,由各叶子结点指针进行连接。先来看一个B+Tree,其高度为2,每页可存放4条记录,扇出(fan out)为5,如图所示
B+Tree的插入必须保证插入后叶子结点中的记录依然有序,同时需要考虑插入到B+Tree的三种情况,每种情况都有可能会导致不同的插入算法。
B+Tree使用填充因子来控制树的删除变化,50%是填充因子可设的最小值。B+树的删除操作同样必须保证删除后叶子节点中的记录依然有序,同插入一样,B+Tree的删除操作同样需要考虑一下三种情况,与插入不同的是,删除根据填充因子的变化来衡量。
前面讨论的都是B+树的数据结构及其一般操作,B+树索引的本质就是B+树在数据库中的实现。但是B+索引在数据库中有一个特点就是高扇出性,因此在数据库中,B+树的高度一般都在2-4层,也就是说查找某一键值的行记录时最多只需要2-4次IO,当前一般的机械磁盘每秒至少可以做100次IO,2~4次的IO意味着查询时间只需0.02~0.04秒.数据库中的B+树索引可以分为聚集索引和辅助索引,但不管是聚集还是辅助的索引,其内部都是B+树的,即高度平衡的,叶子节点存放着所有的数据。聚集索引与辅助索引不同的是,叶子结点存放的是否是一整行的信息。
聚集索引
聚集索引就是按照每张表的主键构造一颗B+树,同时叶子结点中存放的即为整张表的行记录数据,也将聚集索引的叶子节点称为数据页.聚集索引的这个特性决定了索引组织表中数据也是索引的一部分.同B+树数据结构一样,每个数据页都通过一个双向链表来进行链接.
由于实际的数据页只能按照一颗B+树进行排序,因此每张表只能拥有一个聚集索引.在大多数情况下,查询优化器倾向于采用聚集索引.因为聚集索引能够在B+树索引的叶子结点上直接找到数据.此外,由于定义来数据的逻辑顺序,聚集索引能够特别快地访问针对范围值的查询,查询优化器能够快速发现某一段范围的数据页需要扫描.
聚集索引的存储并不是物理上连续的,而是逻辑上连续的.这其中有两点:一是前面说过的页通过双向链表链接,页按照主键的顺序排序;另一点是每个页中的记录也是通过双向链表进行维护的,物理存储上可以同样不按照主键存储.
聚集的数据有一些重要的优点:
可以把相关的数据保存在一起。例如实现电子邮箱时,可根据用户ID来聚集数据,这样只需要从磁盘读取少量的数据页就能获得某个用户的全部邮件。如果没有使用聚簇索引,则每封邮件都可能导致一次磁盘I/O。 数据访问更快。聚簇索引将索引和数据保存在同一个B-Tree中,因此从聚簇索引中获得数据通常比非聚簇索引要快。 使用聚簇索引扫描的查询可以直接使用叶节点中的主键值。
对于主键 排序 查找和范围查找速度非常快.叶子节点的数据就是用户所要查找的数据.如用户需要查找一张注册用户的表,查询最后注册的10位用户,由于B+树索引是双向链表的,用户可以快速找到最后一个数据页,并取出10条记录.若用命令explain进行分析,可得:
可以看到虽然使用order by对记录进行排序,但是在实际过程中并没有进行所谓的filesort操作,而这就是因为聚集索引的特点. 范围查询,即如果要查找主键某一范围内的数据,通过叶子结点的上层中间节点就可以得到页的范围,之后直接读取数据页即可.
同时也有一些缺点:
聚簇索引最大限度地提高了I/O密集型应用的性能,但如果数据全部都放在内存中,则访问顺序就没那么重要了,聚簇索引也就没什么优势了。 插入速度严重依赖于插入顺序。 更新聚簇索引列的代价很高,因为会强制InnoDB将每个被更新的行移动到新的位置。 插入新行或者主键更新时,可能面临“页分裂”的问题,页分裂可能会导致表占用更多的磁盘空间。 可能导致全表扫描变慢,尤其是行稀疏的时候或存储不连续的时候。 二级索引访问需要两次索引查找,而不是一次。
在InnoDB表中按主键顺序插入行,最好避免随机(不连续且值的分布范围非常大)聚簇索引,特别是对于I/O密集型的应用。例如,从性能角度考虑,使用UUID来作为聚簇索引则会很糟糕,无论是插入速度还是索引大小。
辅助索引
对于辅助索引,叶子节点并不包含行记录的全部数据。叶子结点除了包含键值以外,每个叶子结点中的索引行中还包含了一个书签。该书签用来告诉innoDB存储引擎哪里可以找到与索引相对应的行数据。由于innoDB存储引擎是索引组织表,因此innoDB存储引擎的辅助索引的书签就是相应行数据的聚集索引键。
辅助索引的存在并不影响数据在聚集索引中的组织,因此每张表上可以有多个辅助索引。当通过辅助索引来寻找数据时,innoDB存储引擎会遍历辅助索引并通过叶级别的指针获得指向主键索引的主键,然后再通过主键索引来找到一个完整的行记录。
联合索引
联合索引也是一颗B+Tree,不同的是联合索引的键值的数量不是1,而是大于等于2.
如上图,数据按(a,b)的顺序进行了存放。因此对于查询SELECT * FROM TABLE WHERE a=xx and b=xx,显然可以利用这个联合索引。对于单个的a列查询也可以使用这个查询。但是对于b列的查询则不可以使用这棵B+树索引,因为b列的值并不是排序的。联合索引的第二个好处是已经对第二个键值进行了排序处理(在确定前一列值的情况下),因此可以利用联合索引避免多一次的排序操作。
覆盖索引
如果一个索引包含所有需要查询的字段的值,我们就称之为“覆盖索引”。 覆盖索引能极大地提高性能。考虑一下如果查询只需要扫描索引而无需回表,会带来多少好处:极大地减少数据访问量,因为减少了不必要的数据拷贝。因为索引是按照列值顺序存储的,所以对于I/O密集型的范围查询会比随机从磁盘读取每一行数据的I/O要少得多。 一些存储引擎如MyISAM在内存中只能缓存索引,数据则依赖于操作系统来缓存,因此要访问数据需要一次系统调用。由于InnoDB的聚簇索引,覆盖索引对InnoDB特别有用,因为可以避免对主键索引的二次查询。当发起一个被覆盖索引的查询时,在EXPLAIN的Extra列可以看到“Using index”的信息。
B+Tree索引对如下类型的查询有效:
- 全值匹配
- 匹配最左前缀
- 匹配列前缀
- 匹配范围值
- 精确匹配某一列并范围匹配其后一列
- 只访问索引的查询
- B+Tree索引的限制:
- 如果不是按照索引的最左列开始查找,则无法使用
- 不能跳过索引中的列
- 查询中有某个列的范围查询,则其右边的所有列都无法使用索引优化
为什么要用B+Tree索引
一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗,相对于内存存取,I/O存取的消耗要高几个数量级,所以评价一个数据结构作为索引的优劣最重要的指标就是在查找过程中磁盘I/O操作次数的渐进复杂度。换句话说,索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数。下面先介绍内存和磁盘存取原理,然后再结合这些原理分析B-/+Tree作为索引的效率。
主存存取原理
目前计算机使用的主存基本都是随机读写存储器(RAM),现代RAM的结构和存取原理比较复杂,这里本文抛却具体差别,抽象出一个十分简单的存取模型来说明RAM的工作原理。
从抽象角度看,主存是一系列的存储单元组成的矩阵,每个存储单元存储固定大小的数据。每个存储单元有唯一的地址,现代主存的编址规则比较复杂,这里将其简化成一个二维地址:通过一个行地址和一个列地址可以唯一定位到一个存储单元。上图展示了一个4 x 4的主存模型。
主存的存取过程如下:当系统需要读取主存时,则将地址信号放到地址总线上传给主存,主存读到地址信号后,解析信号并定位到指定存储单元,然后将此存储单元数据放到数据总线上,供其它部件读取。写主存的过程类似,系统将要写入单元地址和数据分别放在地址总线和数据总线上,主存读取两个总线的内容,做相应的写操作。这里可以看出,主存存取的时间仅与存取次数呈线性关系,因为不存在机械操作,两次存取的数据的“距离”不会对时间有任何影响,例如,先取A0再取A1和先取A0再取D3的时间消耗是一样的。
磁盘存取原理
上文说过,索引一般以文件形式存储在磁盘上,索引检索需要磁盘I/O操作。与主存不同,磁盘I/O存在机械运动耗费,因此磁盘I/O的时间消耗是巨大的。下图是磁盘的整体结构示意图。
一个磁盘由大小相同且同轴的圆形盘片组成,磁盘可以转动(各个磁盘必须同步转动)。在磁盘的一侧有磁头支架,磁头支架固定了一组磁头,每个磁头负责存取一个磁盘的内容。磁头不能转动,但是可以沿磁盘半径方向运动(实际是斜切向运动),每个磁头同一时刻也必须是同轴的,即从正上方向下看,所有磁头任何时候都是重叠的(不过目前已经有多磁头独立技术,可不受此限制)。下图是磁盘结构的示意图
盘片被划分成一系列同心环,圆心是盘片中心,每个同心环叫做一个磁道,所有半径相同的磁道组成一个柱面。磁道被沿半径线划分成一个个小的段,每个段叫做一个扇区,每个扇区是磁盘的最小存储单元。为了简单起见,我们下面假设磁盘只有一个盘片和一个磁头。当需要从磁盘读取数据时,系统会将数据逻辑地址传给磁盘,磁盘的控制电路按照寻址逻辑将逻辑地址翻译成物理地址,即确定要读的数据在哪个磁道,哪个扇区。为了读取这个扇区的数据,需要将磁头放到这个扇区上方,为了实现这一点,磁头需要移动对准相应磁道,这个过程叫做寻道,所耗费时间叫做寻道时间,然后磁盘旋转将目标扇区旋转到磁头下,这个过程耗费的时间叫做旋转时间。
局部性原理与磁盘预读
由于存储介质的特性,磁盘本身存取就比主存慢很多,再加上机械运动耗费,磁盘的存取速度往往是主存的几百分之一,因此为了提高效率,要尽量减少磁盘I/O。为了达到这个目的,磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存。这样做的理论依据是计算机科学中著名的局部性原理:
当一个数据被用到时,其附近的数据也通常会马上被使用。
程序运行期间所需要的数据通常比较集中。由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率。
预读的长度一般为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页得大小通常为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,然后异常返回,程序继续运行。
B-/+Tree索引的性能分析
上文说过一般使用磁盘I/O次数评价索引结构的优劣。先从B-Tree分析,根据B-Tree的定义,可知检索一次最多需要访问h个节点。数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入。为了达到这个目的,在实际实现B-Tree还需要使用如下技巧:
每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,加之计算机存储分配都是按页对齐的,就实现了一个node只需一次I/O。
B-Tree中一次检索最多需要h-1次I/O(根节点常驻内存),渐进复杂度为O(h)=O(logdN)O(h)=O(logdN)。一般实际应用中,出度d是非常大的数字,通常超过100,因此h非常小(通常不超过3)。
综上所述,用B-Tree作为索引结构效率是非常高的。
而红黑树这种结构,h明显要深的多。由于逻辑上很近的节点(父子)物理上可能很远,无法利用局部性,所以红黑树的I/O渐进复杂度也为O(h),效率明显比B-Tree差很多。
上文还说过,B+Tree更适合外存索引,原因和内节点出度d有关。从上面分析可以看到,d越大索引的性能越好,而出度的上限取决于节点内key和data的大小:dmax=floor(pagesize/(keysize+datasize+pointsize))dmax=floor(pagesize/(keysize+datasize+pointsize))
floor表示向下取整。由于B+Tree内节点去掉了data域,因此可以拥有更大的出度,拥有更好的性能。
B Tree与B+Tree的区别
B+Tree是B Tree的一种变体,与B Tree相比,B+Tree有以下不同点: 每个节点的指针上限为2d而不是2d+1 内存点不存储data,只存储key,叶子结点不存储指针 B+Tree的每个叶子节点增加一个指向相邻叶子结点的指针。
B Tree与B+Tree各自的优点
B+Tree内节点不存储数据,所以一页内存可以存放更多key,因此,访问叶节点上的数据缓存未命中的概率更小。 B+Tree的叶子结点是互相连接的,所以做范围扫描会更加迅速。 B Tree的每个节点都包含数据,因此,经常访问接近根节点的数据时会更加迅速。
Hash索引
InnoDB存储引擎使用哈希算法来对字典进行查找,其冲突机制采用链表方式,哈希函数采用除法散列方式。
自适应哈希索引:当InnoDB注意到某些索引值被使用得非常频繁时,它会在内存中基于B+Tree索引之上再创建一个哈希索引,这样就让B+Tree索引也具有哈希索引的一些优点。这是数据库自身创建并使用,DBA并不能对其进行干预。需要注意的是,哈希索引只能用来搜索等值的查询,而对于其他查找类型,如范围查找,是不能使用哈希索引的。不过可以通过参数innodb_adaptive_hash_index来禁用或启动此特性,默认为开启。
全文检索
MySQL数据库通过MATCH()....AGAINST()语法支持全文检索的查询,MATCH指定了需要被查询的列,AGAINST指定了使用何种方法进行查询.
总结下来索引有如下三个优点:
-
1.索引大大减少了服务器所需要扫描的数据量
-
2.索引可以帮助服务器避免排序和临时表
-
3.索引可以将随机I/O变为顺序I/O
索引设计概要
磁盘IO
一个数据库必须保证其中存储的所有数据都是可以随时读写的,同时因为 MySQL 中所有的数据其实都是以文件的形式存储在磁盘上的,而从磁盘上随机访问对应的数据非常耗时,所以数据库程序和操作系统提供了缓冲池和内存以提高数据的访问速度。
除此之外,我们还需要知道数据库对数据的读取并不是以行为单位进行的,无论是读取一行还是多行,都会将该行或者多行所在的页全部加载进来,然后再读取对应的数据记录;也就是说,读取所耗费的时间与行数无关,只与页数有关。
在 MySQL 中,页的大小一般为 16KB,不过也可能是 8KB、32KB 或者其他值,这跟 MySQL 的存储引擎对数据的存储方式有很大的关系,文中不会展开介绍,不过索引或行记录是否在缓存池中极大的影响了访问索引或者数据的成本。
随机读取
数据库等待一个页从磁盘读取到缓存池的所需要的成本巨大的,无论我们是想要读取一个页面上的多条数据还是一条数据,都需要消耗约10ms 左右的时间:
10ms 的时间在计算领域其实是一个非常巨大的成本,假设我们使用脚本向装了 SSD 的磁盘上顺序写入字节,那么在 10ms 内可以写入大概 3MB 左右的内容,但是数据库程序在 10ms 之内只能将一页的数据加载到数据库缓冲池中,从这里可以看出随机读取的代价是巨大的。
这 10ms 的一次随机读取是按照每秒 50 次的读取计算得到的,其中等待时间为 3ms、磁盘的实际繁忙时间约为 6ms,最终数据页从磁盘传输到缓冲池的时间为 1ms 左右,在对查询进行估算时并不需要准确的知道随机读取的时间,只需要知道估算出的 10ms 就可以了。
内存读取
如果在数据库的缓存池中没有找到对应的数据页,那么会去内存中寻找对应的页面:
当对应的页面存在于内存时,数据库程序就会使用内存中的页,这能够将数据的读取时间降低一个数量级,将 10ms 降低到 1ms;MySQL 在执行读操作时,会先从数据库的缓冲区中读取,如果不存在与缓冲区中就会尝试从内存中加载页面,如果前面的两个步骤都失败了,最后就只能执行随机 IO 从磁盘中获取对应的数据页。
顺序读取
从磁盘读取数据并不是都要付出很大的代价,当数据库管理程序一次性从磁盘中顺序读取大量的数据时,读取的速度会异常的快,大概在 40MB/s 左右。
如果一个页面的大小为 4KB,那么 1s 的时间就可以读取 10000 个页,读取一个页面所花费的平均时间就是 0.1ms,相比随机读取的 10ms 已经降低了两个数量级,甚至比内存中读取数据还要快。
数据页面的顺序读取有两个非常重要的优势:
-
同时读取多个界面意味着总时间的消耗会大幅度减少,磁盘的吞吐量可以达到 40MB/s;
-
数据库管理程序会对一些即将使用的界面进行预读,以减少查询请求的等待和响应时间;
查询过程
索引片
索引片其实就是 SQL 查询在执行过程中扫描的一个索引片段,在这个范围中的索引将被顺序扫描,根据索引片包含的列数不同,数据库索引设计与优化书中对将索引分为宽索引和窄索引:
对于查询SELECT id, username, age FROM users WHERE username="draven"来说,(id, username) 就是一个窄索引,因为该索引没有包含存在于 SQL 查询中的 age 列,而 (id, username, age) 就是该查询的一个宽索引了,它包含这个查询中所需要的全部数据列。
宽索引能够避免二次的随机 IO,而窄索引就需要在对索引进行顺序读取之后再根据主键 id 从主键索引中查找对应的数据:
对于窄索引,每一个在索引中匹配到的记录行最终都需要执行另外的随机读取从聚集索引中获得剩余的数据,如果结果集非常大,那么就会导致随机读取的次数过多进而影响性能。
过滤因子
从上一小节对索引片的介绍,我们可以看到影响 SQL 查询的除了查询本身还与数据库表中的数据特征有关,如果使用的是窄索引那么对表的随机访问就不可避免,在这时如何让索引片变『薄』就是我们需要做的了。
一个 SQL 查询扫描的索引片大小其实是由过滤因子决定的,也就是满足查询条件的记录行数所占的比例:
对于 users 表来说,sex=”male” 就不是一个好的过滤因子,它会选择整张表中一半的数据,所以在一般情况下我们最好不要使用 sex 列作为整个索引的第一列;而 name=”draven” 的使用就可以得到一个比较好的过滤因子了,它的使用能过滤整个数据表中 99.9% 的数据;当然我们也可以将这三个过滤进行组合,创建一个新的索引 (name, age, sex) 并同时使用这三列作为过滤条件:
组合条件的过滤因子就可以达到十万分之 6 了,如果整张表中有 10w 行数据,也只需要在扫描薄索引片后进行 6 次随机读取,这种直接使用乘积来计算组合条件的过滤因子其实有一个比较重要的问题:列与列之间不应该有太强的相关性,如果不同的列之间有相关性,那么得到的结果就会比直接乘积得出的结果大一些,比如:所在的城市和邮政编码就有非常强的相关性,两者的过滤因子直接相乘其实与实际的过滤因子会有很大的偏差,不过这在多数情况下都不是太大的问题。
对于一张表中的同一个列,不同的值也会有不同的过滤因子,这也就造成了同一列的不同值最终的查询性能也会有很大差别:
当我们评估一个索引是否合适时,需要考虑极端情况下查询语句的性能,比如 0% 或者 50% 等;最差的输入往往意味着最差的性能,在平均情况下表现良好的 SQL 语句在极端的输入下可能就完全无法正常工作,这也是在设计索引时需要注意的问题。
总而言之,需要扫描的索引片的大小对查询性能的影响至关重要,而扫描的索引记录的数量,就是总行数与组合条件的过滤因子的乘积,索引片的大小最终也决定了从表中读取数据所需要的时间。
匹配列与过滤列
假设在 users 表中有 name、age 和 (name, sex, age) 三个辅助索引;当 WHERE 条件中存在类似 age = 21 或者 name = “draven” 这种等值谓词时,它们都会成为匹配列(Matching Column)用于选择索引树中的数据行,但是当我们使用以下查询时:
SELECT * FROM users WHERE name = "draven" AND sex = "male" AND age > 20;
虽然我们有 (name, sex, age) 索引包含了上述查询条件中的全部列,但是在这里只有 name 和 sex 两列才是匹配列,MySQL 在执行上述查询时,会选择 name 和 sex 作为匹配列,扫描所有满足条件的数据行,然后将 age 当做过滤列(Filtering Column):
过滤列虽然不能够减少索引片的大小,但是能够减少从表中随机读取数据的次数,所以在索引中也扮演着非常重要的角色。
索引的设计
三星索引
三星索引是对于一个查询语句可能的最好索引,如果一个查询语句的索引是三星索引,那么它只需要进行 一次磁盘的随机读及一个宽索引片的顺序扫描 就可以得到全部的结果集;因此其查询的响应时间比普通的索引会少几个数量级;根据书中对三星索引的定义,我们可以理解为主键索引对于WHERE id = 1就是一个特殊的三星索引,我们只需要对主键索引树进行一次索引访问并且顺序读取一条数据记录查询就结束了。
为了满足三星索引中的三颗星,我们分别需要做以下几件事情:
-
第一颗星需要取出所有等值谓词中的列,作为索引开头的最开始的列(任意顺序);
-
第二颗星需要将 ORDER BY 列加入索引中;
-
第三颗星需要将查询语句剩余的列全部加入到索引中;
三星索引的概念和星级的给定来源于数据库索引设计与优化书中第四章三星索引一节。
如果对于一个查询语句我们依照上述的三个条件进行设计,那么就可以得到该查询的三星索引,这三颗星中的最后一颗星往往都是最容易获得的,满足第三颗星的索引也就是上面提到的宽索引,能够避免大量的随机 IO,如果我们遵循这个顺序为一个 SQL 查询设计索引那么我们就可以得到一个完美的索引了;这三颗星的获得其实也没有表面上这么简单,每一颗星都有自己的意义:
第一颗星不只是将等值谓词的列加入索引,它的作用是减少索引片的大小以减少需要扫描的数据行;
第二颗星用于避免排序,减少磁盘 IO 和内存的使用;
第三颗星用于避免每一个索引对应的数据行都需要进行一次随机 IO 从聚集索引中读取剩余的数据;
在实际场景中,问题往往没有这么简单,我们虽然可以总能够通过宽索引避免大量的随机访问,但是在一些复杂的查询中我们无法同时获得第一颗星和第二颗星。
高性能的索引策略
独立的列
索引列不能是表达式的一部分,也不能是函数的参数。
前缀索引
有时候需要索引很长的字符串,这会让索引变得大且慢。一个策略是前面提过的哈希索引,但是这样做还不够。通常可以索引开始的部分索引,这样可以大大节约索引空间,从而提高索引效率,但这样也会降低索引的选择性。对于BLOB、TEXT、或者很长的VARCHAR类型的列,必须使用前缀索引,因为MySQL不允许索引这些列的完整长度。所以我们应该选择合适的前缀长度。
使用索引扫描来做排序
MySQL有两种方式来生成有序的结果:通过排序操作;或者按索引顺序扫描;如果EXPLAIN出来的type列的值为“index”,则说明MySQL使用了索引扫描来做排序。
扫描索引本身很快的,因为只需要从一条索引记录移动到紧接着的下一条记录。但如果索引不能覆盖查询所需的全部列,那就不得不每扫描一条索引记录就都回表查询一次对应的行。这基本上都是随机I/O,因此速度就慢得多。
下面是一些不能使用索引做排序的查询:
使用了两种不同的方向,但是索引列都是正序排序的。
ORDER BY子句中引用了一个不在索引中的列。
WHERE和ORDER BY中的列无法组成索引的最左前缀。
范围条件列之后的列无法使用索引。
压缩索引
MyISAM使用前缀压缩来减少索引的大小,从而让更多的索引可以放入内存中。方法是:先完全保存索引块中的第一个值,然后将其他值和第一个值进行比较得到相同前缀的字节数和剩余的不同后缀部分把这部分存储起来即可。例如,第一个值是“perform”,第二个值是“performance”,那么第二个值的前缀压缩后存储的是类似“7,ance”这样的形式。
冗余和重复索引
表中的索引越多插入速度会越慢。
索引和锁
索引可以让查询锁定更少的行。即使使用了索引,InnoDB也可能锁住一些不需要的数据。如果不能使用索引查找和锁定行的话可能会更糟糕,MySQL会做全表扫描并锁住所有的行,而不管是不是需要。
优化排序
对于那些选择性非常低的列,可以增加一些特殊的索引来做排序。
不过即使有索引,如果用户界面上需要翻页,并且翻页翻到比较靠后时查询也可能非常慢。无论如何创建索引,这种查询都是个严重的问题。因为随着偏移量的增加,MySQL需要花费大量的时间来扫描需要丢弃的数据。反范式化、预先计算和缓存可能是解决这类查询的仅有策略。
优化这类索引的另一个比较好的策略是使用延迟关联,通过使用覆盖索引查询返回需要的主键,再根据这些主键关联原表获得需要的行。这可以减少MySQL扫描那些需要丢弃的行数。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- MySQL索引使用说明(单列索引和多列索引)
- Elasticsearch索引的基本操作(3)-索引的滚动索引
- Coreseek 增量索引模拟实时索引
- Coreseek 增量索引模拟实时索引
- MySQL高效索引之覆盖索引
- MySQL -- 普通索引与唯一索引
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Compilers
Alfred V. Aho、Monica S. Lam、Ravi Sethi、Jeffrey D. Ullman / Addison Wesley / 2006-9-10 / USD 186.80
This book provides the foundation for understanding the theory and pracitce of compilers. Revised and updated, it reflects the current state of compilation. Every chapter has been completely revised ......一起来看看 《Compilers》 这本书的介绍吧!
HTML 编码/解码
HTML 编码/解码
html转js在线工具
html转js在线工具