当前位置: 首页 > 后端技术 > PHP

MySQL索引如何工作?10分钟搞清楚!

时间:2023-03-29 22:09:35 PHP

1.前言在MySQL中进行SQL优化时,经常会有一些困惑,MySQL在某些情况下是否可以使用索引。例如:1、MySQL遇到范围查询条件就停止匹配,那么范围条件是什么呢?2、MySQL在LIKE进行模糊匹配时如何使用索引?3、MySQL在什么情况下可以使用索引进行排序?今天,我就用一个模型来一一解答这些问题,让你不再害怕使用MySQL索引。2、知识补充EXPLAIN执行计划中有一个key_len列,表示本次查询中选择的索引的字节长度。通常,我们可以用它来确定在联合索引中选择了多少列。这里key_len大小的计算规则是:一般key_len等于索引列类型的字节长度,比如int类型是4字节,bigint是8字节;如果是字符串类型,需要同时考虑字符集因素,例如:CHAR(30)对于UTF8,key_len至少为90字节;如果列类型定义允许NULL,则其key_len需要增加1个字节;如果列类型是变长类型,比如VARCHAR(TEXTBLOB不允许整列创建索引,如果创建一些索引也视为动态列类型),key_len需要增加2个字节;3.哪些条件下可以使用索引首先非常感谢邓博,给了我很好的启发。通过他的文章_,然后结合自己的理解,做了这张图:乍一看是不是晕了,别着急,我们看图分为三部分:1.IndexKey:MySQL用于确定扫描的数据范围,其实就是MySQL索引中可以使用的部分,体现在KeyLength中。2.IndexFilter:MySQL用来判断哪些数据可以被索引过滤。启用ICP后,索引就可以使用了。3、表过滤:MySQL不能使用索引过滤。回表取行数据后,再到server层进行数据过滤。下面我们详细展开一下。IndexKey用于确定MySQL的一个扫描范围,分为上边界和下边界。MySQL使用=、>=、>来确定下边界(第一个键)。使用最左原则,首先判断where条件中第一个索引键值是否存在。如果存在,判断比较符号。如果是(=,>=之一),则添加下边界,然后继续判断下一个索引键,如果存在且是(>),则将键值添加到下边界,停止匹配下一个索引键;如果不存在,则直接停止下边界匹配。exp:idx_c1_c2_c3(c1,c2,c3)wherec1>=1andc2>2andc3=1-->firstkey(c1,c2)-->c1is'>=',加上下边界继续匹配接下来-->c2为'>',添加下边界定义,停止匹配上边界(lastkey)与下边界(firstkey)相似,先判断是否为(=,<=)之一,如果是,则加limit,继续匹配下一个索引键值,如果是(<),加limit,停止匹配exp:idx_c1_c2_c3(c1,c2,c3)wherec1<=1andc2=2andc3<3-->lastkey(c1,c2,c3)-->c1为'<=',添加上边界定义,继续匹配下-->c2为'=',添加上边界定义,继续匹配下-->c3为'<',加上界定界,停止匹配注意:这里简单记忆一下如果比较符号中包含'=','>='也包含'=',则索引键可以使用,可以继续匹配后面的索引键值;如果没有'=',即'>','<',这两个,后面的索引键值是无法匹配的。同时,上下界不能混用。哪个边界可以使用更多的索引键值就是最终可以使用的索引键值的数量。IndexFilter的字面理解就是可以使用索引来进行过滤。即该字段在索引键值中,但不能用于确定索引键的部分。exp:idex_c1_c2_c3wherec1>=1andc2<=2andc3=1indexkey-->c1indexfilter-->c2c3为什么这里的indexkey只有c1?因为c2是用来判断上边界的,但是上边界的c1没有出现(<=,=),而在下边界,c1是>=,c2也没有出现,所以indexkey只有c1场。c2和c3都出现在索引中,被认为是索引过滤器。TableFilter不能使用索引来完成过滤,所以只能使用tablefilter。此时引擎层会将行数据返回给服务器层,然后服务器层进行表过滤。4、Between和Like的处理那么如果查询中存在between和like,MySQL是如何处理的呢?Betweenwherec1between'a'and'b'等价于wherec1>='a'andc1<='b',所以做相应的替换,然后带入上层模型中确定上下边界Like首先需要确认的是,%不能在最左边,这里c1like'%a'不能使用索引,因为索引匹配需要遵守最左前缀原则wherec1like'a%'实际上是相当于wherec1>='a'andc1<'b'大家可以仔细想想。5、索引排序在数据库中,如果不能使用索引来完成排序,随着过滤数据量的增加,排序的成本也会增加。即使使用了limit,数据库也会选择对结果集进行排序,然后将排序后的limit记录取下来,而mysql针对可以用索引排序的limit进行了优化,这样更能降低成本。确保它使用索引在不扫描和排序完整结果集的情况下执行带有LIMIT的ORDERBY非常重要,因此使用索引对它来说很重要-在这种情况下,索引范围扫描将开始并停止查询执行尽快生成所需的行数。CREATETABLE`t1`(`id`int(11)NOTNULLAUTO_INCREMENT,`c1`int(11)NOTNULLDEFAULT'0',`c2`int(11)NOTNULLDEFAULT'0',`c3`int(11)NOTNULLDEFAULT'0',`c4`int(11)NOTNULLDEFAULT'0',`c5`int(11)NOTNULLDEFAULT'0',PRIMARYKEY(`id`),KEY`idx_c1_c2_c3`(`c1`,`c2`,`c3`))ENGINE=InnoDBAUTO_INCREMENT=8DEFAULTCHARSET=utf8mb4select*fromt1;+----+----+----+----+----+----+|编号|c1|c2|c3|c4|c5|+----+----+----+----+----+----+|1|3|3|2|0|0||2|2|4|5|0|0||3|3|2|4|0|0||4|1|3|2|0|0||5|1|3|3|0|0||6|2|3|5|0|0||7|3|2个|6|0|0|+----+----+----+----+----+----+集合中的7行(0.00秒)从t1中选择c1、c2、c3;+----+----+----+|c1|c2|c3|+----+----+----+|1|3|21|3|3||2|3|5||2|4|5||3|2|4||3|2|6||3|3|2|+----+----+---+7rowsinset(0.00sec)有一张表,c1,c2,c3上面有索引,selectc1,c2,c3fromt1;该查询使用了索引全扫描,所以出现的数据相当于在没有索引的情况下从t1中按c1、c2、c3顺序选择c1、c2、c3的结果;那么,索引的排序规则是什么?c1=3—>c2有序,c3无序c1=3,c2=2—>c3有序c1in(1,2)—>c2无序,c3无序有小规矩,idx_c1_c2_c3,那么如何判断某个字段是有序的吗?c1在索引的前面,一定是有序的,c2在第二个位置,只有当c1唯一确定一个值时,c2才是有序的,如果c1有多个值,那么c2不一定有Preface,在同理c3和6类似。通过这篇文章,大家应该了解了MySQL在大多数情况下是如何使用索引的。.