zoukankan      html  css  js  c++  java
  • MySQL之索引(一)

    创建高性能索引

    索引是存储引擎用于快速找到记录的一种数据结构。这是索引的基本功能。索引对于良好的性能非常关键。尤其是当表中的数据量越来越大时,索引对性能的影响愈发重要。在数据量较小且负载较低时,不恰当的索引对性能的影响可能还不明显,但随着数据量增大时,性能则会急剧下降。不过,索引却经常被忽略,有时候甚至被误解,所以在实际案例中经常会遇到由糟糕索引导致的问题。索引优化应该是对查询性能优化最有效的手段了。索引能够轻易将查询性能提高好几个数量级。“最优”的索引有时比一个“好的”索引性能要高出两个数量级,创建一个“最优”索引经常要重写查询。

    索引基础

    在MySQL中,存储引擎现在索引中找到对应的值,然后根据匹配的索引记录找到对应的数据行。假如要运行下面的查询:

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

      

    如果在actor_id列上建有索引,则MySQL将使用该索引找到actor_id为5的行,也就是说,MySQL先在索引上按值进行查找,然后返回所有包含该值的数据行。索引可以包含一个或多个列的值。如果索引包含多个列,那么列的顺序也十分重要,因为MySQL只能高效地使用索引的最左前缀列。创建一个包含两个列的索引,和创建两个只包含一列的索引是大不相同的。

    索引类型

    索引有很多种类型,可以为不同的场景提供更好的性能。在MySQL中,索引是在存储引擎层而不是服务器层实现的。所以,并没有统一的索引标准:不同的存储引擎的索引的工作方式是不同的,也不是所有的存储引擎都支持所有类型的索引。即使多个存储引擎支持同一种类型的索引,其底层的实现也可能不同。下面我们先来看看MySQL支持的索引类型,以及它们的优点和缺点。

    B-Tree索引

    正常情况下,如果不指定索引的类型,那么一般是指B-Tree索引,它使用B-Tree数据结构来存储数据。大多数MySQL引擎都支持这种索引。Archive引擎是一个例外,5.1之前这个引擎不支持任何索引,5.1开始才支持单列自增的索引。

    我们使用术语“B-Tree” ,是因为MySQL在CREATE TABLE和其他语句中也使用该关键字 。不过,底层的存储引擎也可能使用不同的存储结构,例如,NDB集群存储引擎内部实际上使用了T-Tree结构存储这种索引,即使其名字是BTREE;InnoDB则使用的是B+Tree。

    存储引擎以不同的方式使用B-Tree索引,性能也各有不同。例如,MyISAM使用前缀压缩技术使得索引更小,但InnoDB则按照原数据格式进行存储。再如MyISAM索引通过数据的物理位置引用被索引的行,而InnoDB则根据主键引用被索引的行。

    B-Tree通常意味着所有的值都是按顺序存储的,并且每一个叶子到根的距离相同。图1-1展示了B-Tree索引的抽象表示,大致反映了InnoDB索引是如何工作的。MyISAM使用的结构有所不同,但基本思想是类似的。

    图1-1   建立在B-Tree结构上的索引

    B-Tree索引能够加快访问数据的速度,因为存储引擎不再需要进行全表扫描来获取需要的数据,取而代之的是从索引的根节点开始进行搜索。根节点的槽中存放了指向子节点的指针,存储引擎根据这些指针向下层查找。通过比较节点页的值和要查找的值可以找到合适的指针进入下层子节点,这些指针实际上定义了子节点页中值的上限和下限。最终存储引擎要么是找到对应的值,要么该记录不存在。

    叶子节点比较特别,它们的指针指向的是被索引的数据,而不是其他的节点页(不同引擎的“指针”类型不同)。图1-1中仅绘制了一个节点和其对应的叶子节点,其实在根节点和叶子节点之间可能有很多层节点页。树的深度和表的大小直接相关。

    B-Tree对索引列是顺序组织存储的,所以很适合查找范围数据。例如,在一个基于文本域的索引树上,按字母顺序传递连续的值进行查找是非常合适的,所以像“找出所有以I到K开头的名字”这样的查找效率会非常高。

    假如有如下数据表:

    CREATE TABLE People (
    	last_name 	varchar(32) 	not null,
    	first_name 	varchar(32) 	not null,
    	dob 		date 			not null,
    	gender 		enum ('m', 'f') not null,
    	key(last_name, first_name, dob)
    );
    

      

    对于表中的每一行数据,索引中包含了last_name、first_name和dob列的值,图1-2显示了该索引是如何组织数据的存储的。

    图1-2   B-Tree索引树中的部分条目示例

    索引对多个值进行排序的依据是CREATE TABLE语句中定义索引时列的顺序。看一下最后两个条目,两个人的姓和名都一样,则根据他们的出生日期来排列顺序。

    可以使用B-Tree索引的查询类型。B-Tree索引适用于全键值、键值范围或键前缀查找。其中键前缀查找只适用于根据最左前缀的查找。前面所述的索引对如下类型的查询有效。

    全值匹配

    全值匹配指的是和索引中的所有列进行匹配,例如前面提到的索引可用于查找姓名为CubaAllen、出生于1960-01-01的人。

    匹配最左前级

    前面提到的索引可用于查找所有姓为Allen的人,即只使用索引的第一列。匹配列前级也可以只匹配某一列的值的开头部分。例如前面提到的索引可用于查找所有以J开头的姓的人。这里也只使用了索引的第一列。

    匹配范围值

    例如前面提到的索引可用于查找姓在Allen和Barrymore之间的人。这里也只使用了索引的第一列。

    精确匹配某一列并范围匹配另一列

    前面提到的索引也可用于查找所有姓为Allen,并且名字是字母K开头(比如Kim、Karl等)的人。即第一列last_name全匹配,第二列first_name范围匹配。

    只访问索引的查询

    B-Tree通常可以支持“只访问索引的查询”,即查询只需要访问索引,而无须访问数据行。

    因为索引树中的节点是有序的,所以除了按值查找之外,索引还可以用于查询中的ORDER BY操作(按顺序查找)。一般来说,如果B-Tree可以按照某种方式查找到值,那么也可以按照这种方式用于排序。所以,如果ORDER BY子句满足前面列出的几种查询类型,则这个索引也可以满足对应的排序需求。

    下面是一些关于B-Tree索引的限制:

    • 如果不是按照索引的最左列开始查找,则无法使用索引。例如上面例子中的索引在每用于查找名字为Bill的人,也无怯查找某个特定生日的人,因为这两列都不是最左数据列。类似地,也无战查找姓氏以某个字母结尾的人。
    • 不能跳过索引中的列。也就是说,前面所述的索引无法用于查找姓为Smith并且在某个特定日期出生的人。如果不指定名(first_name),则MySQL只能使用索引的第一列。
    • 如果查询中有某个列的范围查询,则其右边所有列都无法使用索引优化查找。例如有查询 WHERE lastname='Smith' AND firstname like '%J%' AND dob='1976-12-23',这个查询只能使用索引的前两列,因为这里的LIKE是一个范围条件(但是服务器可以把其余列用于其他目的)。如果范围查询列值的数量有限,那么可以通过使用多个等于条件来代替范围条件。

    到这里大家应该可以明白,前面提到的索引列的顺序是多么的重要:这些限制都和索引列的顺序有关。在优化性能的时候,可能需要使用相同的列但顺序不同的索引来满足不同类型的查询需求。有些限制并不是B-Tree本身导致的,而是MySQL优化器和存储引擎使用索引的方式导致。

    哈希索引

    哈希索引(hash index)基于哈希表实现,只有精确匹配索引所有列的査询才有效。对 于每一行数据,存储引擎都会对所有的索引列计算一个哈希码(hash code),哈希码是 一个较小的值,并且不同键值的行计算出来的哈希码也不一样。哈希索引将所有的哈希 码存储在索引中,同时在哈希表中保存指向每个数据行的指针。

    在MySQL中,只有Memory引擎显式支持哈希索引。这也是Memory引擎表的默认索 引类型,Memory引擎同时也支持B-Tree索引。值得一提的是,Memory引擎是支持非 唯一哈希索引的,这在数据库世界里面是比较与众不同的。如果多个列的哈希值相同, 索引会以链表的方式存放多个记录指针到同一个哈希条目中。

    下面来看一个例子。假如有如下表:

    CREATE TABLE testhash (
    	fname VARCHAR(50) NOT NULL,
    	lname VARCHAR(50) NOT NULL, 
    	KEY USING HASH(fname)
    )ENGINE=MEMORY;
    

      

    往表插入数据:

    mysql> INSERT INTO testhash(`fname`, `lname`) VALUES('Arjen', 'Lentz'), ('Baron', 'Schwartz'), ('Peter', 'Zaitsev'), ('Vadim', 'Tkachenko');
    

      

    查询表中的数据:

    mysql> SELECT * FROM testhash;
    +-------+-----------+
    | fname | lname     |
    +-------+-----------+
    | Arjen | Lentz     |
    | Baron | Schwartz  |
    | Peter | Zaitsev   |
    | Vadim | Tkachenko |
    +-------+-----------+
    4 rows in set (0.00 sec)
    

      

    假设索引使用假想的哈希函数f(),它返回下面的值(都是示例数据,非真实数据):

    f('Arjen')= 2323 
    f('Baron')= 7437 
    f('Peter')= 8784 
    f('vadim')= 2458
    

      

    则哈希索引的数据结构如下:

    槽(Slot) 值(Value)
    2323 指向第1行的指针
    2458 指向第4行的指针
    7437 指向第2行的指针
    8784 指向第3行的指针

    每个槽的编号是顺序的,但是数据行不是。现在,来看如下査询:

    mysql> SELECT lname FROM testhash WHERE fname='Peter';
    +---------+
    | lname   |
    +---------+
    | Zaitsev |
    +---------+
    1 row in set (0.05 sec)
    

      

    MySQL先计算'Peter'的哈希值,并使用该值寻找对应的记录指针。因为f('Peter')=8784,所以MySQL在索引中查找8784,可以找到指向第3行的指针,最后一步是比较第三行的值是否为'Peter',以确保就是要査找的行。

    因为索引自身只需存储对应的哈希值,所以索引的结构十分紧凑,这也让哈希索引査找的速度非常快。然而,哈希索引也有它的限制:

    • 哈希索引只包含哈希值和行指针,而不存储字段值,所以不能使用索引中的值来避免读取行。不过,访问内存中的行的速度很快,所以大部分情况下这一点对性能的影响并不明显。
    • 哈希索引数据并不是按照索引值顺序存储的,所以也就无法用于排序。
    • 哈希索引也不支持部分索引列匹配査找,因为哈希索引始终是使用索引列的全部内容来计算哈希值的。例如,在数据列(A,B)上建立哈希索引,如果查询只有数据列A,则无法使用该索引。
    • 哈希索引只支持等值比较査询,包括=、IN()、<=> (注意 <> 和 <=> 是不同的操作)。也不支持任何范围査询,例如WHERE price > 100。
    • 访问哈希索引的数据非常快,除非有很多哈希冲突(不同的索引列值却有相同的哈希值)。当出现哈希冲突的时候,存储引擎必须遍历链表中所有的行指针,逐行进行比较,直到找到所有符合条件的行。
    • 如果哈希冲突很多的话,一些索引维护操作的代价也会很高。例如,如果在某个选择性很低(哈希冲突很多)的列上建立哈希索引,那么当从表中删除一行时,存储引擎需要遍历对应哈希值的链表中的每一行,找到并删除对应行的引用,冲突越多,代价越大。

    因为这些限制,哈希索引只适用于某些特定的场合。而一旦适合哈希索引,则它带来的 性能提升将非常显著。举个例子,在数据仓库应用中有一种经典的“星型”schema,需要关联很多査找表,哈希索引就非常适合査找表的需求。

    InnoDB引擎有一个特殊的功能叫做“自适应哈希索引(adaptive hash index)”。当InnoDB注意到某些索引值被使用得非常频繁时,它会在内存中基于B-Tree索引之上再创建一个哈希索引,这样就让B-Tree索引也具有哈希索引的一些优点,比如快速的哈希 査找。这是一个完全自动的、内部的行为,用户无法控制或者配置,不过如果有必要,完全可以关闭该功能。

    空间数据索引(R-Tree)

    MyISAM表支持空间索引,可以用作地理数据存储。和B-TREE索引不同,这类索引无需前缀查询。空间索引会从所有维度来索引数据。查询时,可以有效地使用任意维度来组合查询。必须使用MySQL的GIS相关函数如MBRCONTAINS()等来维护数据。MySQL的GIS支持并不完善,所以大部分人都不会使用这个特性。开源关系数据库系统中对GIS的解决方案做得比较好的是PostgreSQL的PostGIS。

    全文索引

    全文索引是一种特殊类型的索引,它查找的是文本中的关键词,而不是直接比较索引中的值。全文搜索和其他几类索引的匹配方式完全不同,它有许多需要注意的细节,如停用词、词干、布尔搜索等等。全文索引更类似于搜索引擎做的事情,而不是简单的Where条件匹配。

    在相同的列上同时创建全文索引和基于值的B-Tree索引不会有冲突,全文索引适用于MATCH AGAINST操作,而不是普通的WHERE条件操作。

    索引的优点

    索引可以让服务器快速地定位到表的指定位置。但是这并不是索引的唯一作用。根据创建索引的数据结构不同,索引也有一些其他的附加作用。

    最常见的B+Tree索引,按照顺序存储数据,所以MySQL可以用来做ORDER BY和GROUP BY操作。因为数据是有序的,所以B-Tree也就会将相关的列值都存储在一起。最后,因为索引中存储了实际的列值,所以某些查询只使用索引就能够完成全部查询。据此特性,总结下来索引有如下三个优点:

    1. 索引大大减少了服务器需要扫描的数据量。
    2. 索引可以帮助服务器避免排序和临时表。
    3. 索引可以将随机I/O变为顺序I/O。
  • 相关阅读:
    Go语言从入门到放弃(结构体常见的tag)
    Go语言从入门到放弃(设置 go get 为国内源)
    AndroidStuidio安装
    ADB常用命令
    win10安装Nodejs
    VsCode配置Go语言插件
    Visual Studio Code使用指南
    Go语言从入门到放弃(四)
    CentOs7.5安装Redis
    InnoDB INFORMATION_SCHEMA FULLTEXT Index Tables
  • 原文地址:https://www.cnblogs.com/beiluowuzheng/p/9757169.html
Copyright © 2011-2022 走看看