如何判定是否需要创建索引
在了解了索引的利与弊之后,我们知道了索引并不是越多越好,知道了索引也是会带来副作用的。 那我们到底该如何来判断某个索引是否应该创建呢? 实际上,并没有一个非常明确的定律可以清晰的定义出什么字段应该创建索引什么字段不该创建索引。因为我们的应用场景实在是太复杂,存在太多的差异。当然,我们还是仍然能够找到几点基本的判定策略来帮助我们分析是否需要创建索引。
较频繁的作为查询条件的字段应该创建索引;
提高数据查询检索的效率最有效的办法就是减少需要访问的数据量,从上面所了解到的索引的益处中我们知道了,索引正是我们减少通过索引键字段作为查询条件的 Query 的 IO 量的最有效手段。所以一般来说我们应该为较为频繁的查询条件字段创建索引。
◆ 唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件;
唯一性太差的字段主要是指哪些呢?如状态字段,类型字段等等这些字段中存方的数据可能总共就是那么几个几十个值重复使用,每个值都会存在于成千上万或是更多的记录中。对于这类字段,我们完全没有必要创建单独的索引的。因为即使我们创建了索引,MySQL Query Optimizer 大多数时候也不会去选择使用,如果什么时候 MySQL Query Optimizer 抽了一下风。
选择了这种索引,那么非常遗憾的告诉你,这可能会带来极大的性能问题。由于索引字段中每个值都含有大量的记录,那么存储引擎在根据索引访问数据的时候会带来大量的随机 IO,甚至有些时候可能还会出现大量的重复 IO。 这主要是由于数据基于索引扫描的特点所引起的。当我们通过索引访问表中的数据的时候, MySQL 会按照索引键的键值的顺序来依序进行访问。一般来说每个数据页中大都会存放多条记 录,但是这些记录可能大多数都不会是和你所使用的索引键的键值顺序一致。
假如有以下场景,我们通过索引查找键值为 A 和 B 的某些数据。当我们先通过 A 键值找到第一条满足要求的记录后,我们会读取这条记录所在的 X 数据页,然后我们继续往下查找索引,发现 A 键值所对应的另外一条记录也满足我们的要求,但是这条记录不在 X 数据页上面,而在 Y 数据页上面,这时候存储引擎就会丢弃 X 数据页,而读取 Y 数据页。如此继续一直到查找 、完 A 键值所对应的所有记录。然后轮到 B 键值了,这时候发现正在查找的记录又在 X 数据页 上面,可之前读取的 X 数据页已经被丢弃了,只能再次读取 X 数据页。这时候,实际上已经出现重复读取 X 数据页两次了。在继续往后的查找中,可能还会出现一次又一次的重复读取。这无疑极大的给存储引擎增大了 IO 访问量。
不仅如此,如果一个键值对应了太多的数据记录,也就是说通过该键值会返回占整个表比例很大的记录的时候,由于根据索引扫描产生的都是随机 IO,其效率比进行全表扫描的顺序 IO 的效率要差很多,即使不会出现重复 IO 的读取,同样会造成整体 IO 性能的下降。很多比较有经验的 Query 调优专家经常说,当一条 Query 所返回的数据超过了全表的 15% 的时候,就不应该再使用索引扫描来完成这个 Query 了。对于“15%”这个数字我们并不能判定是否很准确,但是之少侧面证明了唯一性太差的字段并不适合创建索引。
◆ 更新非常频繁的字段不适合创建索引;
上面在索引的弊端中我们已经分析过了,索引中的字段被更新的时候,不仅仅需要更新表中的 数据,同时还要更新索引数据,以确保索引信息是准确的。这个问题所带来的是 IO 访问量的较大 增加,不仅仅影响更新 Query 的响应时间,还会影响整个存储系统的资源消耗,加大整个存储系统 的负载。
当然,并不是存在更新的字段就比适合创建索引,从上面判定策略的用语上面也可以看出,是 “非常频繁”的字段。到底什么样的更新频率应该算是“非常频繁”呢?每秒,每分钟,还是每小 时呢?说实话,这个还真挺难定义的。很多时候还是通过比较同一时间段内被更新的次数和利用该 字段作为条件的查询次数来判断,如果通过该字段的查询并不是很多,可能几个小时或者是更长才 会执行一次,而更新反而比查询更频繁,那这样的字段肯定不适合创建索引。反之,如果我们通过 该字段的查询比较频繁,而且更新并不是特别多,比如查询十几二十次或是更多才可能会产生一次 更新,那我个人觉得更新所带来的附加成本也是可以接受的。
◆ 不会出现在 WHERE 子句中的字段不该创建索引;
不会还有人会问为什么吧?自己也觉得这是废话了,哈哈!