帮你快速理解、总结文档立即下载

Adaptive Ordering Index

最近更新时间:2026-08-20 15:23:31
我的收藏

功能介绍

Adaptive Ordering Index(自适应排序索引)用于降低 ORDER BYGROUP BY 配合 LIMIT 查询因统计信息偏差而选错排序索引的风险。
prefer_ordering_index 开启时,优化器可能优先使用能够直接提供目标顺序的现有索引,以避免 filesort,并期望扫描少量记录后便可满足 LIMIT。例如:
SELECT *
FROM messages
WHERE room_id = 100
ORDER BY create_time
LIMIT 10;
如果表上存在以 create_time 开头的索引,优化器可能按时间顺序扫描该索引。但当 room_id = 100 对应的数据在索引中非常稀疏时,查询需要跳过大量不符合条件的记录,实际代价可能远高于“先按过滤条件访问,再排序”的计划。
Adaptive Ordering Index 会在执行期间检查排序索引的实际效果。如果实际扫描进度明显差于优化器预期,并且结果尚未发送给客户端,系统会自动放弃本轮执行,重新优化并执行查询;第二轮将不再优先选择该排序索引。整个过程对应用透明,应用只会收到最终结果。
该功能具有以下特点:
不创建新索引:功能只在已有索引访问计划和其他执行计划之间自适应选择,不会创建、修改或删除物理索引。
运行时纠偏:使用真实扫描行数和已产出行数校验优化器估算,减少统计信息不准确或数据分布不均造成的慢查询。
语句级生效:自适应状态仅用于当前语句,不会持久化为索引或长期学习结果。
查询块隔离:子查询、派生表等查询块独立记录自适应状态;某个查询块触发重优化,不会错误地影响其他查询块。
支持正向和反向索引扫描:可覆盖 ORDER BY ... ASCORDER BY ... DESC 的排序索引访问。
支持普通查询和预处理语句:普通 SELECT、多语句请求中的 SELECT 和 prepared statement 均可透明重执行。

支持版本

适用于 TDSQL Boundless V21.6.4.0 及以上版本。

适用场景

建议用于以下查询:
包含 ORDER BYGROUP BY,并带有非零 LIMIT
已开启 prefer_ordering_index,优化器可能为避免排序而选择排序索引;
过滤列不是排序索引的前导列,或者过滤条件与排序键的分布相关性较强;
数据倾斜、统计信息误差或多表 fanout 估算误差可能导致排序索引扫描量被低估。

使用限制

自适应重执行要求查询可以安全地再次执行,因此以下场景不启用:
SELECT 语句或没有有效 LIMIT 的查询;
存储过程中的语句;
带副作用或不可安全重执行的查询,例如锁定读、用户变量读写、存储函数、UDF 或其他非确定性结构;
活跃读写事务中的查询;只读事务不受此项限制;
并行查询计划;
通过 FORCE INDEXFORCE INDEX FOR ORDER BYFORCE INDEX FOR GROUP BY 强制选择索引的查询;
首轮结果已经刷新到客户端之后才发现计划低效的查询。
该功能的前提是优化器在首轮实际选择了排序索引。开启功能并不代表每条查询都会重执行,也不会改变未选择排序索引的查询。

工作原理

Adaptive Ordering Index 采用“首轮执行采样 + 必要时重新优化”的两阶段机制。

首轮选择排序索引

优化器首先按照正常流程生成计划。当 prefer_ordering_index=on 时,优化器会比较:
使用现有索引直接提供 ORDER BY/GROUP BY 顺序并尽早满足 LIMIT
使用过滤效率更高的访问路径,再执行排序。
对于多表查询,开启 limit_aware_join_order 后,优化器还会评估将带有排序索引的表作为驱动表是否能通过 LIMIT 提前结束,从而调整连接顺序成本。
只有首轮最终采用了非强制的排序索引计划,自适应检查才会建立运行时上下文。

计算运行时检查点

优化器记录以下估算值:
L:查询的 LIMIT 行数;
E:预计为得到 L 行结果需要扫描的索引行数;
T:参数 adaptive_ordering_checkpoint_rows 指定的基础检查阈值。
运行时的预期已产出行数为:
如果 E <= T:expected_seen_rows = L
如果 E > T:expected_seen_rows = max(L / E × T, 1)
实际触发检查的扫描行数为:
checkpoint_rows = max(T, E / L)
该计算同时考虑固定采样规模和优化器估算的平均“每产出一行需要扫描多少行”,避免检查点过早或过晚。

执行期间校验

驱动表扫描期间,系统累计:
actual_examined_rows:实际扫描的记录数;
actual_seen_rows:已经通过上层算子、可计入 LIMIT 的记录数。
当扫描量达到 checkpoint_rows 时:
如果 actual_seen_rows >= expected_seen_rows,说明排序索引效果符合预期,关闭后续检查并继续执行;
如果 actual_seen_rows < expected_seen_rows,说明排序索引的过滤效率明显低于预期,尝试触发自适应重执行。
在 TDStore 条件下推场景中,cond_pushdown_checkpoint 会把检查点传递给存储层,并回传被存储层过滤的行数,使 SQL 层能够正确统计真实扫描量,避免因过滤发生在存储层而漏判。

透明重新优化与执行

触发自适应重执行时,系统会:
1. 确认当前结果尚未刷新到客户端;
2. 回退当前语句的协议输出缓冲区;
3. 仅为触发问题的查询块记录“跳过排序索引偏好”标记;
4. 重新优化并执行整条 SELECT
5. 第二轮同时跳过 prefer_ordering_index 和对应的 limit_aware_join_order 优惠,避免通过另一条优化路径再次选回同一类计划;
6. 向客户端返回第二轮的最终结果。
每次成功触发重执行,状态计数器 Adaptive_ordering_used 增加 1。第二轮不会再次触发同类重执行,因此一条语句不会因该机制无限重试。

参数配置

参数总览

参数
默认值
范围或选项
作用
adaptive_plans_switch.ordering_index
on
on / off
是否启用排序索引的运行时检查和自适应重执行。
adaptive_plans_switch.warnings
off
on / off
重执行时是否向客户端产生 NOTE,说明对应查询块已禁用 prefer_ordering_index/limit_aware_join_order
adaptive_plans_switch.cond_pushdown_checkpoint
on
on / off
是否为 TDStore 条件下推扫描启用存储层检查点和过滤行统计。通常建议保持开启。
adaptive_ordering_checkpoint_rows
50000
118446744073709551615
基础扫描检查阈值。值越小,越早检查并更容易纠正坏计划;值越大,误判风险和重执行概率越低,但发现坏计划更晚。
prefer_ordering_index_fanout_safety_factor
0.1
0.01.0
多表 limit_aware_join_order 的 fanout 安全系数。值越小,成本优惠越保守。单表排序索引选择不受该参数影响。
上述参数支持动态配置。通常使用 SET SESSION 对当前连接生效;也可以通过全局变量或实例启动参数为新连接设置默认值。它们不写入 binlog,并支持通过 SET_VAR 优化器 Hint 进行语句级设置。
说明:
adaptive_plans_switch 的默认值虽然已包含 ordering_index=on,但 prefer_ordering_index 默认关闭,因此按照编译默认配置,Adaptive Ordering Index 不会实际介入执行。需要同时开启 prefer_ordering_index