• Mysql中的索引


    索引是数据库优化中最常用也最重要的手段之一,通过索引可以解决大多数的sql性能问题。在mysql中,索引是在存储引引擎层而不是服务器层实现的,所以,并没有统一的索引标准:不同的存储引擎的索引的工作方式并不一样,也不是所有的存储引擎都支持所有类型的索引。即使多个存储引擎支持同一种类型的索引,其底层的实现也可能不通过。

    一、索引的分类

    InnoDB存储引擎支持以下几种常见的索引:

    • B+树索引:我们通常在讨论索引的时候,如果没有特别指明,通常说的就是B+树索引。MyISAM和InnoDB存储引擎的表默认创建的都是B+树索引。B+树索引中的B不代表二叉树(binary),而是代表平衡树(balanced)。B+树索引并不是一颗二叉树。
    • Hash索引:InnoDB存储引擎是支持hash索引的,并且是自适应的,InnoDB存储引擎会根据表的使用情况自动为表生成创建hash索引,不能人为干预是否在表中创建hash索引
    • 全文索引:InnoDB从Mysql5.6版本开始提供对全文索引的支持。仅限于CHAR、VARCHAR和TEXT列。全文索引只能用于英文,如果出现中文,还是应该使用Lucene等第三方开源框架来处理。

    Mysql官方文档Introduction to InnoDB中图表已经表明了InnoDB存储引擎并不支持Hash索引,但InnoDB内部会使用自适应Hash索引来进行字典的查找,但仅仅是内部使用,并不能手动进行干预。

    二、B+树索引

    B+树是由二叉查找树(BST),再由平衡二叉树(AVL),B树演化而来,这几种树的知识可以参考排序二叉树、平衡二叉树、红黑树、B树、B+树。B+树的操作可以参考B+树的插入和删除操作

    1.为什么数据库索引使用B+树结构,而不选择Hash结构或其它树结构?

    索引为什么不用Hash结构?

    我们可以看一下Mysql参考官方文档中B树索引和Hash索引的对比:8.3.8 Comparison of B-Tree and Hash Indexes,其中Hash索引的特点中已经说的很清楚了:

    • 对于等值查找非常快,但不适合等范围比较操作,如<,between等。
    • 不适合顺序查找。(优化器不能使用Hash索引进行ORDER BY操作)
    • MySQL不能判断两个值间大致有多少行记录(范围优化器会根据这来决定选用哪个索引)
    • 不适合前缀匹配查找

    所以Hash索引不适合作为数据库索引。不适合并不表示数据库不支持,实际上Mysql是支持Hash索引的,其Memory存储引擎就支持Hash索引,前面也提到了InnoDB内部会使用自适应Hash索引来进行字典的查找。

    索引为什么不用其它树型结构?

    普通的二叉树虽然查找时间为O(logN),但极端条件下会退化到O(N)。

    平衡二叉树(AVL树和红黑树)解决了极端条件下查找效率低的问题,但无论如何,二叉的结构决定了树的高度太大,导致IO次数过度,不太适合。

    所以只有平衡的多路查找树才比较适合,其中由B树和B+树的不同特点,决定了B+树才适合作为数据库的索引。

    ①磁盘IO

    一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗,所以索引的组织结构要尽量减少查找过程中磁盘I/O的存取次数。B树的节点既要存索引信息,又要存数据,与B+树相比,在相同数据量下,B树相对需要更多次的IO操作。

    ②查找方式

    数据库需要经常进行范围查找,B+由于其数据都按照顺序保存在叶子节点中,且相互间有指针相连。这样的特点非常适合顺序查找和范围查找。

    2.OLTP和OLAP应用中B+树索引的使用

    应用程序分为两种:OLTP和OLAP。OLTP应用中,查询操作只从数据库中取得一小部分数据,一般在10条记录以内,甚至很多时候只取一条记录,如根据主键获取用户信息,根据订单号取得订单详细信息,这都是典型的OLTP应用。在这种情况下,B+树索引建立后,对该索引的使用应该只是通过该索引取得表中少部分数据。这时建立B+树索引才是有意义的,否则即使建立了,优化器也可能选择不适用索引。

    而对于OLAP应用,则需要访问表中大量的数据,根据这些数据来产生查询的结果,这些查询多是面向分析的查询,目的是为决策者提供支持。如本月每个用户的消费情况,销售额同比、环比增长情况等。这些复杂的查询要涉及到多张表之间的连接操作,因此索引的添加依然是有意义的。但是,如果连接操作使用的Hash Join,那么所以可能又变得不是非常重要。这就需要仔细研究自己的应用了。OLAP应用通常会需要对时间字段进行索引,这是因为大多数统计需要根据时间维度来进行数据的筛选。

    三、聚集索引和辅助索引

    数据库中的B+树索引可以分为聚集索引辅助索引(非聚集索引)无论是聚集的还是辅助的,其内部都是B+树,即高度平衡的,叶子存放着所有的数据。聚集索引与辅助索引不同的是,叶子节点存放的是否是一整行的信息。

    1.聚集索引(clustered index)

    官方文档:14.6.2.1 Clustered and Secondary Indexes

    聚集索引,又称为聚簇索引,它并不是一种单独的索引类型,而是一种数据存储方式。每张InnoDB表都有一个聚集索引,表中的数据行就存储在聚集索引中。通常来讲,聚集索引和主键具有相同的含义,但是并不总是如此。

    • 如果表显式的定义了主键,InnoDB就会将该主键作为聚集索引
    • 如果表没有显式的定义主键,则会使用第一个非NULL的唯一索引作为聚集索引。非空指的是该唯一索引中的所有key都是非空的。
    • 如果没有显式的定义主键,也没有合适的唯一索引,InnoDB就会隐式的创建一个6位的主键自增列,并以该列来创建名为GEN_CLUST_INDEX的聚集索引。该自增列中会记录当前行的行ID,每插入一行记录,对应行的行ID就会自增1,因此行的排列顺序就是行的物理插入顺序。

    聚集索引是如何加速查询速度的

    通过聚集索引来查询时,最终索引到的页中就直接存储了所有的行记录,无需再进行额外的查询操作,所以大大提升了查询效率。当表非常大时,与未使用聚集索引的索引结构相比,则会节省磁盘IO。

    2.辅助索引(非聚集索引,nonclustered index)

    辅助索引,也称为非聚集索引或二级索引,除聚集索引以外的所有索引都称为辅助索引。在InnoDB中,辅助索引中的每个记录都包含了该行的主键列,以及为辅助索引指定的列。InnoDB使用此主键值在聚集索引中搜索行,我们称之为回表

    3.两者的区别

    非聚集索引和聚集索引的区别在于:

    由于聚集索引的叶子节点存放的是完整的行记录信息,而非聚集索引的叶子节点存放的是所要查找的行记录的主键值。所以通过聚集索引可以一次查到需要查找的数据,而通过非聚集索引第一次只能查到记录对应的主键值,需要再次使用主键的值通过聚集索引查找才能到需要的数据。

    聚集索引一张表只能有一个,而非聚集索引一张表可以有多个。

    四、联合索引和覆盖索引

    1.联合索引

    官方文档:8.3.5 Multiple-Column Indexes

    联合索引是在表上的多个列进行索引。比如在如下的表中创建一个联合索引(a,b)

    CREATE TABLE t(
        a INT,
        b INT,
        PRIMARY KEY (a),   #主键索引
        KEY idx_a_b (a,b)  #联合索引
    )ENGINE=INNODB

    联合索引的B+树如下。通过叶子节点可以逻辑上顺序的读出所有数据,(1,1),(1,2),(2,1),(2,4),(3,1),(3,2)

    能够使用联合索引的情况

    #全匹配
    select * from t where a=xxx and b=xxx
    #最左前缀匹配。
    select * from t where a=xxx

    不能使用联合索引的情况

    #不能使用联合索引。叶子节点上b的值为1,2,1,4,1,2,显然不是排序的。
    select * from t where b=xxx

    同理,如果建立(a,b,c)索引,则下面的查询都能用到索引。

    select * from t where a=xxx and b=xxx and c=xxx
    select * from t where a=xxx and b=xxx
    select * from t where a=xxx
    select * from t where b=xxx and c=xxx

    联合索引可对第二个列进行排序处理,减少一次filesort。

    在联合索引(a,b)中,由于a相同的情况下b本来就是排序的,所以下面的查询能够用到(a,b)索引,且不需要额外再进行排序。

    select * from t where a=xxx order by b

    同理,如果建立(a,b,c)索引,下面的查询也能少一次fileSort。

    select * from t where a=xxx and b=xxx order by c
    select * from t where b=xxx order by c
    select * from t where a=xxx order by b

    2.索引的列顺序问题

    联合索引的列顺序非常重要,列的顺序能够决定一个索引的好坏。

    对于如何选择索引的列顺序有一个经验法则:将选择性最高的列放到索引最前列。当然这只是一个建议,场景不同选择也不同,并没有一个放之四海接皆准的法则。 

    当不需要考虑排序和分组时,将选择性最高的列放在前面通常是很好的。这时索引的作用只是用于优化WHERE条件的查找,这种情况下,设计的索引确实能够最快地过滤出需要的行,对于在WHERE字句中只使用了索引部分前缀列的查询来说选择性也更高。

    更具体的例子和详细说明可以参考《高性能mysql》5.3.4 P159多列索引章节

    3.覆盖索引

    要查询的所有列都被包含在一个索引中,称为覆盖索引,此时索引将大大提高查询性能。

    并不是所有类型的索引都可以成为覆盖索引。覆盖索引必须存储索引列的值,而hash索引、全文索引等都不存储索引列的值,所以Mysql只能使用B+树索引做覆盖索引。当发起一个被索引覆盖的查询时,在explain的extra列中可以看到Using index的信息。

    更具体的例子和详细说明可以参考《高性能mysql》5.3.6 P159覆盖索引章节

    4.能够使用索引和索引失效的典型场景

    参考能够使用索引和索引失效的典型场景

    五、更多

    《高性能mysql》第5章

    官方文档:

    8.3 Optimization and Indexes

    面试题:

    1.索引的优点?缺点?

    2.不适合创建索引的场景?

    对于非常小的表,大部分情况下简单的扫描更高效。

    3.索引的原理?

    4.索引为什么使用B+树数据结构,而不选择Hash或者红黑树数据结构?

    5.聚集索引和非聚集索引的区别?

    6.能有效利用索引的情况和索引失效的情况?

    需要注意的是:B+树索引并不能找到一个给定键值的具体行,B+树索引能找到的只是被查找数据行所在的页。然后数据库通过把页读入到内存,再在内存中进行查找,最后得到要查找的数据。

    7.索引中为什么不能有NULL值?

    8.什么是索引覆盖?

  • 相关阅读:
    高并发,执行耗时短的任务,还有低并发,执行耗时长的任务,各自选取什么样的线程池比较合理?为什么?如果业务场景是高并发,且任务耗时长时,有什么解决思路?
    java的重载、覆盖和隐藏的区别
    写出以下代码的输出结果?
    MySQL存储引擎通常有哪3种?各自分别有什么特点?应用场景是哪些?
    oracle的索引有几种?各有何用途?
    Apach Web Server区别于其他应用服务器的主要特点是什么?
    eclipse左边工程列表不见了,怎么调出来
    Java 生成一个记事本
    JS indexOf(),split(),substring(),substr(),Math.ceil(),Math.floor(),Math.round()的内容
    Java 有关于线程
  • 原文地址:https://www.cnblogs.com/rouqinglangzi/p/10765378.html
Copyright © 2020-2023  润新知