MySQL 索引的分类和优化

这篇具有很好参考价值的文章主要介绍了MySQL 索引的分类和优化。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

文章来源地址https://www.toymoban.com/news/detail-842472.html

优质博文:IT-BLOG-CN

索引是什么 : MySQL 官方对索引的定义:索引(Index)是帮助 MySQL 高效获取数据的数据结构。可以得到索引的本质:索引是数据结构。索引的目的在于提高查询效率。可以简单理解为,排好序的快速查找数据结构。在数据之外,数据系统还维护着满足特定查找算法的数据结构,这些结构已某种特定方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。如下:

MySQL 索引的分类和优化,数据库,mysql,数据库,面试,java,后端,职场和发展,性能优化

为了加快 Col2 的快速查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引的键值对和一个指向对应数据记录的物理地址的指针,这样就可以运用二叉树查找在一定复杂度内获取相应的数据,从而快速检索出符合条件的记录。
■ 一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以文件的形式存储在磁盘上。
■ 我们 Java 程序员平常所说的索引:如果没有特别指明,都指的是B树(多路搜索树,并不一定是二叉树)结构组织索引,其中聚集索引、次要索引、覆盖索引、复合索引、前缀索引、唯一索引默认都是B+树索引,统称索引。初B树外,还有哈希(hash index)索引等。
【2】优势:①、类似大学图书馆建书目录,提高数据检索的效率,降低数据库的 IO 成本。
②、通过索引列对数据进行排序,降低数据排序的成本,降低了 CPU 的消耗。
【3】劣势:①、实际上索引也是一张表,该表保存了主键与索引字段,并指向实体记录,索引索引列也是要占用空间的。
②、虽然索引大大提高了查询速度,同时又降低了更新表(Insert、Update、Delete)的速度。因为更新表的时候还要保存一下索引文件每次更新添加了索引列的字段或者更新所带来的键值变化后的索引信息。
③、索引只是提高效率的一个因素,如果 MySQL 有大量的数据表,就需要花时间研究建立最优秀的索引,或优化查询。(索引的建立并非一时)。

索引有很多种类型,为不同的场景提供更好的性能。在MySQL中,索引是在存储引擎层而不是服务器层实现。不同存储引擎的索引其工作方式并不一样。也不是所有存储引擎都支持所有类型的索引。即使多个存储引擎支持同一种类型的索引,其底层实现也可能不同。

一、B-Tree 索引

我们通过提到索引时,多半说的都是 B-Tree 索引,使用 B-Tree 数据结构来存储数据。大多数 MySQL 引擎都支持这种索引。之所以称之为“B-Tree” 是因为 MySQL 在创建表和其他语句中也使用该关键字。不过,底层的存储引擎也可能使用不同的存储结构,例如:InnoDB 则使用 B+Tree。存储引擎以不同的方式使用 B-Tree 索引,性能也各有不同,各有优劣。例如,MyISAM 使用前缀压缩技术使得索引更小,但 InnoDB 则按照原数据格式进行存储。再如 MyISAM 索引通过数据的物理位置引用被索引的行,而 InnoDB 则根据主键引用被索引的行。
【B-Tree(多路搜索树)】:通常意味着所有的值都是按顺序存储的,并且每一个叶子页到根的距离相同。如下图:展示了 B-Tree 索引的抽象表示,大致反映了 InnoDB 索引是如何工作的。InnoDB 的叶子节点称为叶子页,大小为 16K
MySQL 索引的分类和优化,数据库,mysql,数据库,面试,java,后端,职场和发展,性能优化

B-Tree 索引能够加快访问数据的速度,因为存储引擎不再需要进行全表扫描来获取需要的数据,取而代之的是从索引的根节点开始进行搜索。根节点的槽中存放了指向子节点的指针,存储引擎根据这些指针指向下层查找。通过比较节点页的值和要查找的值可以找到合适的指针进入下层子节点,这些指针实际上定义了子节点页中值的上限和下限。最终存储引擎要么是找到对应的值,要么该记录不存在。
叶子节点比较特别,它们的指针指向的是被索引的数据,而不是其他的节点页(不同引擎的“指针”类型不同)。如下图,绘制了一个节点和其对应的叶子节点,其实在跟节点和叶子节点之间可能有很多节点页,树的深度和表的大小直接相关。B-Tree 对索引列是顺序组织存储的,所以很适合查找范围数据。例如下图,基于文本域的索引树上,按字母顺序传递连续的值进行查找是非常合适的,所以像“找出所有以A到C开头的名字”这样的查询效率会非常高。假设数据表信息如下:

CREATE TABLE People(
    last_name VARCHAR(50) NOT NULL,
    first_name VARCHAR(50) NOT NULL,
    birthday DATE NOT NULL,
    gender ENUM('m','f') NOT NULL,
    KEY(last_name,first_name,birthday)
);

对于表中的每一行数据,索引中包含 last_name,first_name 和 birthday列的值,如下图表示索引是如何组织数据的存储的。索引对多个值进行排序的依据是 CREATE TABLE 语句中定义索引时列的顺序,看一下最后两个条目,两个人的姓和名都相同时,则根据他们的出生日期来排列顺序。

MySQL 索引的分类和优化,数据库,mysql,数据库,面试,java,后端,职场和发展,性能优化

可以使用 B-Tree 索引的查询类型。B-Tree 索引使用于全键值、范围键值或键前缀查找(值where条件)。其中键前缀查找只适用于根据最左前缀的查找。前面所述的索引对如下类型的查询有效:
【1】全值匹配: 和索引中的所有列进行匹配,例如前面提到的索引可用于查找姓名为 Cuba Allen、出生于 1960-01-01 的人。
【2】匹配最左前缀: 前面提到的索引可用于查找所有姓为 Allen 的人,即只使用索引的第一列。
【3】匹配列前缀: 也可以只匹配某一列的值的开头部分。例如前面提到的索引可用于查找所有以 A 开头姓的人。这里也只使用了索引的第一列。模糊查询以常量开头,那么可以使用上索引。
【4】匹配范围值: 例如前面提到的索引可用于查找姓在 Allen 和 Barrymore 之间的人。这里也只使用了索引的第一列。
【5】精准匹配某一列并范围匹配另外一列: 前面提到的索引也可用于查找姓为 Allen,并且名字是字母 K 开头的人。即第一列 last_name 全匹配,第二列 first_name 范围匹配。
【6】只访问索引的查询: B_Tree 通常可以支持 “只访问索引的查询”,即查询只需要访问索引,而无需访问数据行。称之为“覆盖索引” 的优化。

MySQL 索引的分类和优化,数据库,mysql,数据库,面试,java,后端,职场和发展,性能优化

所以,索引列的顺序是很重要的,上面的限制都和索引列的顺序有关。在优化性能的时候,可能需要使用相同的列但顺序不同的索引来满足不同类型的查询需求。也有些限制并不是 B-Tree 本身导致的,而是 MySQL 优化器和存储引擎使用索引的方式导致的。这部分限制在未来的版本中可能就不再是限制了。

二、哈希索引

哈希索引(hash index)是基于哈希表实现的,只有精确匹配索引所有列的查询才有效。对于每一行数据,存储引擎都会对所有的索引列计算一个哈希码(hash code),哈希码是一个较小的值,并且不同键值的行计算出来的哈希码也是不一样。哈希索引将所有的哈希码存储在索引中,同时在哈希表中保存指向每个数据行的指针。
【MySQL 中】:只有 Memory 引擎显示支持哈希索引。这也是 Memory 引擎表的默认索引类型,Memory 引擎同时也支持 B-Tree 索引。值得一提的是,Memory 引擎是支持非唯一哈希索引的,这在数据库世界里面是比较与众不同的。如果多个列的哈希值相同,索引会以链表的方式存放多个记录指针到同一个哈希条目中。

CREATE TABLE People(
    last_name VARCHAR(50) NOT NULL,
    first_name VARCHAR(50) NOT NULL,
    KEY USING HASH(last_name)
)ENGINE=MEMORY;

表中包含的数据如下:
MySQL 索引的分类和优化,数据库,mysql,数据库,面试,java,后端,职场和发展,性能优化

f('Allen')= 1223
f('Peter')= 8493
f('Baron')= 3490

则哈希索引的数据结构如下:索引(hash值,指针),每个槽的编号是顺序的,但是数据行不是。

槽(Slot) 值(Value)
1223 指向第1行的指针
3490 指向第3行的指针
8493 指向第2行的指针

【举个栗子】: 进行如下查询:

SELECT last_name FROM People WHERE last_name='Peter';

MySQL 先计算 ‘Peter’ 的哈希值,并使用该值寻找对应的记录指针。因为 f(‘Peter’)=8493,所以对 MySQL 在索引中查找 8493,可以找到指向第二行的指针,最后一步是比较第二行的值是否为’Peter’,以确保就是要查找的行。因为索引自身只需存储对应的哈希值,所以索引的结构十分紧凑,这也让哈希索引查找的速度非常快。然而,哈希索引也有它的限制:
【1】哈希索引只包含哈希值和指针,而不存储字段值,所以不能使用索引中的值来避免读取行。不过,访问内存中的行的速度很快,所以大部分情况下这一点对性能的影响并不明显。
【2】哈希索引数据并不是按照索引值顺序存储的,所以也就无法用于排序。
【3】哈希索引也不支持部分索引列匹配查找,因为哈希索引始终是使用索引列的全部内容来计算哈希值的。例如,在数据列(A,B)上建立索引,如果查询只使用A,则无法使用该索引。是不遵循最左前缀的思想。
【4】哈希索引只支持等值查询,也不支持任何范围查询。
【5】访问哈希索引的数据非常快,除非有很多哈希冲突。当出现哈希冲突的时候,存储引擎必须遍历链表中所有的行指针,逐行进行比较,直到找到所有符合条件的行。
【6】如果哈希冲突很多的话,一些索引维护操作的代价也会很高。

因为上述限制,哈希索引只适用于某些特定的场合。而一旦适合哈希索引,则它的性能会将非常显著。除了 Memory 引擎外,NDB 集群引擎也支持唯一哈希索引。InnoDB 引擎有一个特殊的功能叫做“自适应哈希索引(adaptive hash index)” 当InnoDB 注意到某些索引值被使用得非常频繁时,它会在内存中基于 B-Tree 索引之上再创建一个哈希索引,这样就使 B-Tree 索引也具有哈希索引的一些优点,比如快速的哈希查找。这是一个完全自动化的、内部的行为,用户无法控制或者配置,不过该功能可以关闭。

【创建自定义哈希索引】: 如果存储引擎不支持哈希索引,则可以模拟像 InnoDB 一样创建哈希索引。思路很简单:在 B-Tree 基础上创建一个伪哈希索引,这和真正的哈希索引不是一回事,因为还是使用 B-Tree 进行查找,但是使用 Hash值进行查找而非键值本身。只需要在 WHERE 子句中手动指定使用哈希函数。

【举个栗子】: 例如表中存储了大量的 URL,并需要根据URL 进行搜索查询。如果使用 B-Tree 来存储 URL,存储的内容就会很大,因为 URL本身就很长。若在原有的表中,新增一个被索引的 url_crc列(使用CRC32 对 URL 进行哈希)。使用 CRC32 做哈希就可以使用如下方式查询:性能会提升很多,因为 MySQL 优化器会使用选择性高而体积小的 url_crc 列的索引来查询。

SELECT id FROM url WHERE url="http://www.mysql.com" AND url_crc=CRC32("http://www.mysql.com");

上述的缺点是需要维护哈希值。可以手动维护,也可以使用触发器实现。如下:先临时修改一下语句分隔符,这样就可以在触发器定义中使用分号;

DELIMITER //
CREATE OR REPLACE TRIGGER People_insert_trigger BEFORE INSERT ON  People FOR EACH ROW BEGIN
SET NEW.url_cc=CRC32(NEW.url);
END;
//
DELIMITER ;

记住不要使用 SHA1() 和 MD5() 作为哈希函数。因为这两个函数计算出来的哈希值是非常长的字符串,会浪费大量空间,比较时也会更慢。SHA1() 和 MD5() 是强加密函数,设计目的是最大限制消费冲突,但这里并不需要这么高的要求,简单哈希函数的冲突在一个可以接受的范围,同时又能够提供更好的性能。如果数据表非常大,CRC32() 会出现大量的哈希冲突,则可以考虑自己实现一个简单的 64位哈希函数。这个自定义函数要返回整数,而不是字符串。一个简单的办法可以使用 MD5() 函数返回值的一部分作为自定义哈希函数。这可能比自己写一个哈希算法的性能要差。

【处理哈希冲突】: 当使用哈希索引进行查询的时候,必须在 WHERE 子句中包含常量值。CRC32() 返回的是32位的整数,当索引有93,000 条记录时出现冲突的概率是 1%。所以,避免哈希冲突的办法就是在 WHERE 条件中带入列值。或者使用 FNV64()函数,这是移植 Percona Server 的函数,可以以插件的方式在任何 MySQL版本中使用,哈希值为 64位,速度快,且冲突比CRC32() 要少很多。

SELECT id FROM url WHERE url="http://www.mysql.com" AND url_crc=CRC32("http://www.mysql.com");

三、空间数据索引(R-Tree)

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

四、全文索引

全文索引是一种特殊类型的索引,他查找的是文本中的关键词,而不是直接比较索引中的值。全文搜索和其他几类索引的匹配方式完全不一样。他有许多需要注意的细节,如停用词、词干和复词、布尔搜索等。全文索引更类似 solr这种搜索引擎,而不是简单的 WHERE 条件匹配。同时在列上创建全文索引和基于值的 B-Tree 索引不会有冲突,全文索引适用于 MATCH AGAINST 操作,而不是普通的 WHERE 条件操作。

CREATE TABLE articles (

     id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
     title VARCHAR(200),
     body TEXT,
     FULLTEXT (title,body)

) ENGINE=InnoDB;

【全文索引的使用】: 通过在 title和body 两个字段中查找含有 ‘database’ 内容的行。

SELECT * FROM articles
WHERE MATCH (title,body)
AGAINST ('database' IN NATURAL LANGUAGE MODE);

五、其他索引类型

还有第三方的存储引擎使用不同类型的数据结构来存储索引。例如 TokuDB 使用分型树索引(fractal tree index),这是一类较新开发的数据结构,既有 B-Tree 的很多优点,也避免了 B-Tree 的一些缺点。

六、优化

问题:性能下降, SQL 慢,执行时间长,等待时间长
■ 表结构设计不当。
■ 查询语句写的烂。
■ 索引失效(①、单值索引②、复合索引)
■ 关联查询太多 join(设计缺陷或者不得以的需求)
■ 服务器调优或者各个参数的设置(缓冲、线程数等)

哪些情况下需要创建索引:
【1】主键会自动创建唯一索引。
【2】频繁作为查询条件的字段。
【3】查询中与其他表关联的字段,外键关系建立索引。
【4】频繁修改的字段不建议创建索引。
【5】where 条件用不到的字段不要创建索引。
【6】单例索引与复合建议选择复合索引。
【7】查询的字段若通过索引的顺序去访问将大大提高排序速度。
【8】查询中统计和分组字段。

什么情况下不建议创建索引:
【1】表记录太少
【2】经常增删改的表
【3】数据重复且分布平均的字段。

索引分析: ①、单表:有范围时,后边的索引失效。②、双表:左连接为右表建索引。③、三表:参考②。结论:
● 永远用小的结果集驱动大的结果集。
● 优先优化NestedLoop内层循环。
● 保证 Join 语句中被驱动表上 Join 条件字段已被索引。
● 当无法保证上述 join 字段时,当内存允许的条件下,不要太吝啬 joinBuffer 字段的设置。

索引失效(应该避免):
■ 全值匹配我最爱
■ 最佳左前缀法则
■ 不在索引列上做任何操作(计算、函数、类型转换),会导致索引失效而转向全表扫描。
■ 存储引擎不能使用索引中范围条件右边的列。
■ 尽量使用覆盖索引(只访问索引的查询),减少select *。
■ MySQL在使用不等于(!=或<>)的时候无法使用索引会导致全表扫描。
■ is null,is not null 也无法使用索引。
■ like 已通配符开头,MySQL 索引会失效会变成全表扫描。(%右边的是会进行rang索引的同是不同于>它会后面的索引不会失效。同时当使用左%时,想使用索引直接从索引缓存中查询即覆盖索引)
■ 字符串不添加单引号索引会失效。
■ 少用 or,用它来连接时会索引失效。
【总结】: 定值、范围还是排序,一般 order by 是给个范围。group by 基本上都是需要排序,会有临时表产生(如果错乱时)

一般性建议: 在 where 查询条件中条件不安索引的顺序排列查找和顺序排列查找的效果相同,原因是:MySQL 自身会对 sql 进行优化。(都是常量的提前)
1)、对于单值索引,尽量选择针对当前 query 过滤性更好的索引。
2)、在选择组合索引的时候,当前 query 中过滤性最好的字段在索引字段顺序中,位置越靠左越好。
3)、在选择组合索引的时候,尽量选择可以包含当前 query 中的 where 子句中更多字段的索引。
4)、尽可能通过分析统计信息和调整 query 的写法来达到选择合适索引的目的。

到了这里,关于MySQL 索引的分类和优化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • MySQL数据库:索引

            索引是一种特殊的文件,包含着对数据表里所有记录的引用指针。可以对表中的一列或多列创建索引,并指定索引的类型,各类索引有各自的数据结构实现。         相当于是给数据库中的数据建立了一个目录,通过目录可以知道数据所在位置,然后到指定位置

    2023年04月17日
    浏览(80)
  • 【MySql】数据库索引

    可以简单理解为一本书的目录信息,是为了提升查找效率而建立的 1、在创建一个主键、唯一键、外键时候,数据库会自动地针对查找字段设置索引; 2、在创建表时侯,使用 index 进行普通索引的声明 3、修改表结构,给指定的字段添加索引 alter table 表名 add index 索引名

    2024年02月03日
    浏览(65)
  • MySQL数据库索引机制

    MySQL是一款有客户端和服务端的网络应用,mysql是它的客户端,mysqld是它的服务端。服务端本质就是一个进程,它存在于内存当中。而我们存储在MySQL中的数据是保存在磁盘上的,当我们对MySQL中数据进行增删查改操作时,不可能是直接在磁盘上进行操作,而是将对应的数据加

    2024年02月12日
    浏览(78)
  • MySQL数据库唯一索引

    创建索引是指在某个表的一列或多列上建立一个索引,以便提高对表的访问速度。创建索引有3种方式,分别是1.创建表的时候创建索引、2.在已经存在的表上创建索引和使用3.ALTER TABLE语句来创建索引。 本文福利, 莬 费领取Qt开发学习资料包、技术视频,内容包括(C++语言基

    2024年02月08日
    浏览(67)
  • 简单认识MySQL数据库索引

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 ●索引是一个排序的列表,在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址(类似于C语言的链表通过指针指向数据记录的内存地址)。 ●使用索引后可以不用扫描全表来定位某行的

    2024年02月16日
    浏览(69)
  • MySQL数据库索引的数据结构

    数据库索引的功能就是让查找更加的高效,所以索引的数据结构应该是能够加速查找的数据结构。 MySQL的innoDB存储引擎的索引的数据结构就是多叉搜索树中的b+树,这可以说是为索引量身定做的一个数据结构。 首先,索引可以通过主键,unique修饰创建,也可以直接使用sql语句

    2024年02月10日
    浏览(59)
  • 【MySql系列】深入解析数据库索引

    MySQL索引是数据库中一个关键的概念,它可以极大地提高查询性能,加快数据检索速度。但是,要充分发挥索引的作用,需要深入理解它们的工作原理和使用方式。 在本文中,我们将深入解析MySQL索引,探讨它们的重要性、类型、创建、维护以及最佳实践。 在数据库中,索引

    2024年02月08日
    浏览(76)
  • 【MySQL数据库 | 第十七篇】索引以及索引结构介绍

    目录 前言: 索引简介:  索引结构:           二叉树索引结构         Tree(普通二叉树)         B-Tree(多路平衡查找树)         B+Tree          哈希索引数据结构 总结: 在实际生活中,我们对SQL语句进行优化实际上有很大一部分都是对索引进行优化,因此对索引

    2024年02月09日
    浏览(76)
  • 【Mysql系列】——详细剖析数据库“索引”【上篇】

        😎博客昵称:博客小梦 😊最喜欢的座右铭:全神贯注的上吧!!! 😊作者简介:一名热爱C/C++,算法,数据库等技术、喜爱运动、热爱K歌、敢于追梦的小博主! 😘博主小留言:哈喽! 😄各位CSDN的uu们,我是你的博客好友小梦,希望我的文章可以给您带来一定的帮

    2024年02月02日
    浏览(57)
  • MySQL数据库索引的种类、创建、删除

    目录 一:MySQL 索引 1、MySQL 索引介绍 2、 索引的作用  3、索引的副作用 4、 创建索引的原则依据  二、索引的分类和创建 1、 普通索引 (1) 直接创建索引 (2) 修改表方式创建 (3) 创建表的时候指定索引 2、 唯一索引 (1) 直接创建唯一索引 (2) 修改表方式创建

    2024年02月09日
    浏览(212)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包