索引为什么能够提高MySQL的查询效率?
索引可以理解为目录,通过索引可以快速定位数据,避免全表扫描
一般是B+树结构,查找效率是O(log n)。
索引还能加速排序、分组、连接等操作。
create index idx_name on students(name);
能简单说一下索引的分类吗?
从功能上分,有主键索引、唯一索引、联合索引、前缀索引和全文索引。
从数据结构上分,有B+树索引、哈希索引。
从存储内容上分,有聚簇索引、非聚簇索引。
你对主键索引了解多少?
主键索引用于唯一标识表中的每条记录,其列值必须唯一且不为空。创建主键时会自动生成主键索引。
唯一索引和主键索引有什么区别?
主键索引 = 唯一索引 + 非空,每个表只能有一个主键索引,但是可以有多个唯一索引,唯一索引的列允许插入多个NULL值。
unique key和unique index有什么区别?
unique key是一种约束,创建唯一键时,MySQL会自动创建一个同名的唯一索引;反之,创建唯一索引也会隐式添加唯一性约束。
可通过 UNIQUE KEY uk_name 定义或者 CONSTRAINT uk_name UNIQUE 定义唯一键。
可通过 CREATE UNIQUE INDEX 创建唯一索引。
你对全文索引了解多少?
全文索引是一种优化文本数据检索的特殊类型索引,适用于CHAR、VARCHAR、TEXT
建表时可以通过FULLTEXT INDEX index_name (title, body) WITH PARSER ngram定义
ngram是一种解析器,可以处理中文日文韩文分词
使用时用MATCH(title, content) AGAINST('+xxx -xxx' IN BOOLEAN MODE)
默认按降序返回结果,+标识必须包含,-表示必须排除,*表示通配符。
底层使用倒排索引将字段中的文本内容进行分词,然后建立一个倒排表,不是从文档找词汇,而是从词汇找文档。
创建索引有哪些注意点?
第一、选择合适的字段
- 比如说频繁出现在 WHERE、JOIN、ORDER BY、GROUP BY 中的字段。
- 优先选择区分度高的字段,比如用户 ID、手机号等唯一值多的,而不是性别、状态等区分度极低的字段,如果真的需要,可以考虑联合索引。
第二、要控制索引的数量,避免过度索引,比如说已经有联合索引 (a, b),单索引(a)就是冗余的。每个索引都要占据存储空间,建议单表的索引不超过五个。
第三、联合索引使用时遵循最左前缀原则,定义时区分度高的字段优先于区分度低的字段,等值查询的字段优先于范围查询的字段。
索引哪些情况下会失效?
索引列使用了函数、使用了通配符开头的模糊查询、联合索引不满足最左前缀原则、WHERE条件中使用or的时候部分字段无索引等
索引不适合哪些场景呢?
区分度低的列、频繁更新的列(列更新代表索引也要更新)、数据量小时
区分度 = 字段的唯一值数量 / 字段的总记录数
索引是不是建的越多越好
不是,索引是会占用空间的,并且更新建了索引的字段的值也会同时更新索引,导致写入变慢。在索引过多的情况下优化器也有可能选错索引
说说索引优化的思路?
先通过慢查询日志找出哪些SQL拖后腿,然后调用EXPLAIN查看执行计划,看看是不是走了索引,是否回表、是否排序。然后根据字段特性设计合适的索引,如选择区分度高的字段,使用联合索引和覆盖索引,避免索引失效的写法,最后通过实测来验证优化效果。
为什么innodb要使用B+树作为索引?
降低磁盘的I/O次数,支持有序遍历和范围查找,因为索引本来就是有序的。
B+树的每个节点都是一个页,MySQL在磁盘中也是按页存储的。
B+树的叶子节点构成了一个有序链表
哈希表不支持范围查询(无序),二叉树太深,B树所有节点都要存数据,所以使用B+树
为什么MongoDB的索引采用B树,而MySQL采用B+树?
MongoDB通常以类似JSON形式存储文档,查询的时候一般是使用单键等值。B树既存储key由存储数据,这样允许搜索的时候允许在非叶子节点提前终止,减少IO次数。
MySQL的查询一般涉及范围、排序、分组等操作。B+树的叶子节点是双向链表结构,无序回溯查找,直接通过叶子节点链表顺序遍历即可,天然具有有序性。
一颗B+树能存多少条数据?
取决于树的高度和分支因子
(分支因子)^(树高度-1) × 叶子节点容量
对于 2KW 条数据来说,B+树的高度为 3 层就够了。
为什么索引用B+树而不用二叉树
因为二叉树在按照顺序从大到小插入时会退化成链表,树的高度就是数据量,导致IO次数增多。
平衡二叉树虽然解决了退化成链表的问题,但是每个节点仍然只能有两个分支,深度仍然很大。
为什么使用B+树而不是B树?
第一,B 树的每个节点既存储键值,又存储数据和指针,导致单节点存储的键值数量较少。
第二、B 树的范围查询需要通过中序遍历逐层回溯;而 B+ 树的叶子节点通过双向链表顺序连接,范围查询只需定位起始点后顺序遍历链表即可,没有回溯开销。
第三,B 树的数据可能存储在任意节点,假如目标数据恰好位于根节点或上层节点,查询仅需 1-2 次 I/O;但如果数据位于底层节点,则需多次 I/O,导致查询时间波动较大。
而 B+ 树的所有数据都存储在叶子节点,查询路径的长度是固定的,时间稳定为 O(logN),对 MySQL 在高并发场景下的稳定性至关重要。
为什么使用B+树而不使用跳表?
跳表本质上仍然是链表,假设每次向下都是二分查找,那么2000w条数据下,查找需要24次IO,因为2000w≈2^24,而B+树最多只需要三次IO就够了
B+树的范围查找怎么做的?
先通过索引路径定位到第一个满足条件的叶子节点,然后顺着叶子节点之间的链表向右/向左扫描,直到超过范围。
了解快排吗?
用分治法将一个序列分割为较小和较大的两个子序列,然后递归排序两个字序列。
核心思想是选择一个基准值,将数组分为两个部分,左边小于基准值,右边大于等于基准值
B+树索引和哈希索引有什么区别?
B+树索引是一种平衡多路搜索树,所有的数据都存在叶子节点上,非叶子节点只存储key和页面指针,叶子节点是有序的,支持范围查找和有序遍历和模糊查询。
Hash索引是将键值映射到固定长度的哈希值,通过哈希值定位数据的位置,不支持有序遍历和范围查找,完全无序,只支持等值查询,常见于Memory引擎。
聚簇索引和非聚簇索引有什么区别?
聚簇索引的叶子节点不仅存储索引,还存储了完整行数据,数据和索引是一起的,InnoDB的主键索引就是聚簇索引。非聚簇索引的叶子节点存储的是索引和对应聚簇索引的键值(主键值),需要回表。非主键索引都是非聚簇索引,比如唯一索引,普通索引,全文索引,前缀索引,联合索引。
如果使用非主键索引也不想回表,可以定义覆盖索引,并在使用的时候遵循左前缀原则。
回表了解吗?
使用非聚簇索引时,索引没有覆盖所需要查找的列,需要通过非聚簇索引的叶子节点找到对应的主键值,再利用主键值在聚簇索引中找到完整的行记录,这个过程称为回表。
回表的代价是什么?
回表需要访问额外的数据页,如果想要访问的数据不在内存中,还需要从磁盘查找,增加IO开销
可以通过联合索引和覆盖索引避免回表
什么情况下会触发回表?
当查询字段不在非聚簇索引的叶子节点的键值中时候,必须回到主键索引中获取数据
查询字段包含非索引列的时候,必然触发回表
了解MRR吗
MRR是为了避免大量回表引来的大量随机IO问题引入的一种优化策略。
把非聚簇索引查到的主键值列表进行排序,再按顺序去主键索引中批量回表,将随机IO转换为顺序IO,减少磁盘寻道时间。
联合索引了解吗?
联合索引就是把多个字段放在一个索引里,但必须遵守“最左前缀”原则,只有从第一个字段开始连续使用,索引才会生效。
联合索引会根据字段的顺序构造B+树,比如定义了(age, name),会先根据age排序,age相同是再根据name排序,若两者都相同则按照主键排序。
创建(A,B,C)
联合索引相当于同时创建了(A)
、(A,B)
和(A,B,C)
三个索引。
联合索引底层的存储结构是什么样的?
B+树结构,每个节点都存储了所有的索引列值作为key,并且按照定义时的顺序排序。
非叶子节点存储所有的索引列值,并且存储了指向子节点的指针。
叶子节点存储了所有索引列的值,并且存储了对应的主键值。
什么是最左前缀原则?
在联合索引中,必须从最左边的字段开始匹配,才能命中索引
范围查询后的列还能用作索引吗?
不能,范围查询会中断后面列的索引使用,因为索引是根据左前缀组织的,只有当左前缀的列值相同时,当前列值才有序。范围查询后,后续的字段不再有序。
为什么不从最左边开始查,就无法匹配呢?
因为联合索引组织的时候就是按照最左边的列进行排序的,最左边的列相同后,再依次按照后面的列值进行排序。如果不从左边开始查,无法判断查找范围。
什么是索引下推?
没有ICP的情况下,先在数据引擎层用索引查出来记录,WHERE过滤是在服务层进行。ICP,是指把WHERE条件尽可能下推到索引扫描阶段,在存储引擎层提前过滤掉不符合条件的记录,这样可以减少回表次数。