MST
星途 面试题库

面试题:MySQL索引底层原理及优化策略在高并发场景下的应用

MySQL的InnoDB存储引擎使用B + 树作为索引结构,请深入阐述B + 树索引的底层原理,以及在高并发读写场景下,如何根据B + 树的特性对索引进行优化,以提升系统的整体性能,举例说明优化前后的性能差异。
18.3万 热度难度
数据库MySQL

知识考点

AI 面试

面试题答案

一键面试

B + 树索引底层原理

  1. 数据结构特点
    • 节点类型:B + 树由内部节点(非叶子节点)和叶子节点组成。内部节点仅存储索引键值,用于引导数据查询路径;叶子节点存储实际数据记录以及指向相邻叶子节点的指针,形成一个双向链表结构,方便范围查询。
    • 节点分裂:当一个节点存储的键值达到其容量上限时,会进行节点分裂,将一半键值移到新节点,并在父节点中插入一个连接两个节点的键值。
    • 高度平衡:B + 树通过节点分裂和合并等操作,保证树的高度平衡,使得从根节点到叶子节点的最长路径和最短路径长度之差不超过1,从而确保查询效率稳定。
  2. 查询过程
    • 从根节点开始,根据查询条件中的键值与节点中的键值进行比较,决定向下的搜索路径。
    • 持续在内部节点中进行这种比较和路径选择,直到到达叶子节点。
    • 在叶子节点中通过二分查找或顺序查找(如果没有排序)找到符合条件的数据记录。对于范围查询,利用叶子节点的双向链表结构,可以快速遍历获取范围内的数据。

高并发读写场景下基于B + 树特性的索引优化

  1. 前缀索引优化
    • 原理:对于较长的字符串类型字段,使用前缀索引,即仅对字段的前几个字符建立索引。因为B + 树索引存储键值会占用空间,较短的键值可以在一个节点中存储更多的索引项,减少树的高度,提升查询性能。
    • 举例:假设一个表存储用户的邮箱地址,邮箱地址一般较长。如果对整个邮箱地址建立索引,会占用较多空间。可以对邮箱地址的前10个字符建立前缀索引。例如,原SQL查询为SELECT * FROM users WHERE email = 'example@domain.com';,优化后创建前缀索引CREATE INDEX idx_email ON users (email(10));
  2. 覆盖索引优化
    • 原理:使查询所需要的数据都能从索引中获取,而不需要回表操作。B + 树叶子节点存储了索引键值和指针,如果查询列都包含在索引中,就可以直接从索引中获取数据,减少磁盘I/O。
    • 举例:假设有表orders,包含字段order_idcustomer_idorder_amount。查询SELECT order_id, order_amount FROM orders WHERE customer_id = 123;,可以创建覆盖索引CREATE INDEX idx_customer_amount ON orders (customer_id, order_id, order_amount);,这样查询时直接从索引获取数据,无需再到数据页获取。
  3. 索引顺序优化
    • 原理:根据高并发场景下的查询模式,调整索引列的顺序。在B + 树中,索引的顺序影响查询时的路径选择。如果查询经常以某些列作为条件,将这些列放在索引的前列,能更快定位数据。
    • 举例:对于查询SELECT * FROM products WHERE category = 'electronics' AND brand = 'Apple';,创建索引CREATE INDEX idx_product ON products (category, brand);,先根据category定位,再根据brand进一步筛选,比先按brand后按category的索引顺序更高效。

优化前后性能差异举例

  1. 前缀索引
    • 优化前:假设表user_info有字段user_name(字符串类型,平均长度50),对user_name全字段建立索引。查询SELECT * FROM user_info WHERE user_name = 'long_user_name_example';,由于索引键值较长,节点存储的索引项少,树的高度可能较高,查询需要多次磁盘I/O,假设查询时间为100ms。
    • 优化后:对user_name前10个字符建立前缀索引。同样的查询,由于索引键值变短,节点存储索引项增多,树的高度降低,查询时间可能缩短到30ms。
  2. 覆盖索引
    • 优化前:表employee有字段employee_iddepartmentsalary。查询SELECT employee_id, salary FROM employee WHERE department = 'HR';,没有覆盖索引时,先通过索引找到department = 'HR'的记录指针,再回表获取employee_idsalary,假设回表操作导致查询时间为80ms。
    • 优化后:创建覆盖索引CREATE INDEX idx_department_salary ON employee (department, employee_id, salary);,查询直接从索引获取数据,查询时间可能减少到20ms。
  3. 索引顺序优化
    • 优化前:表book有字段authorcategorytitle。查询SELECT * FROM book WHERE category = 'fiction' AND author = 'John Doe';,创建索引CREATE INDEX idx_author_category ON book (author, category);,查询时先按author筛选,可能需要遍历较多数据才能找到符合category条件的记录,假设查询时间为120ms。
    • 优化后:创建索引CREATE INDEX idx_category_author ON book (category, author);,先按category筛选,能更快定位到符合条件的数据,查询时间可能缩短到40ms。