导读:本期聚焦于广州程序员创作的《MySQL InnoDB建立索引的原则有哪些?一文讲清索引设计的正确姿势》,敬请观看详情。索引建得多不等于查询快,建得不合理反而会拖慢写入、浪费磁盘空间。本文以MySQL的InnoDB存储引擎为例,系统梳理建立索引时需要遵循的核心原则:从最左前缀匹配、联合索引的列顺序安排,到区分度、索引列的选择,再到覆盖索引、前缀索引的取舍,以及哪些列不适合建索引。文章结合具体的建表示例和执行计划分析,讲清每条原则背后的B+树原理,帮助你避开常见的索引误区,设计出真正高效的索引结构。

索引是数据库性能优化的第一道关卡,但很多刚接触MySQL的同学对索引的理解还停留在“加了索引就快”的层面。实际上,索引建得对不对,直接决定了查询是毫秒级还是分钟级。InnoDB作为MySQL最常用的存储引擎,它的索引结构基于B+树组织,理解这套结构才能明白为什么建索引要遵循那些原则。这篇文章就以InnoDB为例,把建立索引时的核心原则一条条讲透。

MySQL InnoDB建立索引的原则有哪些?一文讲清索引设计的正确姿势

先弄懂InnoDB索引的底层结构

InnoDB的索引分为聚簇索引和二级索引两种。聚簇索引就是主键索引,整张表的数据按照主键的顺序存放在B+树的叶子节点上,换句话说,叶子节点存的就是完整的行记录。而二级索引(也叫辅助索引)的叶子节点存放的是索引列的值加上主键值,查完二级索引后如果要取其他列,还得拿主键回聚簇索引再查一次,这个过程就是常说的“回表”。

正因为回表的存在,InnoDB强烈建议使用自增主键。如果主键是随机的(比如UUID),每次插入都要把记录插到B+树的中间位置,导致页分裂频繁,写入性能明显下降。自增主键则永远是顺序追加,页写满了就开新页,效率高得多。同时,二级索引叶子节点存的是主键值,主键越短,二级索引就越小,占用的磁盘空间和内存也就越少。

看一个简单的建表示例:

CREATE TABLE user_order (
    id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    user_id BIGINT NOT NULL,
    order_no VARCHAR(32) NOT NULL,
    status TINYINT NOT NULL,
    amount DECIMAL(10,2) NOT NULL,
    created_at DATETIME NOT NULL,
    PRIMARY KEY (id),
    KEY idx_user_created (user_id, created_at)
) ENGINE=InnoDB;

这里用自增的id做主键,另外给user_idcreated_at建了一个联合索引,这是后面会多次用到的例子。

联合索引与最左前缀原则

联合索引是索引设计中最容易出问题的地方。InnoDB的联合索引按照定义时的列顺序依次排序,比如idx_user_created(user_id, created_at),数据会先按user_id排序,user_id相同再按created_at排序。这意味着查询条件必须从最左边的列开始,才能用到这个索引。

具体来说:单独查user_id能用上索引;查user_idcreated_at能完整用到索引;但只查created_at就用不上了,因为跳过了user_id,B+树里created_at在相同user_id内部才是有序的,全局上是乱序的。还有一点容易被忽略:如果对索引列用了函数或者隐式类型转换,索引同样会失效,比如WHERE DATE(created_at) = '2024-01-01'就走不了索引,应该改写成范围条件。

所以安排联合索引列的顺序时有两条思路:一是把等值查询的列放前面,范围查询的列放后面,因为范围条件之后的列没法继续走索引;二是把区分度高、最常被单独查询的列放最左边,让这个联合索引尽可能多地为一些只查左列的语句服务。区分度指的是不重复值的数量除以总行数,越接近1说明这个列过滤能力越强,越值得放在前面。

覆盖索引与前缀索引的取舍

前面提到回表是有代价的,如果查询需要的列全部包含在索引里,就不用回表了,这种做法叫覆盖索引。比如查询改成SELECT user_id, created_at FROM user_order WHERE user_id = 123,由于idx_user_created本身就包含这两列,直接在二级索引上就能返回结果,执行计划的Extra列会显示Using index,性能比回表快很多。高频查询如果能通过合理设计联合索引实现覆盖,收益非常可观。

对于很长的字符串列(比如邮箱、URL),整列建索引会让索引变得巨大。InnoDB对索引列的长度有限制(默认配置下单列索引最多767字节,开启DYNAMIC行格式后可到3072字节),这时候可以用前缀索引:ALTER TABLE user ADD INDEX idx_email (email(10)),只取前10个字符建索引。前缀索引能省空间,代价是无法用于覆盖索引和ORDER BY优化,因为索引里只有前缀,不能确认完整值。前缀长度怎么定?可以先用一个公式算区分度:

SELECT
    COUNT(DISTINCT LEFT(email, 10)) / COUNT(*) AS sel_10,
    COUNT(DISTINCT LEFT(email, 15)) / COUNT(*) AS sel_15
FROM user;
-- 选择区分度接近整列区分度、且长度较短的那个

哪些情况不适合建索引,以及维护成本

索引不是越多越好。每多一个索引,INSERT、UPDATE、DELETE都要额外维护对应的B+树,写入变慢,磁盘占用增加,优化器在选择执行计划时的负担也会变重。经验上,单表索引数量建议控制在5个以内,重复或者冗余的索引要清理,比如已经建了(a, b)就没必要再单独建(a)

有几类列通常不值得建索引:一是区分度极低的列,比如性别、状态字段只有两三个取值,索引过滤不了多少数据,优化器多半直接走全表扫描;二是频繁更新的列,每次更新都要调整索引结构;三是表数据量很小的表,几百行的表全表扫描比走索引还快;四是WHERE条件里永远不会单独出现的列。

最后要强调的一点是,建索引不能凭感觉,要拿数据说话。EXPLAIN是判断索引是否生效的基本工具,重点看type列(至少要达到range级别,最好能到ref或const)、key列(实际用到的索引)以及rows列(预估扫描行数)。上线前用真实数据量做EXPLAIN验证,上线后配合慢查询日志持续观察,发现无效索引及时调整,这才是管理索引的正确方式。索引设计本质上是在查询速度和写入成本之间找平衡,理解了InnoDB的B+树结构和回表机制,每一条原则都能推导出来,也就不必死记硬背了。

InnoDB索引索引原则MySQL优化修改时间:2026-09-03 23:00:53

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260903/49861.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。