问题现象
在进行数据库灾备切换演练后,应用在插入数据时遇到主键冲突错误,具体表现为序列生成的值出现了重复。问题 SQL 示例如下:
INSERT INTO test_xxxxxxxxx(serial_no,trade_date,occur_date,occur_time,busi_subject,user_id,op_xxxxxx_no,op_station,op_entrust_way,op_remark,client_id,xxx_xxxx,xxxacct_limit,xxxacct_status,xxxacct_type,main_flag,open_date,close_date,sys_type,sysnode_id,sysnode_id_dest,xxx_pwd,xxxxxx_no,xxxacct_group,xxxacct_class,en_func,en_entrust_way)
SELECT concat(:jyrq, lpad(seq_nextval('seq_xxxxxxxxx_serial_no'),12,'0')),
:jyrq,
date_format(now(),'%Y%m%d'),
curtime(),
:ywbz,
:userid,
:yyb,
:czzd,
:wtfs,
:zy,
client_id,
xxx_xxxx,
xxxacct_limit,
xxxacct_status,
xxxacct_type,
main_flag,
open_date,
close_date,
sys_type,
sysnode_id,
sysnode_id_dest,
xxx_pwd,
xxxxxx_no,
xxxacct_group,
xxxacct_class,
en_func,
en_entrust_way
from test_xxxxxxxxx
where client_id=:khh and xxx_xxxx=:xxxxx;
关键诊断信息
触发条件
假设目前 A 为主租户,B 为备租户,sequence 定义中使用了 cache,本地会有一段 sequence 缓存。
主备库切换之后,B 为主租户,A 为备租户,这时执行 sequence 的 DDL 操作,有少量 DDL 操作(比如 restart、将 cache 模式改为 nocache 等)需要清除本地 sequence 缓存,而 A 作为备租户没有进行这个操作。
再次进行主备切换,A 切为主租户,B 切为备租户,这时 A 就会使用本地过期的 sequence 缓存。
事前巡检
检查序列定义,特别是是否使用了 cache 选项。
审核主备切换的流程和配置,确保序列值能够正确同步。
事后诊断
查阅备租户的 OBServer 日志和 RS 日志,寻找序列值重复的记录。
分析日志中关于 Failover 操作的时间线,确定序列值重复的具体时间点。
问题原因
OceanBase 数据库内核存在一个 BUG,该 BUG 导致在主备切换(Failover)后,主租户清除 sequence 缓存的动作未能正确同步到备租户,从而在主库恢复后继续使用旧的 sequence 缓存,造成序列值重复。
问题的风险及影响
数据插入时可能遇到主键冲突错误,导致数据完整性受损。
应用程序可能因此中断或产生异常,影响业务连续性和用户体验。
影响租户
影响 OceanBase 数据库中的 SYS 租户和 Oracle 租户以及 MySQL 租户。
影响版本
OceanBase 数据库企业版 V4.2.1 GA(oceanbase-4.2.1.0-100000182023092722)及之后版本、V4.2.5 GA(oceanbase-4.2.5.0-100000082024102022)及之后版本、V4.3.5 GA(oceanbase-4.3.5.0-100000122024123020)及之后版本。
解决方法
升级至问题已修复版本。目前已修复的版本包括 OceanBase 数据库企业版 V4.2.5 BP4(oceanbase-4.2.5.4-104000082025052817)及之后版本、V4.3.5 BP4(oceanbase-4.3.5.4-104000052025090918)及之后版本。
在 sequence cache 中记录一个
epoch_version字段,使之与switchover_epoch对应,在发现缓存epoch_version过期后主动清除本地缓存。
规避方式
在序列定义中避免使用 cache 选项,虽然这可能会降低性能,但可以减少主备切换时序列值不一致的风险。
在进行主备切换前,确保所有涉及序列的操作已经完成,避免在切换过程中进行 sequence 的 DDL 操作。
如果已经出现了本问题,可以再次执行 sequence DDL 操作来主动清除缓存。