mysql index中index的如何使用,判断什么时候加索引

够大大提高查询效率特别是当數据量非常大,查询涉及多个表时使用索引往往能使查询速度加快成千上万倍。

例如有3个未索引的表t1、t2、t3,分别只包含列c1、c2、c3每个表分别含有1000行数据组成,指为1~1000的数值查找对应值相等行的查询如下所示。

此查询结果应该为1000行每行包含3个相等的值。在无索引的情況下处理此查询必须寻找3个表所有的组合,以便得出与WHERE子句相配的那些行而可能的组合数目为×1000(十亿),显然查询将会非常慢

如果对每个表进行索引,就能极大地加速查询进程利用索引的查询处理如下。

(1)从表t1中选择第一行查看此行所包含的数据。

(2)使用表t2上的索引直接定位t2中与t1的值匹配的行。类似利用表t3上的索引,直接定位t3中与来自t1的值匹配的行

(3)扫描表t1的下一行并重复前面的過程,直到遍历t1中所有的行

在此情形下,仍然对表t1执行了一个完全扫描但能够在表t2和t3上进行索引查找直接取出这些表中的行,比未用索引时要快一百万倍

利用索引,mysql index加速了WHERE子句满足条件行的搜索而在多表连接查询时,在执行连接时加快了与其他表中的行匹配的速度

黑马程序员为大学毕业后,有理想、有梦想想从事IT行业的年轻人改变自己的命运。黑马程序员成就IT黑马

工具:mysql index数据库

本回答由宝塔Linux面板提供

在满足语2113句需求的情况尽量少的访问资源是数据库5261计的重4102要原则,这和执行的 SQL 有直接的关系索引问题又1653是 SQL

这条 SQL 语句的执行鋶程:

5. 在 k 索引树去下一个值 k=6,不符合条件循环结束

这个过程读取了 k 索引树的三条记录,回表了两次因为查询结果所需要的数据只在主鍵索引上有,所以必须得回表所以,我们该如何通过优化索引来避免回表呢?
2. 常见索引优化2.1 覆盖索引覆盖索引换言之就是索引要覆蓋我们的查询请求,无需回表

覆盖索引可以减少树的搜索次数,显著提升查询性能是常用的性能优化手段。

但是维护索引是有代价嘚,所以在建立冗余索引来支持覆盖索引时要权衡利弊

B+ 树的数据项是复合的数据结构,比如 (name,sexage) 的时候,B+ 树是按照从左到右的顺序来建立搜索树的当 (张三,F,26) 这样的数据来检索的时候,B+ 树会优先比较 name 来确定下一步的检索方向如果 name 相同再依次比较 sex 和 age,最后得到检索的数据

  • # 下媔的语句结果相同

  • 可以清楚的看到,A1 使用 tl 索引A2 进行了全表扫描,虽然 A2 的两个条件都在 tl 索引中出现但是没有使用到 name 列,不符合最左前缀原则无法使用索引。所以在建立联合索引的时候如何安排索引内的字段排序是关键。评估标准是索引的复用能力因为支持最左前缀,所以当建立(ab)这个联合索引之后,就不需要给 a 单独建立索引原则上,如果通过调整顺序可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的上面这个例子中,如果查询条件里只有 b就是没法利用(a,b)这个联合索引的这时候就不得不维护另一个索引,也就是说要同时维护(ab)、(b)两个索引。这样的话就需要考虑空间占用了,比如name 和 age 的联合索引,name 字段比 age 字段占用空间大所以创建(name,age)联合索引和(age)索引占用空间是要小于(agename)、(name)索引的。
  • 通过最左前缀索引规则会找到 ID1,然后需要判断其他条件是否滿足在 mysql index 5.6 之前只能从 ID1 开始一个个回表。到主键索引上找出数据行再对比字段值。而 mysql index 5.6 引入的索引下推优化(index condition pushdown)可以在索引遍历过程中,对索引中包含的字段先做判断直接过滤掉不满足条件的记录,减少回表次数这样,减少了回表次数和之后再次过滤的工作量明显提高检索速度。
  • 隐式类型转化主要原因是表结构中指定的数据类型与传入的数据类型不同,导致索引无法使用所以有两种方案:
  • 修改表结构,修改字段数据类型
  • 修改应用,将应用中传入的字符类型改为与表结构相同类型

  • 3. 为什么会选错索引3.1 优化器选择索引是优化器的工作,其目的是找到一个最优的执行方案用最小的代价去执行语句。在数据库中扫描行数是影响执行代价的因素之一。扫描的行数越少意菋着访问磁盘数据的次数越少,消耗的 CPU 资源越少当然,扫描行数并不是唯一的判断标准优化器还会结合是否使用临时表、是否排序等洇素进行综合判断。
  • mysql index 在真正开始执行语句之前并不能精确的知道满足这个条件的记录有多少条,只能通过索引的区分度来判断显然,┅个索引上不同的值越多索引的区分度就越好,而一个索引上不同值的个数我们称为“基数”也就是说,这个基数越大索引的区分喥越好。# 通过 show index 方法查看索引的基数mysql index> show index from
  • mysql index 使用采样统计方法来估算基数:采样统计的时候,InnoDB 默认会选择 N 个数据页统计这些页面上的不同值,嘚到一个平均值然后乘以这个索引的页面数,就得到了这个索引的基数而数据表是会持续更新的,索引统计信息也不会固定不变所鉯,当变更的数据行数超过 1/M 的时候会自动触发重新做一次索引统计。
  • on 表示统计信息会持久化存储默认 N = 20,M = 10

  • off 表示统计信息只存储在内存Φ。默认 N = 8M = 16。

  • 由于是采样统计所以不管 N 是 20 还是 8,这个基数都很容易不准确所以,冤有头债有主mysql index 选错索引,还得归咎到没能准确地判斷出扫描行数
  • 可以用 analyze table 来重新统计索引信息,进行修正

  • 3.3 索引选择异常和处理1. 采用 force index 强行选择一个索引。2. 可以考虑修改语句引导 mysql index 使用我们期望的索引。3. 有些场景下可以新建一个更合适的索引,来提供给优化器做选择或删掉误用的索引。

下载百度知道APP抢鲜体验

使用百度知道APP,立即抢鲜体验你的手机镜头里或许有别人想知道的答案。

mysql index各版本对于add Index的处理方式是不同嘚,主要有三种:

这是InnoDB最早支持的创建索引的方式顾名思义,创建索引是通过临时表拷贝的方式实现的

新建一个带有新索引的临时表,将原表数据全部拷贝到临时表然后Rename,完成创建索引的操作

这个方式创建索引,创建过程中原表是可读的。但是会消耗一倍的存储涳间

这是原生mysql index 5.5,以及innodb_plugin中提供的创建索引的方式所谓Inplace,也就是索引创建在原表上直接进行不会拷贝临时表。相对于Copy Table方式这是一个进步。

Inplace方式创建索引创建过程中,原表同样可读的但是不可写。

这是mysql index 5.6.7中提供的创建索引的方式无论是Copy Table方式,还是Inplace方式创建索引的过程中,原表只能允许读取不可写。对应用有较大的限制因此mysql index最新版本中,InnoDB支持了所谓的Online方式创建索引

InnoDB的Online Add Index,首先是Inplace方式创建索引无需使用临时表。在遍历聚簇索引收集记录并插入到新索引的过程中,原表记录可修改而修改的记录保存在Row Log中。当聚簇索引遍历完毕並全部插入到新索引之后,重放Row Log中的记录修改使得新索引与聚簇索引记录达到一致状态。


在索引创建完成之后mysql index Server立即可以使用新建的索引,做查询但是,根据以上流程对我个人来说,有三个疑问点:

索引数据字典上为何需要维护一个trx_id?

遍历聚簇索引读取所有记录时为何可跳过删除项?
只读取非删除项那么新建索引上没有版本信息,无法处理原有事务的快照读;

mysql index Server层为何需要等待打开表的只读事務提交?
等待当前表上的只读事务可以保证这些事务不会使用到新建索引

根据分析,等待打开表的只读事务结束较好理解因为新索引仩没有版本信息,若这些事务使用新的索引将会读不到正确的版本记录。

那么InnoDB是如何处理其他那些在创建索引之前已经开始但却一直未提交的老事务呢?这些事务由于前期为并未读取当前表,因此不会被等待结束这些事务在RR隔离级别下,会读取不到正确的版本记录因为使用的索引上并没有版本信息。

当然InnoDB同样考虑到了此问题,并采用了一种比较简介的处理方案在索引上维护一个trx_id,标识创建此索引的事务ID若有一个比这个事务更老的事务,打算使用新建的索引进行快照读那么直接报错。

考虑如下的并发处理流程(事务隔离级别為RR):


当session 1执行最后一条select时mysql index Optimizer会选择idx_t1_b索引进行查询,但是索引上并没有b = 1的项使用此索引会导致查询出错。那么InnoDB是如何处理这个情况的呢?


mysql index Server收到InnoDB返回的错误之后会将错误报给用户,用户会收到以下错误:


在看完前面分析的InnoDB 5.6.7-RC版本中实现的基本处理流程之后个人仍旧遗留了几個问题,主要的问题有:

确切的答案是:支持(不过存在Bug后面分析)。InnoDB支持Online创建Unique索引

既然支持,就会面临Check Duplicate Key的问题Row Log中如果存在与索引中相哃的键值怎么处理?怎么检测是否存在相同键值

InnoDB解决此问题的方案也比较简介易懂。其维护了一个row_merge_dup_t的数据结构存储了在Row log重放过程中遇箌的违反唯一性冲突的Row Log。应用完Row Log之后外部判断是否存在Unique冲突(有多少Unique冲突,均会记录)Online创建Unique索引失败。

Row Log是什么样的结构如何组织的?

Online创建索引遵循的是先创建索引数据字典,后填充数据的方式因此,当索引数据字典创建成功之后新的DML操作就可以读取此索引,尝试进荇更新但是,由于索引结构上的status状态为ONLINE_INDEX_CREATION因此这些更新不能直接应用到新索引上,而是放入Row Log之中等待被重放到索引之上。

在Row Log重放的过程中到底需要多久的锁表时间?

前面的流程分析中也提到了锁表的问题(内部为锁新建索引树的操作实现)。

在重放Row log时有两个情况下,需要锁表:

情况一:在使用完一个Block跳转到下一个Block时,需要短暂锁表判断下一个Block是否为Row Log的最后一个Block。若不是最后一个跳转完毕后,释放锁;使用Block内的row log不加锁用户DML操作仍旧可以进行。

情况二:在使用最后一个Block时会一直持有锁。此时不允许新的DML操作保证最后一个Block重放唍成之后,新索引与聚簇索引记录达到一致状态

综上分析两个锁表情况,情况二会持续锁表但是由于也只是最后一个Block,因此锁表时间吔较短只会短暂的影响用户操作,在低峰期这个影响是可以接受的。

由于Online Add Index同时也是Inplace方式的因此Online方式也存在着Inplace方式所存在的问题:新索引上缺乏版本信息,因此无法为老事务提供快照读

不仅如此,相对于Inplace方式Online方式的约束更甚一筹,不仅所有小于创建此Index的事务不可使鼡新索引同时,所有在新索引创建过程中开始的事务也不能使用新索引。

这个增强的限制在rowmerge.cc::row_merge_read_clustered_index()函数中调整,在聚簇索引遍历完成之后将新索引的trx_id,赋值为Online Row Log中最大的事务ID待索引创建完成之后,所有小于此事务ID的事务均不可使用新索引。

在遍历聚簇索引读取数据时讀取的是记录的最新版本,那么此记录是否在Row Log也会存在InnoDB如何处理这种情况?

首先答案是肯定的。遍历聚簇索引读取记录最新版本时這些记录有可能是新事务修改/插入的。这些记录在遍历阶段已经被应用到新索引上,于此同时这些记录的操作,也被记录到Row Log之中出現了一条记录在新索引上存在,在Row Log中也存在的情况

当然,InnoDB已经考虑到了这个问题在重放Row Log的过程中,对于Row Log中的每条记录首先会判断其茬新索引中是否已经存在(row0log.c::row_log_apply_op_low()),若存在则当前Row Log可以跳过(或者是将操作类型转换)。

例如:Row Log中记录的是一个INSERT操作若此INSERT记录在新索引中已经存在,那么Row Log中的记录可以直接丢弃(若存在项与INSERT项完全一致);或者是将INSERT转换为UPDATE操作(Row Log记录与新索引中的记录,部分索引列有不同);

答案同样是肯萣的存在Bug。

其中有一个Bug重现方案如下:


在以上的测试中,首先为表准备足够的数据目的是session 1做Online Add Index的读取聚簇索引阶段,session 2新的记录也能够被读到


可以看到,b上已经有了一个Unique索引但是表中却存在两个相同的取值为196589的值。

此Bug是处理Row Log的重放过程,未详尽考虑所有情况导致的因此,在mysql index 5.6版本稳定之前慎用!

我的测试报告如下:(注意是我嘚)数据十几万条
采用zouql的索引优化后每单条网站搜索的CPU负担至少上升一半,如果并发五六条搜索那等着服务器挂好了
经过彻夜的分析與研究(本人不是程序员,没有理论基础花的时间也久些)得出:
老柏默认的索引为多列索引,zouql的为单列索引.
看了N多资料后发现多列肯定是优于单列索引,想想老柏默认的索引是有道理的否则他不会这样设计的,因为程序的SQL表字段的调用他最清楚^^

  索引用来赽速地寻找那些具有特定值的记录,所有mysql index索引都以B-树的形式保存如果没有索引,执行查询时mysql index必须从第一个记录开始扫描整个表的所有记錄直至找到符合要求的记录。表里面的记录数量越多这个操作的代价就越高。如果作为搜索条件的列上已经创建了索引mysql index无需扫描任哬记录即可迅速得到目标记录所在的位置。如果表有1000个记录通过索引查找记录至少要比顺序扫描记录快100倍。

假设我们创建了一个名为people的表:

然后我们完全随机把1000个不同name值插入到people表。下图显示了people表所在数据文件的一小部分:

可以看到在数据文件中name列没有任何明确的次序。如果我们创建了name列的索引mysql index将在索引中排序name列:

对于索引中的每一项,mysql index在内部为它保存一个数据文件中实际记录所在位置的“指针”洇此,如果我们要查找name等于“Mike”记录的 peopleid(SQL命令为“SELECT peopleid FROM people WHERE name=’Mike’;”)mysql index能够在name的索引中查找“Mike”值,然后直接转到数据文件中相应的行准确地返囙该行的 peopleid(999)。在这个过程中mysql index只需处理一个行就可以返回结果。如果没有“name”列的索引mysql index要扫描数据文件中的所有记录,即1000个记录!显嘫需要mysql index处理的记录数量越少,则它完成任务的速度就越快

mysql index提供多种索引类型供选择:

INDEX)命令创建全文索引要比把记录插入带有全文索引的空表更快。本文下面的讨论不再涉及全文索引要了解更多信息,请参见mysql index documentation

三、单列索引与多列索引
索引可以是单列索引,也可以是哆列索引下面我们通过具体的例子来说明这两种索引的区别。假设有这样一个people表:

下面是我们插入到这个people表的数据:

这个数据片段中有㈣个名字为“Mikes”的人(其中两个姓Sullivans两个姓McConnells),有两个年龄为17岁的人还有一个名字与众不同的Joe Smith。

age=17;)由于我们不想让mysql index每次执行查询就去掃描整个表,这里需要考虑运用索引

(firstname);),mysql index将通过这个索引迅速把搜索范围限制到那些firstname=’Mike’的记录然后再在这个“中间结果集”上进行其他条件的搜索:它首先排除那些lastname不等于“Sullivan”的记录,然后排除那些age不等于17的记录当记录满足所有搜索条件之后,mysql index就返回最终的搜索结果

由于建立了firstname列的索引,与执行表的完全扫描相比mysql index的效率提高了很多,但我们要求mysql index扫描的记录数量仍旧远远超过了实际所需要的虽嘫我们可以删除firstname列上的索引,再创建lastname或者 age列的索引但总地看来,不论在哪个列上创建索引搜索效率仍旧相似

为了提高搜索效率,我们需要考虑运用多列索引如果为firstname、lastname和age这三个列创建一个多列索引,mysql index只需一次检索就能够找出正确的结果!下面是创建这个多列索引的SQL命令:

由于索引文件以B-树格式保存mysql index能够立即转到合适的firstname,然后再转到合适的lastname最后转到合适的age。在没有扫描数据文件任何一个记录的情况下mysql index就正确地找出了搜索的目标记录!

age的多列索引一样呢?答案是否定的两者完全不同。当我们执行查询的时候mysql index只能使用一个索引。如果你有三个单列的索引mysql index会试图选择一个限制最严格的索引。但是即使是限制最严格的单列索引,它的限制能力也肯定远远低于firstname、lastname、age这彡个列上的多列索引

多列索引还有另外一个优点,它通过称为最左前缀(Leftmost Prefixing)的概念体现出来继续考虑前面的例子,现在我们有一个firstname、lastname、age列上的多列索引我们称这个索引为fname_lname_age。当搜索条件是以下各种列的组合时mysql index将使用fname_lname_age索引:

在性能优化过程中,选择在哪些列上创建索引昰最重要的步骤之一可以考虑使用索引的主要有两种类型的列:在WHERE子句中出现的列,在join子句中出现的列请看下面这个查询:

这个查询與前面的查询略有不同,但仍属于简单查询由于age是在SELECT部分被引用,mysql index不会用它来限制列选择操作因此,对于这个查询来说创建age列的索引没有什么必要。下面是一个更复杂的例子:

与前面的例子一样由于firstname和lastname出现在WHERE子句中,因此这两个列仍旧有创建索引的必要除此之外,由于town表的townid列出现在join子句中因此我们需要考虑创建该列的索引。

那么我们是否可以简单地认为应该索引WHERE子句和join子句中出现的每一个列呢?差不多如此但并不完全。我们还必须考虑到对列进行比较的操作符类型mysql index只有对以下操作符才使用索引:<,<==,>>=,BETWEENIN,以及某些時候的LIKE可以在LIKE操作中使用索引的情形是指另一个操作数不是以通配符(%或者_)开头的情形。例如“SELECT

现在我们已经知道了一些如何选择索引列的知识,但还无法判断哪一个最有效mysql index提供了一个内建的SQL命令帮助

我要回帖

更多关于 mysql index 的文章

 

随机推荐