索引不是银弹

发布时间:2024年01月13日

数据库索引:不是银弹

数据库索引是优化性能的良药,但却不是银弹!!!

近日,一同事突然找到我说,xx,我遇见个奇怪的现象的,增加了好几个索引性能竟然没有变化,奇了怪了。我听完后的第一反应是,我不是应该给你写个索引使用攻略啊?!

使用环境

  • MySQL:5.7.24

  • 引擎:InnoDB

索引分类

分类依据索引名称
按存储分聚簇索引
二级索引
按字段特性分主键索引
普通索引
前缀索引
按字段个数分单列索引
联合索引

Tip:InnoDB会自动使用主键作为表的索引键(如果没有主键,就选择第一个不包含 NULL值的唯一列)。

创建索引的代价

任务事物的背后都是有代价的,索引也不例外。创建额外索引的代价主要表现在:

维护代价

对InnoDB存储引擎来说,创建N个索引,就需要创建对应的N颗B+树,新增数据时不光要修改聚簇索引,还要修改这N个二级索引。

空间存储

二级索引不保存原始数据,但是需要保存索引列的数据,索引会占用多余的数据。

Note:SELECT data_length, index_length FROM information_schema.TABLES WHERE table_name=‘TableName’。

回表代价

二级索引不保存原始数据,通过索引找到主键后需要再查询聚簇索引,才能找到需要的数据。

最佳实践

  1. 一开始无需创建太多的索引(除非你确定数据量比较大),等到需要时,比如性能下降,数据量过万,在对具体的场景进行索引优化。

  2. 创建索引后,最好使用explain命令进行验证和确认语句是否真的使用的创建的索引。

  3. 尽量在比较轻的字段上创建索引,比如能索引int字段,就不要索引varchar字段。

  4. 不要忘记了前缀索引,即针对比较长的字段只对前N个字符创建索引。

  5. 尽量不要在生产环境中使用 select * 语句,最好使用查询具体字段。

  6. 查询字段较少并且数据量特别大时,可以考虑使用全部查询字段的联合索引,这样既能实现索引查询加速,又能避免回表操作。

不是所有针对索引列的查询都能使用索引加速查询

我们回到这位同事是问题上来,我们用一个具体的表来模拟他的情况。

CREATE TABLE `person` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `score` int(11) NOT NULL,
  `createTime` timestamp NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

针对表person创建了一个联合索引:

CREATE INDEX name_score ON person(name,score);

索引只能匹配列的前缀

如下图所以,执行计划的type=ALL代表了全表扫描,Extra的值 Using where 也表示使用where语句
explain结果
如下图所以,执行计划的type=range表示走索引扫描,key=name_score 看到实际走了name_score索引
explain结果

条件涉及函数操作的无法使用索引

如下图所以,由于使用了 LENGTH 函数,语句的执行计划type=ALL代表了全表扫描

explain结果

联合索引只能匹配左边的列

这个场景就是同事遇见的场景,虽然对name和score建了联合索引,但是仅按照score列搜索时,InnoDB是无法使用索引的,执行计划给出了结果:

explain结果

其实造成这个的原因也很简单,联合索引是先按照第一列进行排序,第一列相同的数据才会按照第二列进行排序。想让索引跳过第一列,直接使用第二列人眼看也无法解决,还是得按照第二列排序后才能给出合理的答案。问题明确后,解决方案也很简单了:

  1. 只创建针对score列的索引(同事的问题就是使用此方法解决);

  2. 查询条件中加上name字段(很明显这个需要区分场景,这里查询班级分数>80的人,就无法加上name字段了)。

总结

好了,这里就是关于索引的全部内容了,这个故事告诉我们:**不光要使用技术,还要了解一些技术的本质!**祝你技能满满!

Last updated 2024-01-13 06:54:44 +0800

文章来源:https://blog.csdn.net/chlk118/article/details/135565165
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。