首批通过分布式安全可靠测评,为关键业务系统打造
Queuing 表查询缓慢问题
更新时间:2026-05-15 09:06
当用户在某张表上频繁的执行插入并且同时进行批量删除,或者有大量的并发更新操作时,可能会遇到一种现象:表中的数据行数并不大,但是查询和更新的性能出现明显下降。这种现象在 OceanBase 数据库中称为 Queuing 表(业务上有时又称 buffer 表)效应。本文讨论这种问题的处理方法,以及 OceanBase 的处理机制。
适用版本
OceanBase 数据库 V2.2.77 以及之后的版本
Queuing 表简介
Queuing 表(又称 buffer 表)是业务上因使用场景而诞生的名称,意为业务上“像使用 buffer一样使用一张表”,即全表数据有大比例的更新或者增删。该场景具有以下特点:
触发条件:表数据频繁大比例更新当表中大量插入的同时大量连续删除(或者大量更新,因为 OceanBase 数据库更新的本质也是 delete+insert)时,一张表看起来只有几千行数据,但实际上可能已经发生了几百万的插入和删除操作。
直接现象:表行数不大,但查询很慢 buffer 表效应的一个明显特征就是数据量很小的表(例如几千行),查询起来却非常慢。这是因为对于 buffer 表来说,查询的 SQL 在内核处理时,实际需要扫描的行数量可能远大于这个量级(可能是几百到上千万)。默认设置下,一张表中删除的行在 OceanBase 数据库每日合并前并不是真的删除,而只是在内存里打了个删除标记,OceanBase major freeze/merge 期间才会真正处理为删除。
问题原因:执行计划跳变,全表扫描耗时翻倍。这种“mark for delete”的处理方式, 是采用了 LSM-Tree 架构的存储引擎的共同问题。而且因为buffer 表的删除会在合并期间处理为真正的删除,而 OceanBase 数据库在合并期间会收集统计信息,更新执行计划,此时部分表的数据量因为很少,OceanBase 数据库的 CBO 优化器可能根据代价计算而为某些 SQL 生成全表扫描的计划。这个执行计划在白天随着业务访问不断增加,表中的实际数据量不断加大,SQL 性能会出现较大滑坡。
应急处理方案
Buffer 表出现时多数情况下系统已经运行在线上,此时需要的是快速止血,常见处理方式如下。
- 对于存在可用索引,但 OceanBase 优化器计划生成为全表扫描的场景。需要进行执行计划 binding 来固定计划。具体操作详见 SQL 查询导致的异常
- 如果 SQL 查询的主要过滤字段无可用索引,此时推荐在线创建可用索引并绑定该计划。
- 如果业务场景暂时无法创建索引,或者执行的 SQL 多为范围扫描,此时可根据业务场景需要决定是否手动触发合并,将删除或更新的数据版本进行清理,降低全表扫描的数据量,提升速度。
OceanBase 数据库对 Queuing 表的优化
OceanBase 数据库为了优化 buffer 表效应,在 MemTable 和 SSTable 两个层面,对表数据连续删除的“空洞”设定了一个阈值(如 256 行),当这些空洞被查询扫描过一次时,存储层就会在上面打上“可跳过”的标记。这样就能使相同 SQL 下次再查询时,可以直接跳过这些无需扫描的行,实现快速查询。默认场景下,当 OceanBase 数据库在转储/合并发生冻结的瞬间,这些空洞的 range 打标会失效,必须依赖下一次“成功的慢查询(全表扫描)”才能够将标记再次打上去。所以多数情况下,如果用户对 buffer 表的 SQL 的执行计划创建合适的索引并且进行了执行计划绑定,后面即使不做其他干预,经历一次超长耗时的请求,后面即可恢复正常。但是这些方法均为应急止血方案,从 V2.2.7x 版本开始,OceanBase 数据库引入了 buffer minor merge 设计,实现对 queuing 表的特殊转储机制,彻底解决无效扫描问题。对于设计阶段已经明确的 Queuing 表场景,推荐开启该特性作为长期解决方案。
obclient> ALTER TABLE user_table TABLE_MODE = 'queuing';
关于 Queuing 表转储
OceanBase 数据库的自适应的 buffer 表转储策略,由存储层在每次转储时根据转储的统计信息来自主判断是否需要对该表采用 buffer 表转储策略,当发现一个表存在类似 buffer 表行为时,接下来会尝试对这个表做 buffer minor merge 的调度,对这个表基于 Major SSTable 和最新的增量数据以当前的读快照时间生成一个 Buf Minor SSTable,这次 Compaction 动作会消除掉增量数据里的所有 Delete 标记,后续查询基于新生成的 Buf Minor SSTable 就可以避免原有的大量无效扫描动作。