什么是MySQL的深度分页?

MySQL的深度分页是指在数据量较大的情况下,从数据库中获取位于较高页码的数据。例如,当数据表中有大量记录时,要获取接近末尾的几页数据,这种情况就称为深度分页。

深度分页的性能问题

当使用常规的分页查询(LIMITOFFSET)进行深度分页时,MySQL会出现性能问题。这是因为MySQL在执行分页查询时,会先扫描并跳过 OFFSET 指定数量的记录,然后再返回 LIMIT 指定数量的记录。
举个例子,假设有一个包含一百万条记录的表,查询 LIMIT 900000, 1000 的执行过程如下:
示例表字段如下,添加了name字段索引
notion image
notion image
notion image
MySQL执行上述查询时,会先扫描前900000条数据,然后再返回第900001到901000条数据。作为对比,可以看下直接查询前1000条数据的执行时间。
notion image
这种方式在数据量大时会导致以下问题:
  1. 大量的磁盘I/O操作:随着 OFFSET 值的增大,MySQL需要扫描并跳过越来越多的记录,这会导致大量的磁盘读取操作。
  1. 较慢的查询响应时间:因为MySQL需要处理大量不必要的数据,这会导致查询时间显著增加。
  1. 内存消耗增加:处理大数据集时,MySQL可能需要更多的内存来管理临时数据。
 

深度分页的解决方案

为了优化深度分页的性能,可以使用一些替代方法来避免直接使用 OFFSET,以下是一些常见的解决方案及其示例代码:

1. 基于索引的分页

通过索引字段(如主键)来进行分页查询,这种方法避免使用 OFFSET,只需记住上一次查询的最后一个索引值。
在这个例子中,子查询先找到第900000行的 id 值,然后通过该 id 值进行后续的分页查询。
notion image

2. 延迟关联(Deferred Join)

先通过索引字段查询出所需的主键范围,然后再进行关联查询。这种方法适用于有大量列的表,可以减少不必要的列扫描。
通过子查询仅获取所需的主键,再通过主键进行关联查询,提升性能。
notion image
下面这个sql例子更能说明延迟关联的性能优化
notion image

3. 覆盖索引(Covering Indexes)

如果查询涉及的字段都在索引中,可以使用覆盖索引来避免访问表数据。
使用覆盖索引,查询只从索引中获取数据,减少I/O操作。

4. 游标分页

通过自定义的游标来跟踪分页状态,避免重复计算 OFFSET
每次查询后更新 @offset 的值,记录当前页最后一行的 id,下一次查询时从该 id 开始。
notion image
 
通过优化分页查询,可以显著提升深度分页的性能,避免不必要的磁盘I/O操作和查询延迟。在实际应用中,根据数据结构和查询需求选择合适的优化方法,有助于提升系统的整体性能。
优化sql的过程中,可以使用EXPLAIN 来帮助分析和优化SQL查询

使用 EXPLAIN

EXPLAIN提供了有关查询计划的信息,包括如何访问表、使用的索引、估计的行数等。
要使用EXPLAIN,只需在查询前加上EXPLAIN关键字。例如:

结果解释

执行EXPLAIN后,会返回一张表,包含以下列:
  • id: 查询中每个选择查询的标识符。
  • select_type: 查询的类型,例如SIMPLE(简单查询)、PRIMARY(主查询)、SUBQUERY(子查询)等。
  • table: 查询的表。
  • partitions: 匹配的分区。
  • type: 联接类型,表示表的访问方式,例如ALL(全表扫描)、index(索引扫描)、range(范围扫描)、ref(非唯一索引扫描)、eq_ref(唯一索引扫描)等。
  • possible_keys: 可能用于查询的索引。
  • key: 实际使用的索引。
  • key_len: 使用的索引的长度。
  • ref: 哪个列或常量与索引一起使用。
  • rows: MySQL 估计必须要读取的行数。
  • filtered: 经过过滤条件后返回的行百分比。
  • Extra: 附加信息,例如Using where(使用了WHERE子句)、Using index(使用了覆盖索引)等。
 
对于type,联接类型,不同的访问方式对查询性能有显著影响。
  • ALL:性能最差,应尽量避免。
  • index:优于全表扫描,但仍需优化。
  • range:性能较好,适用于范围查询。
  • ref:性能很好,适用于非唯一索引查询。
  • eq_ref:性能最佳,适用于唯一索引或主键查询。
 

示例

假设有一个名为users的表,结构如下:
插入一些数据:
执行查询并使用EXPLAIN分析:
返回结果可能如下:
id
select_type
table
partitions
type
possible_keys
key
key_len
ref
rows
filtered
Extra
1
SIMPLE
users
NULL
ref
idx_username
idx_username
767
const
1
100.00
Using where

解释结果

  • id: 1,表示这是查询中的第一个且唯一的查询。
  • select_type: SIMPLE,表示这是一个简单查询。
  • table: users,表示查询的表是users
  • partitions: NULL,表示未使用分区。
  • type: ref,表示使用了非唯一索引扫描。
  • possible_keys: idx_username,表示可能使用的索引是idx_username
  • key: idx_username,表示实际使用的索引是idx_username
  • key_len: 767,表示索引的长度。
  • ref: const,表示使用了常量进行匹配。
  • rows: 1,表示MySQL估计需要读取1行记录。
  • filtered: 100.00,表示经过过滤条件后返回的行百分比是100%。
  • Extra: Using where,表示使用了WHERE子句进行过滤。
通过分析EXPLAIN的结果,可以了解查询的执行计划,并针对可能的性能问题进行优化,例如添加或修改索引、重写查询等。
 
 
0.2升超迷你主机上手体验Jetcache 缓存的一些记录
Loading...