题目:设计索引的时候有哪些原则(考虑哪些因素)?
难度:high
标签:db/mysql/index, mysql/index, difficulty/high
评分:⭐⭐⭐☆(3/5)
设计索引,MySQL 设计索引首先要考虑数据量情况,然后另外要根据实际业务看业务是怎么划分的,业务怎么查询的,然后以及业务的排序规则去考虑使用哪些索引。要创建索引,毕竟要根据实际业务来,有业务才去建索引,没业务没有必要建这些索引,因为建立索引是要耗费资源。要说原则吧,首先是要在区分度比较大的数据上,字段上进行索引创建,而不要在区分度特别小的字段上建索引。区分小的字段上建索引有意义,但意义不大。另外的话是,我要考虑联合索引,也就是多个字段建立索引。这时候要考虑的话,可能一个要考虑回表,一个是查询规则,还有是排序规则。依据目标去创建对应的联合索引,达到快速查询、快速返回的目的。然后索引的原则,另外如果有匹配的模糊查询之类的,可能会加一些特殊的全文索引,这时候如果不用 eS 类的搜索引擎的话,就加全文索引。基本上索引的创建原则大概就是这些。另外创建索引时候要考虑索引是本身是有长度限制的,不能在太长。这个不能太长指的是单个字段上的字段值不能太长。比如你在一个 Text 字段上加索引,最好截取前面多少,而不是说整个 Text 那么大的一个字段去建索引。Text 不是 test。
优点:
不足:
distinct_count / total_rows,越接近 1 越适合建索引(a,b,c) 存在时 (a,b)、(a) 就是冗余INVISIBLE INDEX(灰度下线)、descending index、functional indexinnodb_large_prefix=ON),旧版本 767 字节补充:
sys.schema_unused_indexes、performance_schema.table_io_waits_summary_by_index_usage.count_star=0ALTER TABLE t ALTER INDEX idx INVISIBLE 观察再删核心考点定位:以业务查询模式为锚点,系统掌握索引设计的全维度决策框架,覆盖数据、访问、性能、治理四条主线。
知识点拆解:
distinct_count / total_rowsSHOW INDEX FROM t 里的 Cardinality 是采样估算,用 SELECT COUNT(DISTINCT col), COUNT(*) FROM t 拿真实值WHERE a=? AND b>? → 索引顺序 (a,b) 才对ORDER BY 的列如果放在联合索引最后,且前面都是等值,可以省 filesort(status, user_id),status 基数 3 会导致扫描大量重复行SELECT 的列全部命中某个索引 → 走覆盖索引,Extra=Using indexLIMIT 10 的查询可能回表几百万次innodb_large_prefix=ON 时 3072 字节(COMPRESSED/DYNAMIC 行格式)COMPACT/REDUNDANT 行格式)INDEX(name(20))SELECT COUNT(DISTINCT LEFT(name, N)) / COUNT(DISTINCT name) FROM t,达到 0.99+ 停止ngram 分词器FUNCTIONAL INDEX(表达式索引),DESCENDING INDEX(降序索引)(a,b,c) 已存在时 (a,b)、(a) 是冗余(a) 和 (a) 重复(a,b) 和 (b,a) 都能用,但用途不同,通常保留一个sys.schema_unused_indexes 找 count_star=0 的索引performance_schema.table_io_waits_summary_by_index_usage 精确统计ALTER TABLE t ALTER INDEX idx INVISIBLE 灰度下线,观察 1-2 周再 DELETEFORCE INDEX 硬编码是雷区:索引被删业务立即崩schema_unused_indexes 和慢查询EXPLAIN ANALYZE(8.0)看真实代价考察方向:
延伸递进面试题:
(status, user_id, create_time) 是常见设计,如果查询是 WHERE status=1 ORDER BY create_time DESC,这个索引能用吗?user_id 未知但因为是联合索引的中间列——不对,中间列未知则 create_time 无法走排序。所以要改成 (status, create_time) 或直接建 (status, create_time DESC)(8.0 支持)。SELECT COUNT(DISTINCT LEFT(col, N)), COUNT(DISTINCT col) FROM t 让比值达到 0.99 或以上,同时兼顾空间。经验值:邮箱 20、手机号 11、用户名 10-15。INVISIBLE INDEX 相比直接删除有什么好处?DROP INDEX。避免"删了才发现在用"的事故。DDL 也是在线的。面试官您好,索引设计我按四条主线来讲:业务、数据、性能、治理。
第一条,业务维度。索引不是越多越好,一定要业务驱动——有查询模式才建索引。我会先摸清几个问题:谁在查、查多频繁、单条查询返回多少行、要不要排序、表是读多还是写多。小表几千行不用建,优化器自己全表扫;大表必须靠索引。写多读少的表要克制,每个索引都是 INSERT/UPDATE/DELETE 的额外维护成本。
第二条,数据维度,重点是基数和联合索引列顺序。基数就是 distinct_count / total_rows,越接近 1 越适合建单列索引,比如 ID、订单号;基数很低(如 status 只有 2-3 值)单独建索引基本没用,但可以放联合索引前面做前缀。联合索引的列顺序有讲究:等值条件列放前面、范围条件列放后面、ORDER BY 列放最后——这样能省 filesort。反过来如果排序列前面还有个未知列,排序就废了。
第三条,性能维度,重点是覆盖索引和回表控制。如果能把 SELECT 列全部塞进一个索引里,就走覆盖索引,EXPLAIN 显示 Using index,性能能提升好几倍。但索引宽度也有代价,不能什么都往里塞。回表次数取决于索引扫描行数而不是 LIMIT,所以要用延迟关联或 ICP 减少。索引键长度也有硬限制,InnoDB 默认 3072 字节,TEXT 字段必须做前缀索引,用 SELECT COUNT(DISTINCT LEFT(col, N)) / COUNT(DISTINCT col) 找最优长度。
第四条,索引治理。这块特别重要,很多线上事故出在这里。三个点:一是冗余治理,(a,b,c) 存在时 (a,b)、(a) 就是冗余,应该删掉;二是无索引治理,用 sys.schema_unused_indexes 或者 performance_schema.table_io_waits_summary_by_index_usage.count_star=0 找长期没人用的索引;三是灰度下线,MySQL 8.0 有 ALTER TABLE t ALTER INDEX idx INVISIBLE,可以先设为不可见观察一两周,确认没有 SQL 依赖后再真删。
特殊场景还有几个:JOIN 关联字段必须建索引;分库分表要考虑 shard key 是否在索引里;唯一约束优先用唯一索引既能防重又能加速;主键选自增 bigint 避免 UUID 打散 B+ 树导致页分裂和随机 IO;索引列最好 NOT NULL,避免 NULL bitmap 的额外开销。
最后工程化流程:任何索引变更走 CR,生产变更前跑 EXPLAIN ANALYZE 看真实代价,每月审计一次 schema_unused_indexes。特别警惕 FORCE INDEX 硬编码——团队有人删了这个索引而代码里还强推,业务直接生产报错,我之前踩过这个坑。
题目:设计索引的时候有哪些原则(考虑哪些因素)?
难度:high
标签:db/mysql/index, mysql/index, difficulty/high
评分:⭐⭐⭐⭐☆(4/5)
练习次数:2
设计索引要考虑的核心机制,首先是业务第一。基于什么样的业务去设计什么样的索引,不能创建一个没有意义的索引。你创建了一个特别好的索引,但这个索引没有意义,创建的是没有用的。
首先创建索引要在区分度比较高的字段上进行创建。如果一个字段区分度特别低,各种数据一样占一半,或者占的比例都很多,没有什么区分度,那么这个索引的意义也不大。
要解决的核心目标是什么?是为了快速的找到我们目标的数据。前置说的区分度高的,就是为了这个;然后整个索引的过程会涉及整个搜索的过程会涉及几个方面。首先是通过索引找到数据,然后如果你要显示的数据在索引中不存在,就会涉及一个回表。这里边要考虑的就是你的索引是否要进行覆盖,覆盖索引的所有字段。如果能覆盖,就会减少一次搜索。
另外就是查询的时候会涉及排序,排序的字段是否要包含在索引中?尽可能的其实要把排序字段在索引中体现出来,这样可以更好的利用索引提前进行排序,而不是搜索出来之后再进行外部的文件排序,那样会浪费更多的时间和资源。
还有就是对于数据量的考量,数据量级具体在什么量级,使用什么样的索引,不同量级的话可能要考虑提前做分库分表,这里不仅仅是索引方面的问题,还有对数据量评估,以什么维度去进行数据库的拆分。
这就是我考虑的创建索引时的主要原则。
优点:
ORDER BY 优化要点不足:
INDEX(col(20)) 而非整列COMPRESSED/DYNAMIC)(a,b,c) 已存在时 (a,b)、(a) 冗余;schema_unused_indexes、INVISIBLE INDEX 灰度下线FUNCTIONAL INDEX(表达式索引)、DESCENDING INDEX(降序索引)补充:
WHERE a=? AND b>? ORDER BY c → (a, b, c)sys.schema_unused_indexes、performance_schema.table_io_waits_summary_by_index_usage.count_star=0ALTER TABLE t ALTER INDEX idx INVISIBLE 观察再删ℹ️ 完整讲解见上方首次记录(2026-09-17)