索引
索引为什么能提高MySQL查询效率
它一般是 B+ 树结构,查找效率是 O(log n),比从头到尾扫一遍数据要快得多。
除了查得快,索引还能加速排序、分组、连接等操作。
项目中最常见的做法就是通过 create index 为经常用作查询条件的字段建索引,比如:
create index idx_name on students(name);能简单说一下索引的分类吗
从功能上分类的话,有主键索引、唯一索引、全文索引;从数据结构上分类的话,有 B+ 树索引、哈希索引;从存储内容上分类的话,有聚簇索引、非聚簇索引。
你对主键索引了解多少
主键索引用于唯一标识表中的每条记录,其列值必须唯一且非空。创建主键时,MySQL 会自动生成对应的唯一索引。
每个表只能有一个主键索引,一般是表中的自增 id 字段。
唯一索引和主键索引有什么区别?
主键索引=唯一索引+非空。每个表只能有一个主键索引,但可以有多个唯一索引。
unique key 和 unique index 有什么区别?
创建唯一键时,MySQL 会自动生成一个同名的唯一索引;反之,创建唯一索引也会隐式添加唯一性约束。
可通过 UNIQUE KEY uk_name 定义或者 CONSTRAINT uk_name UNIQUE 定义唯一键。
普通索引和唯一索引有什么区别?
普通索引仅用于加速查询,不限制字段值的唯一性;适用于高频写入的字段、范围查询的字段。
唯一索引强制字段值的唯一性,插入或更新时会触发唯一性检查;适用于业务唯一性约束的字段、防止数据重复插入的字段。
你对全文索引了解多少
全文索引是 MySQL 一种优化文本数据检索的特殊类型索引,适用于 CHAR、VARCHAR 和 TEXT 等字段。
MySQL 5.7 及以上版本内置了 ngram 解析器,可处理中文、日文和韩文等分词。
建表时通过 FULLTEXT (title, body) 来定义。通过 MATCH(col1, col2) AGAINST('keyword') 进行检索,默认按照降序返回结果,支持布尔模式查询。
- 表示必须包含;
- 表示排除;
- 表示通配符;
底层使用倒排索引将字段中的文本内容进行分词,然后建立一个倒排表。性能比 LIKE '%keyword%' 高很多。、
创建索引有哪些注意点
第一,选择合适的字段
比如说频繁出现在 WHERE、JOIN、ORDER BY、GROUP BY 中的字段。
优先选择区分度高的字段,比如用户 ID、手机号等唯一值多的,而不是性别、状态等区分度极低的字段,如果真的需要,可以考虑联合索引。
第二,要控制索引的数量,避免过度索引,每个索引都要占用存储空间,单表的索引数量不建议超过 5 个。
要定期通过 SHOW INDEX FROM table_name 查看索引的使用情况,删除不必要的索引。比如说已经有联合索引 (a, b),单索引(a)就是冗余的。
第三,联合索引的时候要遵循最左前缀原则,即在查询条件中使用联合索引的第一个字段,才能充分利用索引。
比如说联合索引 (A, B, C) 可支持 A、A+B、A+B+C 的查询,但无法支持 B 或 C 的单独查询。
区分度高的字段放在左侧,等值查询的字段优先于范围查询的字段。例如 WHERE A=1 AND B>10 AND C=2,优先 (A, C, B)。
如果联合索引包含查询的所需字段,还可以避免回表,提高查询效率。
索引哪些情况下会失效呢?
比如索引列使用了函数、使用了通配符开头的模糊查询、联合索引不满足最左前缀原则,或者使用 or 的时候部分字段无索引等。
什么情况下模糊查询不走索引?
模糊查询主要使用 LIKE 语句,结合通配符来实现。
%(代表任意多个字符)和 _(代表单个字符)
但是,如果模糊查询的通配符 % 出现在搜索字符串的开始位置,如 LIKE '%xxx',MySQL 将无法使用索引,因为数据库必须扫描全表以匹配任意位置的字符串。
索引不适合哪些场景呢?
第一,区分度低的列,可以和其他高区分度的列组成联合索引。
第二,频繁更新的列,索引会增加更新的成本。
第三,TEXT、BLOB 等大对象类型的字段,可以使用前缀索引、全文索引替代。
第四,当表的数据量很小的时候,不超过 1000 行,全表扫描可能比使用索引更快。
索引是不是建的越多越好?
索引不是越多越好。虽然索引可以加快查询,但也会带来写入变慢、占用更多存储空间、甚至让优化器选错索引的风险。
说说索引优化的思路?
先通过慢查询日志找出性能瓶颈,然后用 EXPLAIN 分析执行计划,判断是否走了索引、是否回表、是否排序。接着根据字段特性设计合适的索引,如选择区分度高的字段,使用联合索引和覆盖索引,避免索引失效的写法,最后通过实测来验证优化效果。
为什么 InnoDB 要使用 B+树作为索引
因为 B+ 树是一种高度平衡的多路查找树,能有效降低磁盘的 IO 次数,并且支持有序遍历和范围查询。
查询性能非常高,其结构也适合 MySQL 按照页为单位在磁盘上存储。
像其他选项,比如说哈希表不支持范围查询,二叉树层级太深,B 树又不方便范围扫描,所以最终选择了 B+ 树。
B+树的叶子节点是单向链表还是双向链表?如果从大值向小值检索,如何操作?
B+树的叶子节点是通过双向链表连接的,这样可以方便范围查询和反向遍历。
当执行范围查询时,可以从范围的开始点或结束点开始,向前或向后遍历。
在需要对数据进行逆序处理时,双向链表非常有用。
如果需要在 B+树中从大值向小值进行检索,可以先定位到最右侧节点,找到包含最大值的叶子节点。从根节点开始向右遍历树的方式实现。
为什么 MongoDB 的索引用 B树,而 MySQL 用 B+ 树?
MongoDB 通常以 JSON 格式存储文档,查询以单键查询(如 find({_id: 123}))为主。B 树的“节点既存键又存数据”的特性允许查询在非叶子节点提前终止,从而减少 I/O 次数。
MySQL 的查询通常涉及范围(WHERE id > 100)、排序(ORDER BY)、连接(JOIN)等操作。B+ 树的叶子节点是链表结构,天然支持顺序遍历,无需回溯至根节点或中序遍历,效率远高于 B 树。
一棵B+树能存储多少条数据呢?
一棵 B+ 树能存多少数据,取决于它的分支因子和高度。在 InnoDB 中,页的默认大小为 16KB,当主键为 bigint 时,3 层 B+ 树通常可以存储约 2000 万条数据。
现在有一张表 2kw 数据,我这个 b+树的高度有几层?
对于 2KW 条数据来说,B+树的高度为 3 层就够了。
每个叶子节点能存放多少条数据?
如果单行数据大小为 1KB,那么每页可存储约 16 行(16KB/1KB)数据。
索引为什么用 B+树不用普通二叉树?
普通二叉树的每个节点最多有两个子节点。当数据按顺序递增插入时,二叉树会退化成链表,导致树的高度等于数据量。
为什么不用平衡二叉树呢?
平衡二叉树虽然解决了普通二叉树的退化问题,但每个节点最多只有两个子节点的问题依然存在。
为什么用 B+ 树而不用 B 树呢?
B+ 树相比 B 树有 3 个显著优势:
第一,B 树的每个节点既存储键值,又存储数据和指针,导致单节点存储的键值数量较少。
一个 16KB 的 InnoDB 页,如果数据较大,B 树的非叶子节点只能容纳几十个键值,而 B+ 树的非叶子节点可以容纳上千个键值。
第二,B 树的范围查询需要通过中序遍历逐层回溯;而 B+ 树的叶子节点通过双向链表顺序连接,范围查询只需定位起始点后顺序遍历链表即可,没有回溯开销。
第三,B 树的数据可能存储在任意节点,假如目标数据恰好位于根节点或上层节点,查询仅需 1-2 次 I/O;但如果数据位于底层节点,则需多次 I/O,导致查询时间波动较大。
而 B+ 树的所有数据都存储在叶子节点,查询路径的长度是固定的,时间稳定为 O(logN),对 MySQL 在高并发场景下的稳定性至关重要。
B+树的时间复杂度是多少?
为什么用 B+树不用跳表呢?
跳表本质上还是链表结构,只不过把某些节点抽到上层做了索引。
B+树的范围查找怎么做的?
先通过索引路径定位到第一个满足条件的叶子节点,然后顺着叶子节点之间的链表向右/向左扫描,直到超过范围。
B+树索引和 Hash 索引有什么区别?
Hash 索引只支持等值查找,速度快但功能弱,常见于 Memory 引擎。B+ 树索引支持范围查询、有序扫描,是 InnoDB 的默认索引结构。
聚族索引和非聚族索引有什么区别?
聚簇索引的叶子节点存储了完整的数据行,数据和索引是在一起的。InnoDB 的主键索引就是聚簇索引,叶子节点不仅存储了主键值,还存储了其他列的值,因此按照主键进行查询的速度会非常快。
每个表只能有一个聚簇索引,通常由主键定义。如果没有显式指定主键,InnoDB 会隐式创建一个隐藏的主键索引 row_id。
非聚簇索引的叶子节点只包含了主键值,需要通过回表按照主键去聚簇索引查找其他列的值,唯一索引、普通索引等非主键索引都是非聚簇索引。
每个表都可以创建多个非聚簇索引,如果不想回表的话,可以通过覆盖索引把要查询的字段也放到索引中。
回表了解吗?
当使用非聚簇索引进行查询时,MySQL 需要先通过非聚簇索引找到主键值,然后再根据主键值回到聚簇索引中查找完整数据行,这个过程称为回表。
回表的代价是什么?
回表通常需要访问额外的数据页,如果数据不在内存中,还需要从磁盘读取,增加 I/O 开销。
可通过覆盖索引或者联合索引来避免回表。
什么情况下会触发回表?
第一,当查询字段不在非聚簇索引中时,必须回表到主键索引获取数据。
第二,查询字段包含非索引列(如 SELECT *),必然触发回表。
回表记录越多好吗?
回表记录越多,通常代表性能越差,因为每条记录都需要通过主键再查询一次完整数据。这个过程涉及内存访问或磁盘 IO,尤其当缓存命中率不高时,回表会严重影响查询效率。
了解 MRR 吗?
MRR 是 InnoDB 为了解决回表带来的大量随机 IO 问题而引入的一种优化策略。
它会先把非聚簇索引查到的主键值列表进行排序,再按顺序去主键索引中批量回表,将随机 I/O 转换为顺序 I/O,以减少磁盘寻道时间。
联合索引了解吗?
联合索引就是把多个字段放在一个索引里,但必须遵守“最左前缀”原则,只有从第一个字段开始连续使用,索引才会生效。
联合索引会按字段顺序构建B+树。例如(age, name)索引会先按照 age 排序,age 相同则按照 name 排序,若两者都相同则按主键排序,确保叶子节点无重复索引项。
创建(A,B,C)联合索引相当于同时创建了(A)、(A,B)和(A,B,C)三个索引。
联合索引底层的存储结构是怎样的?
联合索引在底层采用 B+ 树结构进行存储,这一点与单列索引相同。
与单列索引不同的是,联合索引的每个节点会存储所有索引列的值,而不仅仅是第一列的值。例如,对于联合索引(a,b,c),每个节点都包含 a、b、c 三列的值。
联合索引的叶子节点存的什么内容?
联合索引属于非聚簇索引,叶子节点存储的是联合索引各列的值和对应行的主键值,而不是完整的数据行。查询非索引字段时,需要通过主键值回表到聚簇索引获取完整数据。
例如索引(a, b)的叶子节点会完整存储(a, b)的值,并按字段顺序排序(如 a 优先,a 相同则按 b 排序)。如果主键是 id,叶子节点会存储 (a, b, id) 的组合。
覆盖索引
覆盖索引指的是:查询所需的字段全部都在索引中,不需要回表,从索引页就能直接返回结果。
可以将高频查询的字段(如 WHERE 条件和 SELECT 列)组合为联合索引,实现覆盖索引。
什么是最左前缀原则?
最左前缀原则指的是:MySQL 使用联合索引时,必须从最左边的字段开始匹配,才能命中索引。
范围查询后的列还能用索引吗?
范围查询只能应用于最左前缀的最后一列。范围查询之后的列无法使用索引。
SQL
-- 索引(a,b,c)
SELECT * FROM table WHERE a = 1 AND b > 2 AND c = 3;
-- 只能使用a和b,c无法使用索引为什么不从最左开始查,就无法匹配呢?
一句话回答:
因为联合索引在 B+ 树中是按照最左字段优先排序构建的,如果跳过最左字段,MySQL 无法判断查找范围从哪里开始,自然也就无法使用索引。
什么是索引下推
索引下推是指:MySQL 把 WHERE 条件尽可能“下推”到索引扫描阶段,在存储引擎层提前过滤掉不符合条件的记录。
当查询条件包含索引列但未完全匹配时,ICP 会在存储引擎层过滤非索引列条件,以减少回表次数。
传统的查询流程是,储引擎通过联合索引定位到符合最左前缀条件的主键 ID;回表读取完整数据行并返回给 Server 层;Server 层对所有返回的行进行 WHERE 条件过滤。
有了 ICP 后,存储引擎在索引层直接过滤可下推的条件,仅对符合索引条件的记录回表读取数据,再返回给 Server 层进行剩余条件过滤。
如何查看是否用到了索引?
可以通过 EXPLAIN 关键字来查看是否使用了索引。
如果使用了索引,结果中的 key 值会显示索引的名称。
