一、MRR优化概述
MRR,全称Multi-Range Read Optimization,直译为多范围读取优化,是MySQL中一种用于提高索引查询性能的技术。MRR通过减少随机磁盘访问次数,将随机IO转换为顺序IO,从而提高数据读取的效率。它特别适用于包含范围条件(如BETWEEN、<、>等)的查询,以及需要通过辅助索引访问表数据的场景。
在InnoDB中表数据是通过聚集索引组织的。当基于辅助索引的范围查询时,需要先通过辅助索引找到对应的主键值,再通过主键值回表查询完整的行数据。这种回表会产生大量的随机磁盘I/O,尤其是在处理大表时,随机I/O的性能瓶颈尤为明显。MRR优化正是为了解决这一问题提出。
MRR优化的核心思想是将多个范围查询中的随机磁盘I/O转换为顺序磁盘I/O,从而提高查询性能。
optimizer_switch
系统变量中的mrr
和mrr_cost_based
标志来控制MRR优化的使用。mrr_cost_based
设置为ON时,优化器会根据成本来决定是否使用MRR;设置为OFF时,则强制使用MRR(但通常不建议这样做,因为优化器在大多数情况下都是正确的)。MRR优化充分利用了磁盘预读机制。当客户端请求读取某一页数据时,磁盘预读功能会预测并提前读取相邻的几页数据到内存缓冲区中。由于MRR将随机访问转换为顺序访问,磁盘预读机制能够更好地发挥作用,减少磁盘寻道时间和旋转延迟,进一步提升读取效率。
局部性原理是MRR优化的另一个理论基础。时间局部性表明,如果某个数据项被访问,那么在不久的将来它可能再次被访问;空间局部性表明,一旦某个数据项被访问,那么其附近的数据项也可能很快被访问。MRR通过顺序访问数据,使得数据访问更加符合局部性原理,从而提高了缓存命中率,减少了磁盘访问次数。
MRR优化适用于基于范围扫描和等值连接的操作中尤为有效。但是,并非所有查询都能从MRR优化中受益。如,当查询完全基于索引元组中的信息(即使用覆盖索引)时,MRR优化就没有必要,因为此时无需回表访问基表数据。
此外,MySQL默认开启MRR优化,但是否真正使用MRR由优化器决定。优化器会根据查询的成本(如IO成本、CPU成本等)来决定是否采用MRR优化。用户可以通过调整optimizer_switch
系统变量中的mrr
和mrr_cost_based
标志来控制MRR优化的使用。
一个为orders
的表结构如下:
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
customer_id INT NOT NULL,
order_date DATE NOT NULL,
total_amount DECIMAL(10, 2) NOT NULL,
INDEX idx_customer_date (customer_id, order_date)
) ENGINE=InnoDB;
表中,customer_id
和order_date
上有一个联合索引idx_customer_date
。想要查询某个特定客户在指定日期范围内的所有订单,SQL语句:
SELECT * FROM orders WHERE customer_id = 123 AND order_date BETWEEN '2023-01-01' AND '2023-12-31';
idx_customer_date
来定位满足customer_id = 123
和order_date BETWEEN '2023-01-01' AND '2023-12-31'
条件的索引元组。customer_id
、order_date
以及对应的主键值(id
)。orders
表的基表部分,检索出完整的订单数据行。