Oceanbase BUG分析--obloader入库导致odp异常core

:man_technologist: 关于我

山东某通讯非知名一线DBA

17年开始从事数据库相关工作,23年初左右开始正式接触Oceanbase数据库。

阅读本文章,非常的枯燥,中间分析的过程也很费劲,折腾了两天,而且公众号书写还挺麻烦,耗费精力,还是希望大家能沉下心来完成的阅读完毕,如果大家中间有疑惑的地方,可以加群进行沟通,当然也可以加我微信直接找我私聊,下面请跟着我的脚步来一步步了解obloader batch的内部原理。

前两天数据库obproxy突然发生异常core,影响生产业务,拉了紧急工单,直接到L3级别,OB原厂工程师也远程过来分析了下基本情况,刚开始通过日志分析可能是网络问题导致的,但是通过tsar查询网络基本无波动,原厂研发也无法通过现有的信息基本上没有办法分析出来什么导致的core,最后的最后,还是现场通过后台日志信息定位问题客户端,咨询业务判断obloader导数导致的obproxy异常core,研发最终判断属于obproxy的相关bug。

分析过程

obproxy.log日志相关有用信息基本上没有,研发获取到的相关信息是obproxy在core的过程中端口被不断地占用,obproxy无法启动,obproxy的dump信息不全,无法判断具体core的原因。

1.obproxy_error.log日志出现陌生报错

obproxy.log日志中确实有用信息太少,无法定位,这里我查询了obproxy_error.log相关日志,发现日志中出现了大量的 -5044:unkown system variable nvarchar_set_connection 报错

2.后台打点脚本发现异常客户端

后台部署的打点脚本,会实时的打印客户端信息,这里我们通过问题时间段的日志发现了有一个陌生的client_ip连接,且连接数较大

3.通过obproxy_error.log对应的trace_id定位具体的客户端

咨询业务后判断为obloader入库操作,文件是其他业务部门提供,入库的改表最近增加了大量的字段,进一步了解后得知,该入库业务是第一次进行执行入库操作,2025-11-19晚八点再次执行obloader命令后,obproxy再次发生core,这里断定obproxy的异常core和obloader入库有关。

入库脚本说明

业务有一个obloader入库的操作,使用的是csv格式的文本,通过ctl文件指定了入参的列数为85(大家记住这个导入的表有85个列,这点很重要!!!)

相关命令如下:

./obloader -hxx.xx.xx.xx -P3306 -utest --no-sys --obcluster1 -ttenant1 --table t1 -f /intuint/xxx.avl --ctl-patch=/intuint/xxx.ctrl --log-path=/intunit/xxx.log --cut --file-suffix ‘*.avl’ --file-encoding GBK --column-splitter 0x09

原理剖析

下面我们来分析为什么obloader入库会出现odp的异常core问题

根据ob产研说法,prepare参数大于65535就会触发,其实这个说法是不准确的,经过测试obloader在参数化的情况下不会超过65535这个阈值,只能等于小于。实际测试应该是大于等于65535的时候就会触发,就会异常core,属于obproxy相关bug,升级到3.2.11.0 BP4 hotfix5 (2024-07-04)版本 解决此类bug。

1、入库操作prepare为何达到65535上限

这里肯定都有一个疑问,为何之前入库从未出现过异常的core,这里先给出一个结论,因为65535刚好是入库列数的整数倍,非常巧合!!!!

2、默认入库的batch是如何计算的

我们来看下面这段代码:

从上述代码中,首先会算一个最大的batch,这个值是65535除以列数目计算出一个batch-1,然后根据数据文件中第一个记录的大小算出一个batch-2,然后两个batch-1和batch-2进行比较取小的。 根据文件的第一行记录算出batch大小是用一个固定的大小:1024*1024除以第一行记录的大小

第二种情况根据文件第一行计算出batch大小用作固定,还有一个隐含的条件就是batch不能超过800,相关代码如下:

那么事实上,ob是通过如上两种方式进行切分的吗?我做了如下测试:

修改ps参数,将相关参数修改为false

调整obloader软件目录下的参数文件conf/session.conf.json ,把useServerPrepStmts改成false 。禁止服务端ps,这样就避免了入参大于等于65535使用obloader入库报错的问题。

按照65535除以列数目进行切分方式

使用obloader进行导入数据,并观察obloader自动切分的batch大小

./obloader -hxx.xx.xx.xx -P3306 -utest --no-sys --obcluster1 -ttenant1 --table t1 -f /intuint/xxx.avl --ctl-patch=/intuint/xxx.ctrl --log-path=/intunit/xxx.log --cut --file-suffix ‘*.avl’ --file-encoding GBK --column-splitter 0x09

这里得到的batch数据,刚好就是65535/85(业务导入数据的列数)=771,从这里验证obloader自动batch切分确是是通过65535这个数据进行计算的!!!

按照最大值800进行切分方式

这里我对其他的业务表进行了测试,测试代码如下,并观察了obloader自动切分batch的大小,这里我们能观察到batch切分的大小为800.

./obloader -hxx.xx.xx.xx -P3306 -utest --no-sys --obcluster1 -ttenant1 --table t2 -f /intuint/xxx.avl --ctl-patch=/intuint/xxx.ctrl --log-path=/intunit/xxx.log --cut --file-suffix ‘*.avl’ --file-encoding GBK --column-splitter 0x09

具体运行代码是通过如下方式:

第一行记录的大小算出来是401,1024 * 1024 除以 401 实际得到的是 2614。最终还要用 2614 在一个静态表中做一次查询,得到batch为800,这个具体的查询方法 floorEntry 的作用是:

本小节总结如下:

  1. 65535 / 列数量

  2. 1024 * 1024 / 第一行记录的大小,所得值大于 800 的时候取 800,不足 800 的时候向下取整

因此在 1 和 2 之中选择小的那个,这里选择了800

为何触发BUG

通过上一章节中,obloader按照第一种方式进行切分,在ps协议下,如果一个batch对应的行数,所有参数个数加起来正好等于65535,也会obloader报错(原因当前版本proxy的限制有关),业务的场景挺巧的,85个列,一个batch771行,相乘正好是65535,因此触发了bug,导致odp异常core

那么如果我们入库的数据列数不被65535正好除尽,那么是否会触发此类bug呢?答案是肯定不会触发,只要列数不能被65535除尽,就不会触发此类bug,下面我做了如下测试,调整了入库的控制文件对应的列数,将85个列修改为86个,再次进行了入库。成功导入,这里仅观察下batch大小,发现为762,按照第一种计算方式:65535/86-762.03 这里ob直接截断,设置batch为762 总数不会超过65535,因此不会报错

./obloader -hxx.xx.xx.xx -P3306 -utest --no-sys --obcluster1 -ttenant1 --table t2 -f /intuint/xxx.avl --ctl-patch=/intuint/xxx.ctrl --log-path=/intunit/xxx.log --cut --file-suffix ‘*.avl’ --file-encoding GBK --column-splitter 0x09

如何规避

研发给出两种方案:

1.升级obproxy,解决obproxy ps参数超过65535就会触发core的问题。

2.修改obloader参数文件把useServerPrepStmts改成false。禁止服务端ps

这里我根据原理,测试出第三种方案:

既然,ps协议下,参数不能超过65535就会报错,那么只要不超过不就没有问题了?

上面我们知道batch是如何计算的,那么我们反向证明,列数不是85嘛,那么我们将batch设置为771,并使用ps协议进行导入,符合预期,如下报错了,代码如下:

./obloader -hxx.xx.xx.xx -P3306 -utest --no-sys --obcluster1 -ttenant1 --table t1 -f /intuint/xxx.avl --ctl-patch=/intuint/xxx.ctrl --log-path=/intunit/xxx.log --cut --file-suffix ‘*.avl’ --file-encoding GBK --column-splitter 0x09 --batch 771

将batch修改为770,按照之前的结论,这样ps协议下,参数就不会超过65535,不会报错,下面验证,啊!!!!!成功进行导入,说明我们之前的猜想都是正确的,代码如下

./obloader -hxx.xx.xx.xx -P3306 -utest --no-sys --obcluster1 -ttenant1 --table t1 -f /intuint/xxx.avl --ctl-patch=/intuint/xxx.ctrl --log-path=/intunit/xxx.log --cut --file-suffix ‘*.avl’ --file-encoding GBK --column-splitter 0x09 --batch 770

文本协议下是否是65535限制

这里我将把useServerPrepStmts改成false。禁止服务端ps,启用了文本协议,强制指定batch为1000,从测试情况来看,在文本协议下,并没有65535的限制。

./obloader -hxx.xx.xx.xx -P3306 -utest --no-sys --obcluster1 -ttenant1 --table t2 -f /intuint/xxx.avl --ctl-patch=/intuint/xxx.ctrl --log-path=/intunit/xxx.log --cut --file-suffix ‘*.avl’ --file-encoding GBK --column-splitter 0x09 --batch 1000

故障总结

1、本次故障主要原因是因为使用ps协议参数超过或等于65535,引发proxy core

2、为啥之前没有问题?这次出现问题?因为这次数据比较巧合,入库的列刚好是65535整数倍!!!

3、如何规避、

一:升级proxy

二:修改参数关闭ps协议

三:调整batch大小,限制每次入库的参数个数

6 个赞

厉害

2 个赞

good

2 个赞

感谢分享。

2 个赞

666

2 个赞

学到了

2 个赞

厉害合理

1 个赞