MST
星途 面试题库

面试题:MySQL基本配置完成后,如何基于性能分析对查询执行计划进行深度优化?

给出一个复杂的多表关联查询SQL示例,完成MySQL基本配置后,通过性能分析工具(如EXPLAIN等)分析该查询的执行计划,详细阐述如何根据分析结果优化索引结构、调整查询语句逻辑以及利用MySQL的优化提示(hint)等手段,以达到最优的查询性能。并且说明在不同版本的MySQL中,这些优化手段可能存在的差异。
37.4万 热度难度
数据库MySQL

知识考点

AI 面试

面试题答案

一键面试

1. 复杂多表关联查询SQL示例

假设有三个表:orders(订单表)、order_items(订单项表)、products(产品表)。 orders表结构:

CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT,
    order_date DATE
);

order_items表结构:

CREATE TABLE order_items (
    item_id INT PRIMARY KEY,
    order_id INT,
    product_id INT,
    quantity INT,
    FOREIGN KEY (order_id) REFERENCES orders(order_id)
);

products表结构:

CREATE TABLE products (
    product_id INT PRIMARY KEY,
    product_name VARCHAR(255),
    price DECIMAL(10, 2)
);

查询每个客户购买的产品名称、数量和总金额,SQL语句如下:

SELECT 
    o.customer_id,
    p.product_name,
    oi.quantity,
    oi.quantity * p.price AS total_amount
FROM 
    orders o
JOIN 
    order_items oi ON o.order_id = oi.order_id
JOIN 
    products p ON oi.product_id = p.product_id;

2. 使用EXPLAIN分析执行计划

在MySQL中,使用EXPLAIN关键字分析上述查询的执行计划:

EXPLAIN SELECT 
    o.customer_id,
    p.product_name,
    oi.quantity,
    oi.quantity * p.price AS total_amount
FROM 
    orders o
JOIN 
    order_items oi ON o.order_id = oi.order_id
JOIN 
    products p ON oi.product_id = p.product_id;

执行结果会包含以下关键信息:

  • id:表示查询中每个SELECT语句的标识符,相同id表示是同一层次的查询。
  • select_type:查询类型,常见的有SIMPLE(简单查询,不包含子查询或联合查询)。
  • table:表示当前行涉及的表。
  • partitions:分区信息(如果表是分区表)。
  • type:连接类型,常见的有ALL(全表扫描)、index(索引扫描)、range(范围扫描)、ref(非唯一索引扫描,通过索引找到匹配的行)等。理想情况下,我们希望连接类型尽可能为indexrangeref,避免ALL
  • possible_keys:可能使用的索引。
  • key:实际使用的索引。如果为NULL,则表示没有使用索引。
  • key_len:使用的索引长度。
  • ref:显示哪些列或常量被用来和索引比较。
  • rows:估计需要扫描的行数。
  • filtered:估计返回结果的行数占扫描行数的百分比。

3. 根据分析结果优化索引结构

  • 添加索引
    • 如果EXPLAIN结果中typeALL,且possible_keysNULL,说明没有使用索引,需要添加合适的索引。例如,在order_items表的order_id列和product_id列上添加索引:
CREATE INDEX idx_order_id ON order_items(order_id);
CREATE INDEX idx_product_id ON order_items(product_id);
- 在`orders`表的`customer_id`列上添加索引,以便在查询客户相关信息时更快:
CREATE INDEX idx_customer_id ON orders(customer_id);
  • 复合索引:如果查询涉及多个列的条件,可以考虑创建复合索引。例如,如果经常按照customer_idorder_date查询订单,可以创建复合索引:
CREATE INDEX idx_customer_date ON orders(customer_id, order_date);

注意复合索引的顺序,按照最常用的查询条件在前的原则。

4. 调整查询语句逻辑

  • 避免使用子查询:子查询有时会导致性能问题,可以将其改写为连接查询。例如,如果原始查询中有子查询获取满足特定条件的订单ID,然后再查询相关订单项和产品,可以将其合并为一个连接查询。
  • 合理使用JOIN类型:确保使用正确的JOIN类型(INNER JOINLEFT JOINRIGHT JOIN等)。如果只需要获取满足连接条件的记录,使用INNER JOIN通常性能更好;如果需要保留左表(或右表)的所有记录,使用LEFT JOIN(或RIGHT JOIN)。

5. 利用MySQL的优化提示(hint)

  • SQL_NO_CACHE:如果数据变化频繁,且不希望查询结果被缓存,可以使用SQL_NO_CACHE提示。例如:
SELECT /*+ SQL_NO_CACHE */
    o.customer_id,
    p.product_name,
    oi.quantity,
    oi.quantity * p.price AS total_amount
FROM 
    orders o
JOIN 
    order_items oi ON o.order_id = oi.order_id
JOIN 
    products p ON oi.product_id = p.product_id;
  • FORCE INDEX:如果希望MySQL使用特定的索引,可以使用FORCE INDEX提示。例如,强制使用idx_order_id索引:
SELECT /*+ FORCE INDEX(oi idx_order_id) */
    o.customer_id,
    p.product_name,
    oi.quantity,
    oi.quantity * p.price AS total_amount
FROM 
    orders o
JOIN 
    order_items oi ON o.order_id = oi.order_id
JOIN 
    products p ON oi.product_id = p.product_id;

6. 不同版本MySQL优化手段的差异

  • 索引优化:在较新的MySQL版本中,索引算法和优化器对索引的使用更加智能。例如,MySQL 8.0引入了降序索引,在某些查询场景下可以更高效地处理按降序排序的查询。旧版本则不支持降序索引,对于降序排序的查询可能需要额外的排序操作。
  • 优化提示:不同版本对优化提示的支持可能不同。一些较新的优化提示可能在旧版本中不被识别或支持。例如,某些高级的查询优化提示是在MySQL 5.7或更高版本中才引入的。
  • 执行计划:优化器在不同版本中对执行计划的生成方式有所改进。较新版本可能会更好地估计查询成本,从而生成更优的执行计划。例如,MySQL 8.0的优化器在处理复杂查询时,能够更准确地判断索引的使用情况,相比旧版本可以避免一些不必要的全表扫描。