首批通过分布式安全可靠测评,为关键业务系统打造
涉及大数据量修改的 SQL 执行慢/事务提交慢
更新时间:2026-06-10 09:51
问题现象
打开死锁检测开关时,写入大量数据情况下可能导致,SQL 执行超时,客户端报错后,事务仍然长时间不结束(但最终可结束),用户侧整体感知为执行慢。
示例如下。
如业务环境中使用了 90 个 session,执行 insert into select 操作,将 90 张源表数据写入同一张目的表中,每条语句操作数据量为 400W 条,总计数据量为 3.6 亿条,关闭死锁检测时 16min 可以导入成功,打开死锁检测后,30min 未成功,最终客户端超时,在客户端超时后,事务还经历了一段时间执行回滚操作,有悬挂告警,但最终回滚成功,整体表现为执行时间过慢,不符合预期。
关键诊断信息
触发条件
写入数据量大,表现为大 SQL 或者大事务。
内部未触发转储、未触发 fast commit 摘除 callback(日志同步速度跟不上)。
事前巡检
查看死锁检测开关是否开启。
如果是 V4.2.5 BP1 版本,只要开启死锁检测,结合触发条件就有可能遭遇该问题。
预计 V4.2.5 BP2 版本,调整内存使用大小后,在死锁检测开启状态下,该问题可以被极大程度缓解。
预计 V4.2.5 BP3 版本,采用自适应内存调整策略来彻底解决该问题,打开死锁检测的影响可忽略或可接受。
事后诊断
关键日志信息(在出问题时间点附近查找,命令如 grep 'DETECT.CACHE.SUMMARY.*T1002' observer.log.xxxx):
[2025-01-15 11:28:47.260987] INFO [DETECT.CACHE.SUMMARY]periodic_tasks (ob_row_hash_holder_map.cpp:389) [404534][T1002_LockWaitM][T1002][Y0-0000000000000000-0-0] [lt=16] dump RowHolderMapper nodes info:[T1002]tenant_memory_limit:324_GB, total_used:36_MB, bucket_used:4_MB, valid_bucket:32768, valid_list:52227627, valid_node:52227622, history_node_alloc_cnt:0, history_node_free_cnt:0, history_list_alloc_cnt:0, history_list_free_cnt:0
如果 valid_list/valid_node 的数量远大于 valid_bucket 的数量,那么就说明存在 bucket 热点,会导致锁冲突严重,拖慢执行时间。
关键堆栈信息(在出问题时抓取堆栈):
在堆栈中查找 RowHolderMapper 关键字,如果能找到,说明有线程卡在相应的流程中,可能是冲突热点导致的。
问题原因
RowHolderMapper 维护了行锁到行锁持有者的映射关系,这是死锁检测运行时必不可少的辅助信息。但维护动作发生于主路径,内部为了提升大部分路径的执行效率,将 bucket 大小由自适应改为了固定大小,这可以帮助避免在正常压测过程中发生 bucket 扩缩容而产生的性能抖动问题。但是固定 bucket 大小将会在超大数据量下导致冲突严重,进而导致内部的 latch 锁的访问瓶颈,从而拖慢语句执行速度。如果语句或者事务执行过程中,触发了内部的转储或者 fast commit,那么 RowHolderMapper 维护的数据量将会减少,避免访问冲突变得严重,但转储和 fast commit 并不总是会被及时触发,无法被依赖,当数据在内存中不断积压时,默认的参数设置将无法支撑,最终导致执行变慢。
结合问题场景来看,为了让一般场景运行得更快会导致 bad case,因此还得改回动态扩缩容策略。
问题的风险及影响
遭遇该问题时,语句执行,或者事务提交会变慢,可能导致客户端遭遇超时报错。
写入 MemTable 的数据可能无法及时转储,内存中的数据会有积压(但最终会成功转储)。
影响租户
影响 OceanBase 数据库中的 Oracle 租户和 MySQL 租户,对于 SYS 租户无影响。
影响版本
OceanBase 数据库企业版 V4.2.5 GA(oceanbase-4.2.5.0-100000082024102022)及之后版本。
解决方法
升级至问题已修复版本。目前已修复的版本包括 OceanBase 数据库企业版 V4.2.5 BP3(oceanbase-4.2.5.3-103000142025033110)及之后版本。
最简单的方式,可以通过关闭死锁检测,来避免遭遇该场景,命令如下。
alter system set _lcl_op_interval='0ms';如果是数据导入场景,也可以通过旁路导入方式来避免数据在内存中堆积过多,进而导致热点。
如果应用侧的每次写入数据量可以修改,将大事务/大 SQL 拆分为小事务/小 SQL 可以避免遭遇该场景。
规避方式
运维规避:
可以通过关闭死锁检测,来避免遭遇该场景,命令如下。
alter system set _lcl_op_interval='0ms';
用户规避:
如果是数据导入场景,也可以通过旁路导入方式来避免数据在内存中堆积过多,进而导致热点。
如果应用侧的每次写入数据量可以修改,将大事务/大 SQL 拆分为小事务/小 SQL 可以避免遭遇该场景。