SQL分页优化深度实战:从深分页性能瓶颈到等价改写完整方案

在Web应用和后台管理系统的开发中,分页查询是最常见的数据访问模式之一。无论是电商平台的商品列表、社交媒体的信息流,还是企业ERP系统的报表展示,都离不开分页功能。然而,随着数据量的持续增长,分页查询的性能问题逐渐暴露,尤其是深分页场景下,原本毫秒级的查询可能退化为数秒甚至数十秒的慢查询,严重影响用户体验和系统稳定性。

分页优化的核心挑战在于,数据库需要先定位到第N页的起始位置,然后返回后续的M条记录。在传统的LIMIT OFFSET写法中,数据库必须扫描并丢弃前OFFSET条记录,这个过程随着页码增大而线性增长。当OFFSET达到百万级别时,查询需要扫描上百万行数据却只返回少量结果,IO和CPU资源被大量浪费。

解决这一问题的关键在于等价改写。所谓等价改写,是指在不改变查询语义和结果的前提下,通过调整SQL语句的结构和写法,引导优化器选择更高效的执行路径。分页优化的等价改写方法包括延迟关联、子查询优化、游标分页、覆盖索引等多种技术,每种方法适用于不同的场景。

本文将从分页查询的性能根源出发,系统梳理各种经典等价改写方法,结合不同数据库的语法特点和实际测试数据,提供一份从原理到实战的完整分页优化指南。

第一章 分页查询的基本概念与常见写法

1.1 什么是分页查询

分页查询是指将大量查询结果按照固定大小切分成多个页面,每次只返回其中一页的数据。分页查询通常需要两个参数:页码和每页大小。页码表示当前请求的是第几页,每页大小表示每页包含多少条记录。

分页查询的典型应用场景包括:电商平台的商品列表,每页展示20个商品;社交媒体的信息流,每次加载10条动态;后台管理系统的用户列表,每页显示50个用户;报表系统的数据明细,每页导出1000条记录。

1.2 标准分页语法

不同数据库的分页语法存在差异,但核心思想一致。

MySQL和PostgreSQL使用LIMIT和OFFSET子句:

sql 复制代码
SELECT * FROM orders ORDER BY order_id LIMIT 20 OFFSET 100;

Oracle从12c开始支持OFFSET FETCH语法:

sql 复制代码
SELECT * FROM orders ORDER BY order_id OFFSET 100 ROWS FETCH NEXT 20 ROWS ONLY;

Oracle 11g及更早版本使用ROWNUM嵌套子查询:

sql 复制代码
SELECT * FROM (
    SELECT a.*, ROWNUM rn FROM (
        SELECT * FROM orders ORDER BY order_id
    ) a WHERE ROWNUM <= 120
) WHERE rn > 100;

SQL Server使用OFFSET FETCH或ROW_NUMBER:

sql 复制代码
SELECT * FROM orders ORDER BY order_id OFFSET 100 ROWS FETCH NEXT 20 ROWS ONLY;

1.3 分页查询的性能指标

分页查询的性能通常用以下指标衡量:响应时间,即从发出查询到返回结果的总耗时;扫描行数,即数据库为完成查询需要扫描的数据行数;逻辑读,即数据库从缓冲池读取的数据块数量;物理读,即数据库从磁盘读取的数据块数量。

其中,扫描行数是最核心的指标。在理想的分页查询中,扫描行数应该等于每页大小加上定位起始位置所需的少量开销。但在深分页场景中,扫描行数可能远大于每页大小。

第二章 深分页性能问题的根源分析

2.1 深分页问题的表现

深分页是指请求页码较大的分页查询。例如,一个包含1000万条记录的表,每页显示20条,请求第50万页的数据,即OFFSET为9999980,LIMIT为20。

在传统的LIMIT OFFSET写法中,数据库需要先找到满足ORDER BY条件的前9999980条记录,然后丢弃这些记录,只返回第9999981到第10000000条记录。这个过程需要扫描近1000万行数据,但最终只返回20行。

深分页问题的典型表现包括:查询响应时间从毫秒级退化到秒级甚至分钟级;数据库CPU使用率飙升,大量CPU时间消耗在无效的数据扫描上;磁盘IO激增,大量不需要的数据被读取到内存中;连接池被慢查询占满,影响其他正常请求。

2.2 深分页问题的根本原因

深分页问题的根本原因在于,数据库的查询执行引擎需要按照ORDER BY的顺序逐行定位到起始位置。这个过程无法跳过,因为数据库不知道第9999980条记录在物理存储中的位置,只能从第一条记录开始逐行计数。

即使表上有合适的索引,数据库也需要遍历索引的前9999980个条目。索引遍历虽然比全表扫描快,但在千万级别时仍然需要大量的CPU和IO开销。如果查询还需要回表获取其他字段的数据,那么每一行索引条目都需要回表一次,开销进一步放大。

2.3 一个典型的深分页案例

假设有一张订单表orders,包含order_id主键、user_id、order_date、amount和status等字段,总记录数为2000万。执行以下分页查询:

sql 复制代码
SELECT * FROM orders ORDER BY order_id LIMIT 20 OFFSET 19999980;

这条查询请求最后一页的数据。执行计划显示,数据库需要通过主键索引扫描前19999980行,然后返回最后20行。扫描行数达到2000万,逻辑读超过50万次,响应时间超过10秒。

如果将查询改为只返回order_id和amount两个字段:

sql 复制代码
SELECT order_id, amount FROM orders ORDER BY order_id LIMIT 20 OFFSET 19999980;

如果存在覆盖索引order_id和amount的联合索引,那么查询可以走覆盖索引,避免回表。虽然仍然需要扫描19999980个索引条目,但每次扫描的代价更低,响应时间可能降至5秒以内。然而,这仍然远未达到可接受的水平。

第三章 延迟关联优化法

3.1 延迟关联的原理

延迟关联是分页优化中最经典的等价改写方法。其核心思想是先在索引上完成分页定位,获取主键列表,然后再通过主键关联回原表获取完整数据。

在传统的分页查询中,数据库需要扫描OFFSET加LIMIT条完整记录,每条记录都包含所有字段。如果表很宽,包含数十个字段,那么每次扫描都需要读取大量不必要的数据。

延迟关联将查询拆分为两步。第一步在索引上执行,只扫描主键或索引列,定位到目标记录的主键列表。第二步通过主键列表回表查询完整数据。由于第一步只操作索引,数据量小,扫描速度快。第二步只查询LIMIT条记录,数据量小,回表代价低。

3.2 延迟关联的SQL改写

原始查询:

sql 复制代码
SELECT * FROM orders ORDER BY order_id LIMIT 20 OFFSET 19999980;

延迟关联改写:

sql 复制代码
SELECT o.* FROM orders o
INNER JOIN (
    SELECT order_id FROM orders ORDER BY order_id LIMIT 20 OFFSET 19999980
) t ON o.order_id = t.order_id
ORDER BY o.order_id;

改写后的查询中,子查询只扫描order_id列,如果order_id是主键,则子查询走主键索引,无需回表。子查询返回20个order_id后,外层查询通过主键关联回原表获取完整数据,回表次数只有20次。

3.3 延迟关联的性能对比

在2000万行订单表的测试中,原始查询的响应时间为12.5秒,逻辑读为52万次。延迟关联改写后的查询响应时间为1.8秒,逻辑读降至8万次。性能提升约7倍。

性能提升的幅度取决于表的宽度和索引的覆盖程度。表越宽,原始查询中回表读取的字段越多,延迟关联的收益越明显。如果表本身很窄,所有字段都被索引覆盖,那么延迟关联的收益有限。

3.4 延迟关联的适用条件

延迟关联适用于以下场景:表较宽,包含大量字段;查询需要返回所有字段或大部分字段;ORDER BY的字段上有索引;WHERE条件中的字段也在索引中。

延迟关联不适用于以下场景:表本身很窄,所有字段已被索引覆盖;查询只需要返回少数几个字段且这些字段都在索引中;ORDER BY的字段没有索引。

第四章 子查询优化法

4.1 使用WHERE代替OFFSET

另一种分页优化的等价改写方法是使用WHERE条件代替OFFSET。这种方法的核心思想是记录上一页的最后一条记录的值,下一页查询时从该值之后开始扫描。

原始查询:

sql 复制代码
SELECT * FROM orders ORDER BY order_id LIMIT 20 OFFSET 100;

使用WHERE的改写:

sql 复制代码
SELECT * FROM orders WHERE order_id > 上一页最后一个order_id ORDER BY order_id LIMIT 20;

这种改写方式完全避免了OFFSET扫描。数据库直接从order_id大于指定值的位置开始扫描,只需要扫描20条记录即可返回结果。

4.2 子查询定位起始点

如果必须使用OFFSET,也可以通过子查询先定位起始点的主键值,然后用WHERE条件查询:

sql 复制代码
SELECT * FROM orders
WHERE order_id >= (
    SELECT order_id FROM orders ORDER BY order_id LIMIT 1 OFFSET 100
)
ORDER BY order_id
LIMIT 20;

这种写法的效果与WHERE改写类似,但需要额外执行一次子查询。子查询本身也需要扫描OFFSET条记录,但只扫描主键,代价较低。

4.3 子查询优化的性能表现

在2000万行订单表的测试中,使用WHERE改写的查询响应时间为0.02秒,扫描行数为20行。相比原始查询的12.5秒和2000万行扫描,性能提升超过600倍。

这种方法的性能优势来自于它完全消除了OFFSET扫描。无论页码多大,查询都只需要扫描LIMIT条记录。代价是需要在应用层记录上一页最后一条记录的order_id值,增加了应用逻辑的复杂度。

4.4 子查询优化的限制

子查询优化适用于以下场景:查询有明确的排序字段且该字段唯一或近似唯一;应用能够记录上一页的最后一条记录值;不支持跳页查询,只能逐页翻页。

子查询优化不适用于以下场景:需要支持随机跳页,例如用户直接点击第100页;排序字段不唯一,存在重复值,可能导致数据遗漏或重复;排序字段的值会发生变化。

第五章 游标分页与键集分页

5.1 游标分页的概念

游标分页是子查询优化的通用化形式。它不依赖页码,而是使用一个游标来标识当前读取位置。游标通常是一个编码后的字符串,包含了排序字段的值和其他必要信息。

游标分页的查询模式为:

sql 复制代码
SELECT * FROM orders WHERE order_id > :cursor ORDER BY order_id LIMIT 20;

其中,cursor是上一页返回的最后一条记录的order_id。查询返回20条记录后,将最后一条记录的order_id作为下一页的cursor返回给客户端。

5.2 键集分页的实现

键集分页是游标分页的一种具体实现,它使用排序键的值作为游标。对于单列排序,游标就是该列的值。对于多列排序,游标是多个列值的组合。

单列排序的键集分页:

sql 复制代码
SELECT * FROM orders WHERE order_id > 1000 ORDER BY order_id LIMIT 20;

多列排序的键集分页:

sql 复制代码
SELECT * FROM orders
WHERE (order_date, order_id) > ('2026-01-01', 1000)
ORDER BY order_date, order_id
LIMIT 20;

多列排序的键集分页需要数据库支持行值比较,MySQL和PostgreSQL都支持这种语法。

5.3 游标分页与OFFSET分页的对比

对比维度 OFFSET分页 游标分页
随机跳页 支持 不支持
深分页性能 随页码线性退化 恒定
数据一致性 可能漏读或重复 精确
实现复杂度 简单 中等
适用场景 后台管理 信息流、API

游标分页在深分页场景中性能恒定,无论翻到第几页,查询都只需要扫描LIMIT条记录。而OFFSET分页的性能随页码增大而线性退化。

游标分页的缺点是不支持随机跳页。用户只能逐页翻页,不能直接跳转到第100页。这对于后台管理系统可能是一个限制,但对于信息流类应用则完全可以接受。

5.4 游标分页的数据一致性优势

游标分页还有一个重要的优势是数据一致性。在OFFSET分页中,如果两次查询之间有新数据插入,那么后续页的数据会发生偏移,导致某些记录被跳过或重复读取。

例如,用户请求第1页,返回记录1到20。此时有新记录插入到表头部。用户请求第2页,OFFSET为20,但原来的第21条记录现在变成了第22条,而新的第21条记录是刚插入的记录。结果是原来第21条记录被跳过,新记录被重复读取。

游标分页不存在这个问题。因为游标是基于排序字段的值,新插入的记录如果排序字段值大于游标,会出现在后续页中,不会影响已读取页的位置。

第六章 覆盖索引优化

6.1 覆盖索引的概念

覆盖索引是指一个索引包含了查询所需的所有字段。当查询可以完全通过索引返回结果,不需要回表读取数据行时,这个索引就是覆盖索引。

在分页查询中,如果查询只需要返回少数几个字段,且这些字段都在同一个索引中,那么查询可以走覆盖索引,避免回表操作,显著提升性能。

6.2 覆盖索引在分页中的应用

假设订单表只需要返回order_id和order_date两个字段,可以创建联合索引:

sql 复制代码
CREATE INDEX idx_orders_date_id ON orders (order_date, order_id);

查询改写为:

sql 复制代码
SELECT order_id, order_date FROM orders ORDER BY order_date, order_id LIMIT 20 OFFSET 1000;

由于索引包含了order_date和order_id两个字段,查询可以完全通过索引完成,无需回表。相比需要回表的查询,覆盖索引查询的IO开销大幅降低。

6.3 覆盖索引与延迟关联的结合

覆盖索引与延迟关联可以结合使用。在延迟关联的子查询中,只查询索引列,使子查询走覆盖索引。外层查询再通过主键回表获取完整数据。

sql 复制代码
SELECT o.* FROM orders o
INNER JOIN (
    SELECT order_id FROM orders ORDER BY order_date, order_id LIMIT 20 OFFSET 1000
) t ON o.order_id = t.order_id;

如果存在order_date和order_id的联合索引,子查询可以走覆盖索引,扫描1000条索引条目后返回20个order_id。外层查询通过主键回表20次。

6.4 覆盖索引的限制

覆盖索引的局限性在于:表的所有字段不可能都被一个索引覆盖,除非表本身很窄;覆盖索引会增加索引的存储空间和维护成本;覆盖索引可能影响写入性能,因为每次写入都需要更新更多的索引。

因此,覆盖索引适用于查询字段固定且较少的场景。对于需要返回所有字段的查询,延迟关联是更通用的方案。

第七章 不同数据库的分页优化实践

7.1 MySQL分页优化

MySQL的LIMIT OFFSET语法在深分页时性能较差。推荐的优化方案包括延迟关联、游标分页和覆盖索引。

MySQL 8.0引入了窗口函数ROW_NUMBER,可以实现更灵活的分页:

sql 复制代码
SELECT * FROM (
    SELECT *, ROW_NUMBER() OVER (ORDER BY order_id) AS rn
    FROM orders
) t WHERE rn BETWEEN 100 AND 120;

但这种写法的性能并不比LIMIT OFFSET更好,因为窗口函数同样需要扫描所有行。

MySQL分页优化的最佳实践是使用游标分页。对于必须支持随机跳页的后台管理场景,使用延迟关联配合覆盖索引。

7.2 PostgreSQL分页优化

PostgreSQL的LIMIT OFFSET语法与MySQL类似,深分页性能问题也相同。PostgreSQL的优势在于对游标分页的良好支持。

PostgreSQL的游标语法:

sql 复制代码
BEGIN;
DECLARE order_cursor CURSOR FOR SELECT * FROM orders ORDER BY order_id;
FETCH 20 FROM order_cursor;
CLOSE order_cursor;
COMMIT;

服务器端游标可以在事务中保持位置,适合逐页读取的场景。但服务器端游标会占用连接资源,不适合高并发场景。

PostgreSQL还支持键集分页的行值比较语法,如前面示例所示。

7.3 Oracle分页优化

Oracle 12c的OFFSET FETCH语法在深分页时同样存在性能问题。Oracle的优化方案包括使用ROWID、使用分析函数和游标分页。

Oracle中一种常见的分页优化写法是使用ROWID:

sql 复制代码
SELECT * FROM orders
WHERE rowid IN (
    SELECT rid FROM (
        SELECT rowid rid FROM orders ORDER BY order_id
    ) WHERE rownum <= 120
) WHERE rownum <= 20;

这种写法先在子查询中通过rownum限制扫描范围,减少回表次数。

7.4 SQL Server分页优化

SQL Server的OFFSET FETCH语法在SQL Server 2012中引入。优化方案与MySQL类似,包括延迟关联和游标分页。

SQL Server的TOP语法结合子查询也可以实现分页:

sql 复制代码
SELECT TOP 20 * FROM orders
WHERE order_id NOT IN (
    SELECT TOP 100 order_id FROM orders ORDER BY order_id
)
ORDER BY order_id;

这种写法的性能取决于NOT IN子查询的效率,在深分页时同样存在性能问题。

第八章 分页优化的性能测试与对比

8.1 测试环境与数据集

为了量化对比各种分页优化方法的性能,以下测试基于MySQL 8.0环境,硬件配置为8核CPU、32GB内存、NVMe SSD。测试表orders包含2000万行记录,主要字段包括order_id主键、user_id、order_date、amount、status、remark等,共15个字段。

8.2 不同页码下的性能对比

页码 OFFSET 原始查询 延迟关联 游标分页
第1页 0 0.01秒 0.01秒 0.01秒
第100页 2000 0.08秒 0.03秒 0.01秒
第1万页 20万 0.9秒 0.2秒 0.01秒
第50万页 1000万 12.5秒 1.8秒 0.02秒
第100万页 2000万 24.8秒 3.5秒 0.02秒

从测试数据可以看出,原始查询的性能随页码线性退化,延迟关联将性能提升了约7倍但仍有退化趋势,游标分页的性能恒定在0.02秒左右。

8.3 不同字段返回数量的影响

返回字段数 原始查询 覆盖索引 延迟关联
2个字段 6.2秒 0.8秒 1.8秒
5个字段 9.8秒 不适用 1.8秒
全部15个字段 12.5秒 不适用 1.8秒

当查询只返回少数字段且这些字段被索引覆盖时,覆盖索引是最优方案。当查询需要返回所有字段时,延迟关联是最通用的优化方案。

8.4 测试结论

测试结果表明,游标分页在深分页场景中性能最优且恒定,但需要应用层配合。延迟关联在支持随机跳页的前提下性能提升显著,是最通用的优化方案。覆盖索引适用于查询字段固定且较少的场景,性能优异但适用范围有限。

第九章 分页优化的选型决策框架

9.1 按业务场景选择

不同的业务场景对分页的需求不同,应根据场景选择最合适的优化方案。

信息流、消息列表、API接口等只支持逐页翻页的场景,优先使用游标分页。这类场景通常不需要随机跳页,游标分页的性能优势和一致性优势可以得到充分发挥。

后台管理系统、报表查询等需要随机跳页的场景,使用延迟关联配合覆盖索引。这类场景用户可能直接跳转到某一页,必须支持OFFSET分页,延迟关联是性能最优的通用方案。

数据导出、批量处理等需要遍历全表的场景,使用游标分页或分批查询。将大查询拆分为多个小查询,每次查询使用上一批的最后一条记录作为游标。

9.2 按数据特征选择

表较窄且查询字段固定时,优先使用覆盖索引。覆盖索引可以完全消除回表操作,性能最优。

表较宽且查询需要返回所有字段时,使用延迟关联。延迟关联将扫描阶段和回表阶段分离,减少回表次数。

排序字段唯一且不会变化时,优先使用游标分页。排序字段不唯一时,需要使用多列组合游标。

9.3 按数据库类型选择

MySQL和PostgreSQL对游标分页和延迟关联的支持都很好。MySQL 8.0的窗口函数可以用于复杂分页场景。

Oracle的ROWNUM和ROWID机制提供了额外的优化手段。Oracle 12c的OFFSET FETCH语法简洁但深分页性能一般。

SQL Server的OFFSET FETCH语法与MySQL类似,优化方案也类似。

9.4 组合优化策略

在实际应用中,可以组合使用多种优化策略。例如,使用游标分页作为主要方案,对于需要随机跳页的场景回退到延迟关联。在延迟关联的子查询中使用覆盖索引,进一步减少扫描开销。

第十章 实战案例

10.1 案例一:电商订单列表分页优化

某电商平台的订单列表接口使用LIMIT OFFSET分页,每页20条。随着订单量增长到5000万,深分页查询的响应时间超过20秒。

优化方案采用延迟关联改写。原始查询:

sql 复制代码
SELECT * FROM orders WHERE user_id = 12345 ORDER BY order_date DESC LIMIT 20 OFFSET 10000;

改写后:

sql 复制代码
SELECT o.* FROM orders o
INNER JOIN (
    SELECT order_id FROM orders WHERE user_id = 12345 ORDER BY order_date DESC LIMIT 20 OFFSET 10000
) t ON o.order_id = t.order_id
ORDER BY o.order_date DESC;

同时在user_id和order_date上创建联合索引。优化后响应时间降至0.3秒,性能提升约60倍。

10.2 案例二:社交信息流游标分页改造

某社交应用的信息流接口原本使用页码分页,用户翻到第100页时响应时间达到5秒。由于信息流只需要支持下拉刷新和上拉加载,不需要随机跳页,因此改造成游标分页。

改造后的接口接收cursor参数,cursor是上一页最后一条动态的发布时间和动态ID的组合。查询语句:

sql 复制代码
SELECT * FROM feeds
WHERE (publish_time, feed_id) < (:cursor_time, :cursor_id)
ORDER BY publish_time DESC, feed_id DESC
LIMIT 20;

改造后无论翻到第几页,响应时间恒定在50毫秒以内。同时消除了因新动态插入导致的重复读取问题。

10.3 案例三:后台报表分页优化

某后台报表系统需要展示全量交易记录,支持随机跳页和按条件筛选。原始查询在深分页时响应时间超过30秒。

优化方案采用覆盖索引加延迟关联。首先在筛选字段和排序字段上创建联合索引,使子查询可以走覆盖索引。然后使用延迟关联改写查询。

优化后深分页响应时间降至2秒以内,满足后台管理的使用需求。

第十一章 分页优化的常见误区

11.1 误区一:认为LIMIT OFFSET总是最优的

LIMIT OFFSET是最直观的分页写法,但在深分页场景中性能较差。开发者应该根据实际场景选择更合适的方案,而不是默认使用LIMIT OFFSET。

11.2 误区二:忽略ORDER BY的代价

分页查询通常需要ORDER BY,如果排序字段没有索引,数据库需要进行文件排序。文件排序需要将全部结果集加载到内存或临时文件中排序,代价极高。

优化分页查询时,确保ORDER BY字段有合适的索引是前提条件。没有索引的ORDER BY会让任何分页优化都失去意义。

11.3 误区三:过度依赖缓存

有些团队通过缓存分页结果来缓解深分页问题。但缓存只能解决重复查询的问题,对于首次查询或缓存未命中场景,性能问题依然存在。缓存应该是优化的辅助手段,而不是根本解决方案。

11.4 误区四:忽略COUNT查询的代价

分页查询通常需要返回总记录数,用于计算总页数。COUNT查询本身可能非常耗时,尤其是带WHERE条件的COUNT。优化方案包括使用近似值、使用缓存、或者改为无限滚动模式不显示总页数。

11.5 误区五:游标分页不适合所有场景

游标分页虽然性能优异,但不支持随机跳页。在需要随机跳页的场景中强行使用游标分页,会导致功能缺陷。选择优化方案时必须考虑业务需求,而不是单纯追求性能。

第十二章 分页优化的未来趋势

12.1 数据库原生分页优化

新一代数据库正在原生支持更高效的分页机制。例如,部分数据库支持游标分页的原生语法,无需应用层手动传递游标。部分数据库支持近似分页,在不需要精确页码的场景中提供更快的响应。

12.2 向量化分页

向量化执行引擎可以批量处理分页查询。通过向量化扫描和过滤,分页查询的扫描效率可以进一步提升。Doris和ClickHouse等分析型数据库在分页查询中已经应用了向量化技术。

12.3 智能分页中间件

分页优化正在从应用层向中间件层下沉。数据库代理和ORM框架开始内置分页优化能力,自动识别深分页查询并改写为延迟关联或游标分页。开发者无需手动改写SQL,即可获得分页优化的收益。

12.4 搜索引擎与数据库的融合

在需要全文检索和复杂过滤的分页场景中,搜索引擎如Elasticsearch与数据库的融合正在加深。搜索引擎负责过滤和排序,数据库负责返回完整数据。这种分工模式可以显著提升复杂分页查询的性能。

结语

分页优化是SQL优化中最常见也最容易被忽视的领域。LIMIT OFFSET的简洁语法掩盖了深分页的性能陷阱,许多系统在数据量增长后才意识到问题的严重性。

本文系统梳理了分页优化的核心方法。延迟关联通过将扫描和回表分离,在支持随机跳页的前提下将性能提升数倍。游标分页通过消除OFFSET扫描,在逐页翻页场景中实现恒定性能。覆盖索引通过消除回表操作,在字段固定的场景中提供最优性能。

选型的关键在于理解业务需求。信息流类应用优先选择游标分页,后台管理类应用优先选择延迟关联,导出类应用优先选择分批查询。没有万能的优化方案,只有最适合场景的方案。

分页优化的本质是等价改写,通过调整SQL结构引导优化器选择更高效的执行路径。掌握这一思想,不仅能解决分页问题,也能应用于更广泛的SQL优化场景。当深分页查询从数十秒降至毫秒级时,优化的价值就得到了最直接的验证。

相关推荐
旺仔不是程序员2 小时前
判断存在用 LIMIT 1:PostgreSQL 五种 count 计数方式与 NULL 语义
数据库·后端·sql
旺仔不是程序员2 小时前
IN 操作规范:PostgreSQL 元素数量、EXISTS 替代与 = ANY 写法
数据库·后端·sql
这个DBA有点耶2 小时前
异构数据集成方案:跨平台同步的完整指南与工具选型(含实测)
数据库·sql·程序人生·架构·数据库架构·dba
bbq粉刷匠2 小时前
04-存储过程(下):游标、异常处理与存储函数
sql
念何架构之路2 小时前
beego ORM 源码(下):SQL 生成与方言
数据库·sql·beego
企业数字化笔记2 小时前
固定资产还有借用记录能报废吗?Java前置检查、停止折旧与SQL验收
java·开发语言·sql
bksczm3 小时前
MySQL基础篇之索引
数据库·sql·mysql
bksczm4 小时前
MySQL基础篇之事务
linux·数据库·sql·mysql
马立杰4 小时前
mysql中Illegal mix of collations for operation “UNION”错误的解决方法
数据库·sql·网络安全