案例背景案例分析案例解答MySQLInnoDB索引原理索引类型通过主键(主键索引)查询商品数据的过程通过非主键(辅助索引)查询商品数据的过程优点B+Tree索引的优势B+Tree相对于B-Tree索引结构的优势B+Tree相对于二叉树索引结构的优势B+Tree相对于哈希表存储结构的优势执行计划索引失败的常见情况常见索引优化方法前缀索引优化覆盖指数优化联合指数汇总案例背景假设面试官问你:在电商平台的订单中心系统中,通常需要根据商品类型和订单状态筛选出需要的订单,并根据订单创建的时间。对于下面的SQL,如何通过索引来提高查询效率呢?select*fromorderwherestatus=1orderbycreate_timeasc有些同学会认为只为status创建一个索引就可以了。但是更好的办法是创建status和create_time的组合索引,这样可以避免MySQL数据库中的文件排序。因为查询的时候只能使用status索引,但是如果要对create_time进行排序,就需要使用filesort,也就是在SQL执行计划中,Extra列会出现Usingfilesort。因此,需要利用索引的有序性,在status和create_time列上建立联合索引,让根据status过滤的数据按照create_time排序,避免按文件排序。案例分析通过这个案例,你可以发现“索引知识”的重要性。数据库索引底层使用了哪些数据结构和算法?为什么MySQLInnoDB会选择B+Tree作为默认的索引数据结构?如何通过执行计划查看索引使用详情?哪些情况会导致索引失败?优化索引的常用方法有哪些?...总结一下几点:理解MySQLInnoDB的索引原理;掌握B+Tree相对于其他索引数据结构(如B-Tree、二叉树、Hash表)的优势;掌握MySQL执行计划的方法;掌握导致索引失效的常见情况;掌握实际工作中常用的构建高效索引的技巧(如前缀索引、构建覆盖索引等)。如果你曾经被问过这些问题之一,那么你有必要认真巩固MySQL索引和优化的内容。案例解答MySQLInnoDB的索引原理从数据结构来看,常见的MySQL索引有B+Tree索引、HASH索引、Full-Text索引。MySQL常见的存储引擎InnoDB、MyISAM和Memory支持的索引类型。(后两种存储引擎在实际工作和面试中很少提到,所以只提到了InnoDB)。索引类型在实际应用中,InnoDB是MySQL建表时默认的存储引擎,B+Tree索引类型也是MySQL存储引擎使用最广泛的索引类型。InnoDB存储引擎在创建表时默认使用表的主键作为主键索引。主键索引就是聚集索引(ClusteredIndex)。如果表没有定义主键,InnoDB会自己生成一个隐藏的6字节主键ID值。作为主键索引,创建的主键索引默认使用B+Tree索引。下面我们通过一个简单的例子来说明B+Tree索引在存储数据中的具体实现,目的是让大家理解通过B+Tree进行索引的原理。首先,我们创建一个产品表:CREATETABLE`product`(`id`int(11)NOTNULL,`product_no`varchar(20)DEFAULTNULL,`name`varchar(255)DEFAULTNULL,`price`decimal(10,2)DEFAULTNULL,PRIMARYKEY(`id`)USINGBTREE)CHARACTERSET=utf8COLLATE=utf8_general_ciROW_FORMAT=Dynamic;然后添加几行数据:通过主键(主键索引)查询商品数据的过程此时,当我们使用主键索引查询商品15时,那么根据B+Tree索引的原理,如何找到对应的数据?select*fromproductwhereid=15我们可以通过data手动构造一个B+Tree,每个节点包含3个子节点(B+Tree允许每个节点有M个子节点,且M>2),根节点中的数据值为1,18,36分别是子节点(1,6,12),(18,24,30),(36,41,52)中的最小值。每一层父节点的数据值都会出现在下层子节点的数据值中,所以所有的数据值信息都包含在叶子节点中,每个叶子节点指向下一个叶子节点,形成一个链表。如图:下面举例说明B+Tree的查询过程。比如要查找数据值15,B+Tree会从上到下逐层查找:将15与根节点的数据(1,18,36)进行比较,15在1和18之间,于是根据B+Tree的查找逻辑,找到第二层的数据块(1,6,12);在第二层的数据块(1,6,12)中Search,因为15大于12,所以找到第三层的数据块(12,15,17);在叶子节点的数据块(12,15,17)中查找,找到数据值15;最后根据数据值15找到叶子节点存放的数据。整个过程一共进行了3次I/O操作,所以相对于B-tree和二叉树,B+Tree最大的优势就是查询效率。那么问题来了,如果当前查询数据,不使用主键ID查询商品,查询过程是怎样的呢?通过非主键(辅助索引)查询商品数据的过程如果使用商品编码查询商品(即使用辅助索引查询),首先会在辅助索引中查找B+Tree的商品编码,找到对应的叶子节点,并获取主键值,然后通过主键索引中的B+Tree树查询对应的叶子节点,进而获取整行数据。这个过程被回调到表中。以上就是索引的实现原理。面试的时候,面试官一般不会让你直接描述查询索引的过程,而是会通过考察你对索引优化方法的理解来评价你对索引原理的掌握程度,比如MySQLInnoDB为什么选择B+Tree作为默认索引数据结构?MySQL中优化索引的常用方法有哪些?那么接下来,让我们仔细看看在面试中如何回答索引优化问题。B+Tree索引的优点如果你被问到“为什么MySQL选择B+Tree作为索引数据结构?”其实你考察的是两个方面:B+Tree索引原理;B+Tree索引相对于其他索引类型的优点。刚才讲了B+Tree的索引原理,现在来回答一下B+Tree相对于其他常见的索引结构,比如B-tree、二叉树或者Hash索引结构有什么优势?B+Tree相对于B-tree索引结构的优势B+Tree只在叶子节点存储数据,B-tree的非叶子节点也存储数据,所以B+Tree单个节点的数据量更小,并且同一个磁盘,I/O次数越少,查询到的节点就越多。另外,B+Tree的叶子节点使用双链表连接,适合MySQL中常见的基于范围的顺序查找,而B-tree做不到这一点。B+Tree相对于二叉树索引结构的优势对于一个有N个叶子节点的B+Tree,其搜索复杂度为O(logdN),其中d表示该节点允许的最大子节点数为d。在实际应用中,d值大于100,保证了即使数据达到千万级别,B+Tree的高度依然保持在3~4层左右,也就是说只需要一次数据查询操作待完成3~4次磁盘I/O操作后即可查询到目标数据(这里的查询指的是上面B+Tree的聚簇索引的查询过程)。但是二叉树每个父节点的子节点个数只能是2,也就是说它的查找复杂度是O(logN),远高于B+Tree,所以二叉树检索目标数据。磁盘I/O更高。B+Tree相对于Hash表存储结构的优势我们知道范围查询是MySQL中常见的场景,但是Hash表不适合范围查询,它更适合等值查询,这也是为什么B+Tree索引优于哈希表索引之所以具有更广泛的适用场景。至此,你就知道“为什么MySQL选择B+Tree做索引了”。回答的时候要着重介绍B+Tree的优点,然后介绍索引原理的查询过程(掌握了这些知识点,这道题其实更容易回答)。接下来我们进入下一个问题:实际工作中如何查看索引的执行计划。通过执行计划查看索引使用详情。这是一个存储产品信息的演示表产品:CREATETABLE`product`(`id`int(11)NOTNULL,`product_no`varchar(20)DEFAULTNULL,`name`varchar(255)DEFAULTNULL,`price`decimal(10,2)DEFAULTNULL,PRIMARYKEY(`id`)USINGBTREE,KEY'index_name'('name').KEY'index_id_name'('id','name'))CHARACTERSET=utf8COLLATE=utf8_general_citable包含主键索引,name字段上的公共索引,以及id和name两个字段的联合索引。下面我们来看一个简单查询语句的执行计划:executionplan对于执行计划,参数包括possible_keys字段表示可能使用的索引,key字段表示实际使用的索引,key_len表示长度索引的行数,行数表示扫描的数据行数。其中,需要重点关注type字段,它表示数据扫描的类型,即描述查找所需数据时使用的扫描方式。常见扫描类型的执行效率从低到高的顺序是(考虑查询效率,尽量避免Fulltablescan和fullindexscan):ALL(全表扫描);索引(全索引扫描);范围(索引范围扫描);ref(非唯一索引扫描);eq_ref(唯一索引扫描);const(仅导致一个主键或唯一索引扫描)。总的来说,执行计划是研发工程师分析索引细节的必备技能(很多大公司招京东都写着“SQL语句调优”),所以你也应该知道执行计划的核心参数的含义面试时的执行计划。比如类型。回答的时候也要从关键参数开始,扩展到其他参数,然后说说你是怎么做SQL优化的。索引失效的常见情况在我们的工作中,经常会遇到SQL语句不适用于已有索引的情况。我们来看一个索引失败的例子:这条带有like查询的sql语句没有使用product表索引中的index_name。下面结合普通索引的B+Tree结构来看一下索引失败的原因:当MySQL优化器根据namelike'%这样的条件对索引index_name的B+Tree结构进行查询评估时router',它发现当前节点的左右子节点上的值可能都满足'%router'的条件,所以优化器决定当前索引需要扫描整个索引并返回查询的表,最好直接扫描全表。当然,还有其他类似的索引失效的情况:对索引列进行计算、函数、类型转换等操作。在这些情况下,索引失败是因为查询过程需要扫描整个索引并返回表,这比直接全表扫描代价更大;like匹配使用前缀匹配字符'%abc';不带引号的字符串会导致类型转换;我给你的建议是,如果MySQL查询优化器估计索引遍历的代价大于全表扫描的代价,那么不使用对应的索引,而是直接扫描全表。如果使用索引的代价比全表扫描的代价低,就使用索引。优化索引前缀索引的常用方法优化前缀索引就是利用字符串在某个字段中的前几个字符来建立索引。比如我们可以对订单表的商品名称字段的前5个字符建立索引。使用前缀索引的目的是减小索引字段的大小,可以增加索引页中存储的索引值,有效提高索引的查询速度。当一些大的字符串字段作为索引时,使用前缀索引可以帮助我们减少索引项的大小。但是,前缀索引有一定的局限性。比如orderby不能使用前缀索引,前缀索引不能作为覆盖索引。覆盖索引优化覆盖索引是指SQL中查询的所有字段,那些在索引B+tree的叶子节点上能找到的索引,记录是从辅助索引中查询的,而不是聚集索引查询。假设我们只需要查询商品的名称和价格,有没有办法避免返回表呢?我们可以创建一个组合索引,即商品ID、名称、价格作为一个组合索引。如果数据存在于索引中,则查询不会再次检索主键索引,从而避免回表。因此,使用覆盖索引的好处是显而易见的,即不需要查询包含整行记录的所有信息,减少了大量的I/O操作。联合索引联合索引时有一个最左匹配原则,即按照最左优先的方式进行索引匹配。比如一个联合索引(userpin,username),如果查询条件是WHEREuserpin=1ANDusername=2,就可以匹配到联合索引;或者查询条件为WHEREuserpin=1,也可以匹配到联合索引,但是如果查询条件为WHEREuserpin=2,则不能匹配到联合索引。另外,创建联合索引时的字段顺序对索引效率也有很大的影响。字段被用于索引过滤的概率越高,被用于索引过滤的概率就越高。在实际开发工作中创建联合索引时,应将区分度高的字段排在最前面,这样区分度高的字段才更有可能被更多的SQL使用。到达。区分度是一个字段列的不同值的个数除以表中的总行数。比如性别的区分度很小,不适合做索引或者排在联合索引列的前面,而uuid之类的字段更适合做索引或者排在联合索引列的前面联合索引列。总结主要讲了MySQL的索引原理,介绍了InnoDB为什么采用B+Tree结构。因为B+Tree可以减少单次查询的磁盘访问次数,最大化查询效率。此外,我们还谈到了如何查看SQL执行计划,从而发现索引失效的问题,有针对性地进行索引优化。
