前言
本专栏为150道MySQL大厂高频面试题讲解分析,这些面试题都是通过MySQL8.0官方文档和阿里巴巴官方手册还有一些大厂面试官提供的资料。
MySQL应用广泛,在多个开发语言中都处于重要地位,所以最好都要掌握MySQL的精华面试题,这也是面试官最喜欢问的,现在面试官在面试的时候更关心的是某个技术点的深度,所以专栏的内容也会从底层开始讲解,本专栏会一直不断的进行更新,欢迎大家一起交流学习。
讲义
假设有一个表index_demo,表中有2个INT类型的列,1个CHAR(1)类型的列,c1列为主键:
CREATE TABLE index_demo(c1 INT,c2 INT,c3 CHAR(1),PRIMARY KEY(c1)) ;
index_demo表的简化的行格式示意图如下:
我们只在示意图里展示记录的这几个部分:
record_type:
表示记录的类型, 0是普通记录、 2是最小记录、 3 是最大记录、1是B+树非叶子节点记录。next_record:
表示下一条记录的相对位置,我们用箭头来表明下一条记录。各个列的值:
这里只记录在 index_demo 表中的三个列,分别是 c1 、 c2 和 c3 。其他信息:
除了上述3种信息以外的所有信息,包括其他隐藏列的值以及记录的额外信息。
将其他信息
项暂时去掉并把它竖起来的效果就是这样:
把一些记录放到页里的示意图就是(这里一页就是一个磁盘块,代表一次IO):
name age sex
MySQL InnoDB的默认的页大小是16KB
,因此数据存储在磁盘中,可能会占用多个数据页。如果各个页中的记录没有规律,我们就不得不依次遍历所有的数据页。如果我们想快速的定位到需要查找的记录在哪些数据页中
,我们可以这样做 :
- 下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值
- 给所有的页建立目录项
以页28
为例,它对应目录项2
,这个目录项中包含着该页的页号28
以及该页中用户记录的最小主键值 5
。我们只需要把几个目录项在物理存储器上连续存储(比如:数组),就可以实现根据主键值快速查找某条记录的功能了。比如:查找主键值为 20 的记录,具体查找过程分两步:
- 先从目录项中根据二分法快速确定出
主键值为20的记录在目录项3中
(因为 12 ≤ 20 < 209 ),对应页9
。 - 再到页9中根据二分法快速定位到主键值为 20 的用户记录。
至此,针对数据页做的简易目录就搞定了。这个目录有一个别名,称为索引
。
InnoDB中的索引方案
我们新分配一个编号为30的页来专门存储目录项记录
,页10、28、9、20专门存储用户记录
:
目录项记录和普通的用户记录的不同点:
- 目录项记录 的 record_type 值是1,而 普通用户记录 的 record_type 值是0。
- 目录项记录只有主键值和页的编号两个列,而普通的用户记录的列是用户自己定义的,包含很多列,另外还有InnoDB自己添加的隐藏列。
现在查找主键值为 20 的记录,具体查找过程分两步:
- 先到页30中通过二分法快速定位到对应目录项,因为 12 ≤ 20 < 209 ,就是页9。
- 再到页9中根据二分法快速定位到主键值为 20 的用户记录。
更复杂的情况如下:
我们生成了一个存储更高级目录项的 页33 ,这个页中的两条记录分别代表页30和页32,如果用户记录的主键值在 [1, 320)
之间,则到页30中查找更详细的目录项记录,如果主键值 不小于320 的话,就到页32中查找更详细的目录项记录。这个数据结构,它的名称是 B+树 。
B+树索引的数据结构
B+树是一种多路平衡搜索树,它的特点包括:
- 多路平衡:多路指的是树中每个节点可以有多个子节点,平衡则是指树的高度相对均衡,以确保查找效率。
- 节点结构:在B+树中,非叶子节点只存储索引信息(即键值),而叶子节点则存储实际的数据记录。所有叶子节点都在同一层,且叶子节点之间通过链表相连。
- 磁盘友好:B+树的设计考虑了磁盘读写效率,每个节点的大小通常等于一个磁盘页的大小(如InnoDB中的默认页大小为16KB)。这样,每次磁盘I/O操作可以读取或写入一个节点的全部内容,减少了磁盘访问次数。
B+树索引的工作原理
查找过程:
- 当执行查询操作时,MySQL会根据查询条件在B+树中查找对应的记录。
- 首先,从根节点开始,根据键值比较确定查找方向,进入相应的子节点。
- 重复上述过程,直到到达叶子节点。
- 在叶子节点中,通过线性查找(或二分查找,如果叶子节点内的记录已排序)找到符合条件的记录。
插入与删除:
- 插入新记录时,MySQL会首先找到应该插入的叶子节点。
- 如果叶子节点有空闲空间,则直接插入;否则,会进行节点分裂,将部分记录分裂到新的节点中,并更新父节点的索引信息。
- 删除记录时,MySQL会找到包含该记录的叶子节点,并将其删除。
- 如果删除后叶子节点中的记录数过少(低于某个阈值),则可能会进行节点合并或重新平衡操作,以保持B+树的平衡性。
案例解析
假设有一个名为employees的表,包含以下字段:id(员工ID,主键)、name(员工姓名)、age(员工年龄)、department(员工所属部门)。
创建表并添加B+树索引:
CREATE TABLE employees (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100),
age INT,
department VARCHAR(100)
);
-- 为name、age、department字段添加B+树索引
CREATE INDEX idx_name ON employees(name);
CREATE INDEX idx_age ON employees(age);
CREATE INDEX idx_department ON employees(department);
插入数据:
INSERT INTO employees (name, age, department) VALUES
('Alice', 30, 'HR'),
('Bob', 25, 'IT'),
('Charlie', 35, 'Finance'),
('David', 28, 'IT'),
('Eve', 22, 'HR');
使用索引进行查询:
等值查询:查询名字为"Alice"的员工信息。
SELECT * FROM employees WHERE name = 'Alice';
MySQL会利用idx_name索引快速定位到名字为"Alice"的记录。
范围查询:查询年龄在25到30岁之间的员工信息。
SELECT * FROM employees WHERE age BETWEEN 25 AND 30;
MySQL会利用idx_age索引快速定位到年龄在指定范围内的记录。
排序查询:按照年龄升序查询所有员工信息。
SELECT * FROM employees ORDER BY age;
MySQL会利用idx_age索引进行快速排序。
B+树索引的优势与注意事项
优势:
- 查询效率高:由于B+树的高度相对较低,查找、插入和删除操作的效率都较高。
- 磁盘读写效率高:每个节点的大小等于磁盘页的大小,减少了磁盘访问次数。
- 支持范围查询和排序:B+树的叶子节点通过链表相连,支持高效的范围查询和排序操作。
注意事项:
- 索引维护成本:索引需要占用额外的存储空间,并在插入、删除和更新操作时进行维护。
- 选择合适的索引列:应根据查询需求选择合适的索引列,避免创建冗余索引。
- 索引失效:在某些情况下(如使用函数、隐式类型转换等),索引可能会失效,导致查询性能下降。