一张表到底建多少个索引才是合适呢?

上周的一天,到公司接了杯水刚刚坐稳,就看到 DBA就在群里@ 某个研发帅哥,说"你的表已经有10个索引了,怎么这次还要再加呢?"

关于索引,是数据库后台用来加快查询速度的强大工具,索引通过提供快速查找所需数据的方法来增强查询能力。

但我们经常听到这样一句话:"索引是把双刃剑"。

说明索引并不是越多越好,索引可以提高查询的效率,但会降低写数据的效率,有时不恰当的索引还会降低查询的效率。

那我就在想:一张表到底建多少个索引才是合适呢?

要搞懂这个问题,我们就需要弄清楚以下这几个问题:

1)常见的索引分类有哪些?

2)MySQL 是如何使用索引的?

3)一张表最多可以建多少索引?

4)新建索引的规范原则有哪些?

本文我们就一起来展开聊聊这几个问题~

1、常见的索引分类有哪些?

1.1 应用层分类

从应用层面,常见分类:

  • 普通索引INDEX:加速查找

  • 唯一索引:

  • 主键索引PRIMARY KEY:加速查找+约束(不为空、不能重复)

  • 唯一索引UNIQUE:加速查找+约束(不能重复)

  • 联合索引:

  • PRIMARY KEY(id,name):联合主键索引

  • UNIQUE(id,name):联合唯一索引

  • INDEX(id,name):联合普通索引

1.2 数据结构层分类

从数据结构层面,分类如下:

  • 哈希(hash)索引

  • 基于哈希函数来实现。哈希函数会将索引键值(如数据库表中的某个字段值)作为输入,通过特定的算法运算,生成一个固定长度的哈希值。

  • 查询速度快,但不支持范围查找

  • B 树(btree)索引

  • 一种平衡的多叉树数据结构。B 树索引会将表中的索引键值按照一定的顺序(如升序或降序)存储在树的节点中。每个节点可以存储多个键值以及指向其他节点的指针。

  • 支持范围查询,但占用空间较大

2、新建索引的规范原则有哪些?

关于新建索引,通常需要注意以下规范原则:

2.1 最左前缀匹配原则

MySQL 会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配

比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的;

如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。

2.2 尽量选择区分度高的列作为索引

区分度的公式是:count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少。

唯一键的区分度是1,而一些 status 状态、性别等 字段可能在大数据面前区分度就是0。

2.3 索引列不能参与计算

保持索引列"干净",这个原因其实很简单,b+树中存的都是数据表中的字段值。但是在进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。

比如 from_unixtime(create_time) = '2014-05-29' 就不能使用到索引,需要将语句改写成:create_time = unix_timestamp('2014-05-29')。

2.4 尽量的扩展索引,不要新建索引

比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可,而不建议再单独去建一个b索引。

3、MySQL 是如何使用索引的?

索引用于快速查找具有特定列值的行,其目的在于提高查询效率。

与我们查阅图书所用的目录是一个道理:先定位到章,然后定位到该章下的一个小节,然后找到页数。相似的例子还有:查字典,查火车车次,飞机航班等。

本质都是:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件。

也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。

数据库也是一样,但显然要复杂得多,因为不仅面临着等值查询,还有范围查询(>、<、between、in)、模糊查询(like)、并集查询(or)等等。数据库应该选择怎么样的方式来应对所有的问题呢?

大多数 MySQL 索引(PRIMARY KEY、UNIQUEINDEX 和FULLTEXT)都存储在 B树 中。

另外:

空间数据类型使用 R 树;

MEMORYtable 还支持哈希索引;

InnoDB 对 FULLTEXT 索引使用倒排列表。

在 MySQL 中,使用索引进行以下操作:

3.1 = 和 in 可以乱序

比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,MySQL 的查询优化器会帮你优化成索引可以识别的形式。

3.2 and 与 or

联合索引:(d,a,b,c)

  1. 查询条件:

    a = 10 and b = 'xxx' and c > 3 and d =4

MySQL 会按照联合索引,从左到右的顺序找一个区分度高的索引字段(这样便可以快速锁定很小的范围),加速查询,即按照d--->a->b->c的顺序

  1. 查询条件:

    a = 10 or b = 'xxx' or c > 3 or d =4

MySQL 会按照条件的顺序,从左到右依次判断,即a->b->c->d

4、一张表最多可以建多少个索引?

4.1 理论上来说

MySQL 的存储引擎(如 InnoDB、MyISAM 等)本身并没有对一个表能创建的索引数量设置一个固定数值限制,,而是由MySQL数据库引擎内部的数据结构和算法决定的。

从数据库设计和架构的角度,理论上只要满足以下条件,就可以新增创建索引:

  • 存储空间允许:每个索引都需要占用一定的磁盘空间来存储索引数据结构,所以只要磁盘空间足够容纳新创建的索引结构及其相关数据,在空间层面就不会因空间不足而无法创建索引。

  • 性能可接受:随着索引数量的增加,虽然查询性能在某些情况下可能会因合适的索引而提升,但过多的索引也会带来一些负面效应,比如数据更新(插入、删除、修改操作)时需要同时更新相关索引,这会导致更新操作变慢。只要系统整体性能(包括查询性能和更新性能等)在可接受的范围内,理论上可以继续创建索引。

4.2 实际应用情况

然而在实际应用场景中,通常不会无限制地创建索引。一方面是因为上述提到的性能问题,过多的索引往往会导致数据更新操作变得极为缓慢,严重影响系统的正常运行。

例如,在一个高并发的电商订单处理系统中,如果对订单表的大量字段都创建了索引,那么每一次订单的插入、修改或删除操作,都要花费大量时间来更新相关索引,导致订单处理效率大幅下降。

另一方面,不同的 MySQL 版本以及不同的存储引擎在实际表现上也会有差异。

例如,对于一个拥有百万条记录的用户信息表,在 MySQL 5.7 中按照用户姓氏进行模糊查询,可能需要遍历相当一部分数据,查询速度相对较慢,假设平均每次查询需要 20 秒。而 MySQL 8.0 引入了新的索引算法和数据结构优化,新的索引算法对这种模糊查询的支持更好,同样的查询可能只需要 5 秒左右,性能提升明显。

总结

索引是应用程序设计和开发的一个重要方面。若索引太多,应用程序的性能可能会受到影响。而索引太少,对查询性能又会产生影响,要找到一个平衡点,这对应用程序的性能至关重要。

MySQL 表能创建的索引数量没有一个确切的、通用的绝对上限,而是要综合考虑多方面因素,在满足性能要求和存储空间允许的条件下合理创建索引。

其实做了这么长时间的语句优化后才发现,任何数据库层面的优化都抵不上应用系统的优化,同样是MySQL,可以用来支撑Google/FaceBook/Taobao应用,但可能连你的个人网站都撑不住。套用最近比较流行的话:"查询容易,优化不易,且写且珍惜!"

·END·

希望今天的讲解对大家有所帮助,谢谢!

Thanks for reading!

作者:张张,十年研发风雨路,大厂架构师,「架构精进之路」专注架构技术沉淀学习及分享,职业与认知升级,坚持分享接地气儿的干货文章,期待与你一起成长。

关注并私信我回复"01",送你一份程序员成长进阶大礼包,欢迎勾搭。

相关推荐
蜗牛沐雨11 分钟前
警惕 Rust 字符串的性能陷阱:`chars().nth()` 的深坑与高效之道
开发语言·后端·rust
&Sinnt&1 小时前
Git 版本控制完全指南:从入门到精通
git·后端
陈随易1 小时前
MoonBit助力前端开发,加密&性能两不误,斐波那契测试提高3-4倍
前端·后端·程序员
wfsm1 小时前
spring事件使用
java·后端·spring
老纪的技术唠嗑局1 小时前
OceanBase PoC 经验总结(二)—— AP 业务
数据库
微风粼粼2 小时前
程序员在线接单
java·jvm·后端·python·eclipse·tomcat·dubbo
阿里云大数据AI技术2 小时前
OpenSearch 视频 RAG 实践
数据库·人工智能·llm
rebel2 小时前
若依框架整合 CXF 实现 WebService 改造流程(后端)
java·后端
极客悟道2 小时前
颠覆传统虚拟化:在Docker容器中运行Windows系统的开源黑科技
前端·后端
调试人生的显微镜3 小时前
WebView 中 Cookie 丢失怎么办?跨域状态不同步的调试与修复经验
后端