MySQL索引设计

本文详细介绍了MySQL中各种类型的索引,包括InnoDB的B-Tree、聚簇索引、辅助索引、动态哈希索引和覆盖索引。讨论了前缀索引的选择及其优缺点,并提供了创建和维护索引的建议,如如何利用索引进行排序、分组、求distinct、带有limit子句的查询以及连接操作。此外,还提到了避免重复和多余索引的重要性,以及索引对插入和更新的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、索引类型

1.1  InnoDB B-Tree

存储引擎使用了不同的方式把B-Tree索引保存到磁盘上,它们会表现出不同的性能。例如MyISAM使用前缀压缩的方式以减小索引;而InnoDB不会压缩索引。同时MyISAM的B-Tree索引按照行存储的物理位置来引用被索引的行,但是InnoDB按照主键值引用行。这些不同有各自的优点和缺点。 

1.2  InnoDB聚簇索引(cluster index)

聚簇索引不是一种单独的索引类型,而是一种存储数据的方式。当表有聚簇索引的时候,它的数据行实际保存在索引的叶子页。聚簇是指实际的数据行和相关的键值都保存在一起。每个表只能有一个聚簇索引。

由于是存储引擎负责实现索引,并不是所有的存储引擎都支持聚簇索引。当前只有SolidDB和InnoDB是唯一支持聚簇索引的存储引擎。

数据与索引在同一个B-Tree上,一般数据的存储顺序与索引的顺序一致。InnoDB cluster index每个叶子节点包含primary key和行数据,非叶子节点只包括被索引列的索引信息。

聚簇索引的优缺点:

优点:

1.相关的数据保存在一起,利于磁盘存取;

2. 数据访问快,因为聚簇索引把索引和数据一起存放;

3.覆盖索引可以使用叶子节点的primary key的值使查询更快;

缺点:

1.如果访问模式与存储顺序无关,则聚簇索引没有太大的用处;

2.按主键顺序插入和读取最快,但是如果按主键随机插入(特别是字符串)则读写效率降低;

3.更新聚簇索引的代价较大,因为它强制InnoDB把每个更新的行移到新的位置;

4.建立在聚簇索引上的表在插入新行,或者在行的主键被更新,该行必须被移动的时候会进行分页。分页发生在行的键值要求行必须被放到一个已经放满了数据的页的时候,此时存储引擎必须分页才能容纳该行,分页会导致表占用更多的磁盘空间。

5.聚簇表可能会比全表扫描慢,尤其在表存储的比较稀疏或因为分页而没有顺序存储的时候。

6.非聚簇索引可能会比预想的大,因为它们的叶子节点包含了被引用行的主键列。

7.非聚簇索引访问需要两次索引查找,而不是一次。

其它需要说明点:

InnoDB的primary key为cluster index,除此之外,不能通过其他方式指定cluster index,如果InnoDB不指定primary key,InnoDB会找一个unique not null的field做cluster index,如果还没有这样的字段,则InnoDB会建一个非可见的系统默认的主键---row_id(6个字节长)作为cluster_index。

建议使用数字型auto_increment的字段作为cluster index。不推荐用字符串字段做cluster index(primary key) ,因为字符串往往都较长, 会导致secondary index过大(secondary index的叶子节点存储了primary key的值),而且字符串往往是乱序。cluster index乱序插入容易造成插入和查询的效率低下。

1.3  InnoDB辅助索引(secondary index)

InnoDB中非cluster index的所有索引都是secondary index。

secondary index的查询代价变大,需要两次B-Tree查询,一次secondary index,一次cluster index。所以在建立cluster index和secondary index的时候需要考虑到这点。

当secondary index满足covering index(参见3.5.1.4章节介绍)时,只需要一次B-Tree查询并且直接在secondary index便可获取所需数据,不需要再进行数据读取,提高了效率。我们在设计索引和写SQL语句的时候就可以考虑利用到covering index的优势。

建议尽量减少对primary key的更新,因为secondary index叶子节点包含primary key的value (这样避免当row被移动或page split时更新secondaryindex), primary key的变化会导致所有secondary index的更新。

1.4  InnoDB动态哈希(adaptive hash index)

动态哈希索引是InnoDB为了加速B-Tree上的节点查找而保存的hash表 。B-Tree上经常被访问的节点将会被放在动态哈希索引中。

注意点:

MySQL重启后的速度肯定会比重启前慢,因为InnoDB的innodb_buff_pool和adaptive hash index都是内存型的,重启后消失,需要预热(访问一段时间)后性能才能慢慢上来。

1.5  InnoDB覆盖索引(covering index)

索引通常是用于找到行的,但也可以用于找到某个字段的值而不需要读取整个行,因为索引中存储了被索引字段的值,只读索引不读数据,这种情况下的索引就叫做覆盖索引。

覆盖索引是很有力的工具,可以极大地提高性能。它主要的优势如下:

索引记录通常远小于全行大小,因此只读索引,MySQL就能极大的减少数据访问量。这对缓存的负载是非常重要的,它大部分的响应时间都花在拷贝数据上。对于I/O密集型的负载也有帮助。因为索引比数据小很多,能很好的装入内存。

索引是按照索引值来进行排序的,因此I/O密集型范围访问将会比随机地从磁盘提取每行数据要快的多。

覆盖索引对于InnoDB来说非常有用,因为InnoDB的聚集缓存。InnoDB的辅助索引在叶子节点保存了主键值,因此,覆盖了查询的第二索引在主键上避免了另外一次索引查找

1.6  前缀索引
1.6.1前缀索引介绍

在MySQL中,索引只能从字段内容的最左端开始建,查询的时候也只能从索引的最左端开始查, 对字段内容只建从左开始的部分字节的索引,而非全部做索引的这种index就叫做前缀索引(prefix index)。

前缀索引的优缺点:

优点

在索引满足一定的区分度的情况下,索引变得更小,更有利于放入或将更多的索引放入内存,减少I/O操作,提高效率。

缺点

前缀索引不支持covering index和order by。举例说明下:假如表account上有如下索引(balance,customer_email(50),account_number);其中字段customer_email的定义为varchar(100),那如下的两个SQL并不能完全使用该索引。

selectaccount_number

fromaccount

wherebalance=100.1 and customer_email=’1@1.com’;

selectaccount_id

fromaccount

wherebalance=100.1 and customer_email=’1@1.com’ order by account_number;

1.6.2.1前缀索引适合的字段类型

前缀索引一般可以提供高性能所需的选择。如果索引blob和text列,或者很长的varchar列,就必须定义前缀索引,这样既能节约空间同时能得到好的性能。

int型的不建议使用prefix index,虽然可以提升效率,但是却不能使用order by, covering index等, 建议使用更小的数字类型如tinyint,bit等来满足。

1.6.2.2前缀索引的合理长度选择

前缀索引涉及索引到底建多长的选择。短的索引可以节约空间。但是前缀又应该足够长,使他的选择性能够接近索引整个列,因此前缀的基数性应该接近于全列的基数性。

设计索引的时候结合记录数、字符集大小、字段长度、字段内容的重复程度、字符之间的相关性等考虑索引长度,索引长度不当将使索引过于庞大,内存资源利用不高,造成IO较重,程序效率降低。合理的索引长度,可以在满足较好索引区分度的情况下减少索引所占空间,我们的目标就是找到索引空间大小与索引区分度的一个平衡点。

选择索引长度的方法:

1)   首先了解表中记录的总体情况,如果表中数据还不存在或者很少,应该通过了解业务去构造和模拟符合业务和产品特点的数据,使用这些数据来模拟上线后的真实数据。

2)   show table status\G;能看到avg_row_length (每行的平均长度,不准确)、rows(不准确)、data所占空间、已有索引所占空间等信息。

3)   select count(*) from table;查看准确的总体行数。

4)   查看欲建立索引的字段的总体情况

5)   通过select * from t procedureanalyse()\G;能看到表中所有字段的min_value、max_value、min_length、max_length、是否为null、字段平均长度、字段类型优化建议等信息。其中字段长度的相关信息很重要,它给出了字段的大致信息,对索引长度的选择很有帮助,而字段类型优化则是在已有内容基础上给出的类型优化,例如:如果你的表中有1000万行, 字段name为字符串, 但是却只有”a”,”b”,”c”三个值,则会建议优化字段类型为enum(“a”,”b”,”c”),这样查询和索引效率都会大大提高。

6)   查看欲建立字段的最佳索引区分度,select count(distinctcity)/count(*) from city_demo;是该字段全部内容长度都做索引能达到的最理想的区分度,这个首先可以用来衡量该字段是否适合做索引。

7)   看不同索引长度的区分度,这个是个平均值例如:

Select count(distinct left(city,3))/count(*) as sel3,     

Count(distinct left(city, 4))/count(*) assel4,

Count(distinct left(city, 5))/count(*) assel5,       

Count(distinct left(city, 6))/count(*) assel6,       

Count(distinct left(city, 7))/count(*) assel7      

Fromcity_demo;

8)   查看到city字段做3个字节索引、4个字节索引、5个字节索引、6个字节索引、7个字节索引的区分度,可以一直增加索引长度来探测结果。

9)   如果随着索引长度的增加,索引区分度在很明显地增大, 那说明我们应该继续增加索引长度,使当我们增加索引长度时,索引区分度没有明显变化,我们仍然应该继续增加索引长度探测。

10)  那么探测到何时为止呢?当我们发现继续增加很多索引长度但是区分度却没有明显提升而现有区分度接近第3条中的最佳区分度时,这个时候的索引长度可能就比较合理了。

11)  截止上面的步骤,我们找的都是平均分布,有可能出现的是平均区分度很好而少量数据集中出现区分度极差的情况,所以我们还需要查看一下区分度分布是否均匀。

12)  查看区分度是否均匀:

selectcount(*) as cnt,city

fromcity_demo

group bycity 

order bycnt desc limit 100;

 

selectcount(*) as cnt,left(city,3) as pref

fromcity_demo

group bypref

order bycnt desc limit 100;

 

selectcount(*) as cnt,left(city,4) as pref

fromcity_demo

group bypref

order bycnt desc limit 100;

 

selectcount(*) as cnt,left(city,5) as pref

fromcity_demo

group bypref

order bycnt desc limit 100;

13)  索引选择的最终长度应该在平均区分度(前4条)与区分度是否均匀(第5条)之间长度做一个综合的选择。

建完索引后show table status查看索引大小。这是一个收尾且非常重要的工作,我们必须清楚的知道建立这个索引的代价。

二、索引的合理设计和使用

2.1索引的字段及长度

2.1.1  主键和候选键上的索引

在create table语句中,我们可以指定主键和候选键。主键和候选键都有unique约束,这些列不会包含重复值。MySQL自动为主键和每个候选键创建一个唯一索引,以便新值的唯一性可以很快检查,而不必扫描全表。同时加速对于这些列上确定值的查找。主键的索引名为primary,候选键的名为该键包含的第一列的列名。如果存在多个候选键的名字以同一个列名开头,就在该列明后放置一个顺序号码区别。

2.1.1.1 连接列上创建索引

一般会在表的连接列上建立索引,尤其是该表频繁参与连接操作。对于一个比较大的连接操作,如果被驱动表的连接列上没有索引的话,由于MySQL的连接算法是nested loop算法,会造成多次扫描被驱动表,对数据库造成的压力和开销是巨大的。

2.1.1.2 在高选择度的列上创建索引

属性列上的选择度是指该列所包含的不重复的值和数据表中总行数(T)的比值,它的比值在1到1/T之间。选择度越大,越适合建索引。因为对于要查找这个列上的一个值的行,通过索引可以过滤掉大部分数据行,剩下的符合要求的行数较少,可以快速在数据表中定位这些行。相反,如果列的选择度比较小,通过索引过滤后的行数依然很大,和全表扫描的开销没有明显的改善,甚至会更大(全表扫描带来的是顺序I/O,而通过索引过滤后的扫描可能是随机I/O)。因此,在选择索引列时的首要条件就是候选列的选择度。索引要建立在那些选择度高的索引上,在选择度低的列上尽量避免建索引。

2.1.1.3     创建联合索引的选择

在很多时候,where子句中的过滤条件并不是只针对某一个字段,经常会有多个字段一起作为查询过滤条件存在于where子句中。在这时候,就需要判断是该仅仅为过滤性最好的(选择度最大)的列建立索引,还是在所有过滤条件中所有列上建立组合索引。对于这个问题,需要衡量两种方案各自的优劣。当where子句中的这些字段组成的联合索引过滤性远大于其中过滤性最高的单列,就适合建联合索引。这样就意味着where子句中对应的每个列过滤性都不高,但是这些单列的过滤性乘在一起后过滤性就高了。

例如:要从存储着学籍信息的表中查找来自中国,大连的女性学生,使用的SQL的where子句如下:

wherecountry =‘china’and city =‘dalian’and gender = female;

country和city的联合(country,city)的选择性会比country和city各自的选择性高,同时因为gender本身的选择性低,将其加入对于提高总体选择性贡献不大,所以在此情境下适合建立(country,city)的联合索引。

同时从性能角度讲,MySQL使用联合索引比使用index_merge算法来使用各个单列索引的效率要高,性能要好。因此对于经常一起出现在where子句中的过滤条件组合,优先考虑建立这些条件列的联合索引,而不是为每个单列建立索引。

2.1.1.4通过索引列属性的前缀控制索引的长度

索引占用的空间越小,对于MySQL获得高性能越有益。不管是什么类型的索引,在查询中使用都是需要从磁盘中加载到内存中去的,无论是MyISAM对应的key cache,还是InnoDB对应的buffer pool。这些受到程序自身和硬件条件限制都是有大小限制的,如果索引大小比较大的话,会造成这些存放索引的内存区域无法存下整个索引数据,根据LRU算法频繁地淘汰索引,加载新的索引进去,这就造成比较大的I/O开销。

如果要建索引的列是很长的字符串的话,它会使索引变大。如果大小超过限制的话,可以考虑建前缀索引,即只索引数据列中存储的数据的前几个字符,而不是全部的值,这样可以有效地减小索引的大小。当然这样做的前提是保证索引的选择性。在选择列上要索引的字符长度时,考虑选择性不能只看平均值,还要考虑最坏情况下的选择性。因为使用前缀索引而索引的字符数不足的话,容易造成数据分布不均匀。如果这种情况比较极端,可能会造成索引的作用下降。

2.2  如何在操作中利用索引
2.2.1索引与排序操作

MySQL有两种产生排序结果的方式:使用文件排序(filesort),或者使用扫描有序的索引。explain的输出中type列的值为“index”,这说明MySQL会扫描索引。

MySQL能为排序和查找行使用同样的索引。如果可能,按照这样一举两得的方式设计索引是个好主意。按照索引对结果进行排序,只有当order by子句中的顺序和索引最左前缀顺序完全一致,并且所有列排序的方向(升序或降序)一样才可以。order by无需定义索引的最左前缀的一种情况是索引中其它前导列在where子句中为常量。如果查询联接了多个表,只有在order by子句的所有列引用的是第一个表才可以。

2.2.2   索引与分组操作

group by实际上也同样需要进行排序操作,而且与order by相比,group by主要只是多了排序之后的分组操作。当然,如果在分组时还是用了其他一些聚合函数,还需要一些聚合函数的计算。所以在group by的实现过程中,与orderby一样可以使用索引。同时使用索引带来的性能提升是巨大的。

在MySQL中group by使用索引的方式有两种:使用松散(loose)索引扫描;使用紧凑索引扫描。

松散索引扫描实现group by是指MySQL完全利用索引扫描来实现group by时,并不需要扫描所有满足条件的索引键即可完成操作,得出结果。如果MySQL使用了这种方式,则在explain的extra行会出现“using index for group-by”。要利用到松散索引扫描实现group by,需要至少满足以下几个条件:

a)   group by条件列必须处在同一个索引的最左连续位置;

b)   在使用group by同时,只能使用max和min这两个聚合函数;

c)   如果引用到了该索引中group by条之外的列条件,它就必须以常量形式出现。

紧凑索引扫描实现group by是指MySQL需要在扫描索引时,读取所有满足条件的索引键值,然后再根据读取到的数据来完成groupby操作,已得到相应的结果。这时的执行计划中就不会出现“using index for group-by”。使用这种索引扫描方式要满足的条件是:group by条件列必须是索引中的列,同时索引中位于该条件列左边的列必须以常数的形式出现在where子句中。

除了上述两种使用索引扫描来完成group by外,还可以使用临时表加filesort实现。但是这种方式带来的性能开销比较大,一般也比较费时。所以groupby最好实现方式是松散索引扫描,其次是紧凑索引扫描,最后是使用临时表和filesort。

2.2.3   索引与求distinct查询

distinct实际上和group by操作非常相似,只是在group by之后的每组中只取其中一条记录而已。所以,distinct的实现方式和group by也基本相同。同样通过松散索引扫描或者紧凑索引扫描的方式实现要优于使用临时表实现。但在使用临时表时,MySQL仅是使用临时表缓存数据,而不需要进行排序,也就省了filesort操作。

2.2.4索引与带有limit子句的查询

含有limit子句的查询往往同时含有order by子句(如果没有orderby子句则优化方法和普通查询一样)。这样的查询最好在排序时使用索引扫描进行排序。否则即使limit子句中只取排序后起始部分很少的数据都会引起MySQL取出全部符合条件的数据进行排序。如果使用索引扫描的话,则不需要对所有数据排序,只需扫描索引取出满足limit限制的数据即可。

同时对于limit子句中的大偏移量的offset,比如limit 10000,20,它就会产生10020行数据,并且丢掉前10000行。这个操作的代价太大。一个提高效率的简单技巧是在覆盖索引上进行偏移,而不是对全行数据进行偏移。也可以将从覆盖索引上提取出来的数据和全表数据进行连接,然后取得需要的数据。

2.2.4   索引与连接操作

在MySQL中,只有一种连接join算法即nested loop join。该算法就是通过驱动表的结果集作为循环的基础数据,然后将该结果集中的数据作为过滤条件一条条地到下一个表中查询数据,最后合并结果。所以在通过结果集中的数据作为过滤条件到下一个表中地位数据时,最好是通过索引,而不是扫表。因为如果结果集中的数据比较多,要是每次都通过扫描来定位的话,造成的开销和对MySQL的压力是巨大的。因此,最好在被驱动表的连接列上建立索引,并且使MySQL在连接过程中使用索引。

2.2.5   索引与隔离列

如果在查询中没有隔离索引的列,MySQL通常不会使用索引。“隔离”列意味着它不是表达式的一部分,也没有位于函数中。

例如,下面的查询不能使用actor_id上的索引:

selectaccount_id from account where account_id+1=5;

人们能轻易地看出where子句中的actor_id等于4,但是MySQL却不会帮你求解方程。应该主动去简化where子句,把被索引的列单独放在比较运算符的一边。

下面是另外一种常见的问题:

selectexpenditure from consume where to_days(current_date)-to_days(consume_time)<=10;

这个查询将会查找date_col值离今天不超过10的所有行,但是它不会使用索引,因为使用了to_days()函数。下面是一种比较好的方式:

selectexpenditure from consume where consume_time>= date_sub(current_date,interval10 day) ;

这个查询就可以使用索引,但是还可以改进。使用current_date将会阻止查询缓存把结果缓存起来,可以使用常量替换掉current_date的值:

selectexpenditure from consume where consume_time>= date_sub('2010-12-12',interval10 day);

2.3 索引创建的建议
2.3.1对联合索引中包含属性列的选择

对于where子句中过滤条件涉及的属性列大致相同的一系列SQL建立共同的索引。如果共同涉及的属性列是多个的话,则应建立联合索引。在确定联合索引应该包含这些共同涉及的属性列中的哪些时,应该考察这些WHERE子句对于涉及这些列上的过滤条件的形式。对于那些是范围条件对应的列,由于B-Tree索引本身的限制,只能选取其中一个选择度比较高的列进入联合索引。而对于那些等值条件对应的列,原则上都可以进入联合索引,但是需要综合考虑联合索引最后的大小和进入索引的列的选择度。如果属性列的选择度非常低的话,把它放入索引对于联合索引的选择度贡献比较小,但是会增大索引大小,引起其它开销。所以不要把这样的列加入到索引中去。如3.2.1.4节中提到的例子,gender列的选择性较低,加入联合索引对于提高联合索引的选择性没有太大帮助,但却增加了联合索引的大小。

2.3.2   正确创建联合索引中各列的顺序

对于MySQL普遍使用的B-Tree索引,索引列的顺序对于SQL使用该索引至关重要。如果索引中列的顺序不合理,在使用过程中往往会使该索引无法被使用或者通过该索引得到的过滤能力大大减弱。

首先由于B-Tree索引的数据结构限制,只有当SQL的where子句使用索引的最左前缀的时候,索引才能被使用、发挥作用。所以在创建索引、决定索引的顺序时,应提取希望使用该索引SQL的where子句中的过滤条件,提炼出其中的最常出现的条件和其对应的属性列。按照这些列的选择度由高到低排列这些属性列,按照这个顺序创建这个索引。同时相关SQL的where子句中出现的过滤条件顺序,以尽量让这些SQL可以使用建立的索引的最左前缀。

对于联合索引中包含的属性列中,有一列对应在相关SQL的where子句的过滤条件是以范围条件出现,而索引中其他属性列是以等于条件出现,则应该把这些等值条件对应的列放在索引的前面,把范围条件对应的列放在索引的最后。

selectaccount_id from consume where account_payee =72478814 andexpenditure>1.00;

为上述SQL创建对应的联合索引时:如果创建索引(expenditure,account_payee),由于expenditure列上是范围条件,所以索引(expenditure,account_payee)无法使用完全(只能使用索引中的expenditure部分);如创建索引(account_payee,expenditure),SQL则可以完全使用此索引。所以针对上述SQL应该创建联合索引(account_payee, expenditure)。

2.3.3避免重复索引

MySQL允许你在同一列上创建多个索引,它不会注意到你的错误,也不会为错误提供保护。MySQL不得不单独维护每一个索引,并且查询优化器在优化查询的时候会逐个考虑它们,这会严重影响性能。重复索引是类型相同,以同样的顺序在同样的列上创建的索引。应该避免创建重复索引,并且在发现它时把它移除掉。

有时会在不经意间创建重复索引。例如下面的代码:

createtable test(

    idint not null primary key,

    unique(id),

    index(id)

);

对于id列,首先它是primarykey,同时unique(id)使MySQL自动为id创建了名为id的索引,最后index(id),现在给id列创建了三个索引。这通常是不需要的,除非需要为同一列建立不同类型的索引,如B-Tree,fulltext等类型索引。

2.3.4避免多余索引

多余索引和重复索引不同。例如列(A,B)上有索引,那么另外一个索引(A)就是多余的。也就是说(A,B)上的索引能被当成索引(A)(这种多余只适合B-Tree索引)。多余索引通常发生在向表添加索引的时候,例如,有人也许会在(A,B)上添加索引,而不是对索引(A)进行扩展。

对于B-Tree类型索引,有单列索引对应的属性列出现在了某个联合索引的第1位置上,那么这个单列索引可能是多余的。

如果某一索引是主键的超集,那么这个索引除非有特殊理由(如希望使用覆盖索引),否则也是多余索引。因为主键是唯一索引,过滤能力很强,和它建立联合索引意义不大。

在大部分情况下,多余索引都是不好的,为了避免它,应该扩展已有索引,而不是添加新的索引。但是,还有一些情况出于性能考虑需要多余索引。使用多余索引的主要原因是扩展已有索引的时候,它会变得很大。

2.3.4.1使用覆盖索引

索引是找到行的高效方式,但是MySQL也能使用索引来接收数据,这样就可以不用读取行数据。包含所有满足查询需要的数据的索引叫覆盖索引。覆盖索引和任何一种索引都不一样。覆盖索引必须保存它包含的列的数据。MySQL只能使用B-Tree索引来覆盖查询。

在SQL执行中要使用覆盖索引的话,需要相应的索引包含SQL中where子句中涉及的列都在索引中且满足最左前缀,同时SQL的返回列也必须在索引中。同是where子句中的过滤条件中不能包含like等操作符和函数。MySQL使用了覆盖索引,会在explain输出中出现“using index”字样。

有关使用覆盖索引的案例请参见第4章4.2.3.6使用covering index优化select语句。

2.4 索引的维护
2.4.1数据的optimizeanalyze操作

MySQL查询优化器在决定如何使用索引的时候会调用两个API,以了解索引如何分布。第一个调用接受范围结束点并且返回该范围内记录的数量;第二个调用返回不同类型的数据,包括数据基数性(每个键值有多少记录)。当存储引擎没有向优化器提供查询检查的行的精确数量的时候,优化器会使用索引统计来估计行的数量,统计可以通过运行analyze table重新生成。MySQL的优化器基于开销,并且主要的开销指标是查询会访问多少数据。如果统计永远没有产生,或者过时了,优化器就会做出不好的决定。解决方案是运行analyze table。

每个存储引擎实现索引统计的方式不同,由于运行analyze table的开销不同,所以运行它的频率也不一样。

Memory存储引擎根本就不保存索引统计。

MyISAM把索引统计保存在磁盘上,并且analyze table执行完整的索引扫描以计算基数性。整个表都会在这个过程中被锁住。

InnoDB不会把统计信息保存到磁盘上,同时不会时时去统计更新它们,而是在第一次打开表的时候利用采样的方法进行估计。InnoDB上的analyze table命令就使用了采样方法,因此InnoDB统计不够精确,除非让服务器运行很长的时间,否则不要手动更新它们。同样,analyzetable在InnoDB上不是阻塞性的,并且相对不那么昂贵,因此可以在不大影响服务器的情况下在线更新统计。

B-Tree索引能变成碎片,它降低了性能。碎片化的索引可能会以很差或非顺序的方式保存在磁盘上。同是表的数据存储也能变得碎片化。碎片化对于数据的读取,尤其是范围数据的读取,会使读取速度慢很多。为了消除碎片,可以运行optimize table解决。

2.5 索引的其他说明
2.5.1索引对插入、更新的影响和避免

索引是独立于基础数据之外的一部分数据。假设在table t中的column c创建了索引idx_t_c,那么任何更新column c的操作包括插入insert,update,MySQL在更新表中columnc的同时,都必须更新column c上的索引idx_t_c数据,调整因为更新带来键值变化的索引信息。而如果没有对column c建立索引,则仅仅是更新表中column c的信息就可以了。这样因调整索引带来的资源消耗是更新带来的I/O量和调整索引所致的计算量。

基于以上的分析,在更新非常频繁地字段不适合创建索引。很多时候是通过比较同一时间内被更新的次数和利用该列作为条件的查询次数来判断的,如果通过该列的查询并不多,可能几个小时或者更长时间才会执行一次,更新反而比查询更频繁,那么这样的字段肯定不适合创建索引。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值