记录一次在客户环境发现的达梦数据的bug,下面详细讲述一下问题的发现过程和问题的详细原因。
问题现象
客户现场问题现象大致如下,客户将数据导出为excel表格,发现有一些数据某一列的值为空。经过排查,用户相关配置没有问题,后台查询数据库发现对应的列也是有值的。此时我怀疑应该是个程序bug,导出数据为excel其实就是先去数据库中查询数据,然后把数据写入excel的过程,大概率就是查询数据出来数据后,后面有什么业务逻辑给这个值给清空了或者搞丢了。按照这个方向,通过arthas工具去抓取数据,看看到底是哪一步出现的问题,改一下bug,问题应该就解决了。
疑点?
但是当我实际操作时发现事情没有这么简单,客户现场总共33000多条数据,导出excel时,总有一些数据某一列值为空,但是当我单独选择某一条问题数据时,又是正常的。只导出个几百条数据(其中包含很多问题数据)也是正常的。此时我感觉此事有点蹊跷,可能不是简单的程序bug。
这个缺值列的查询逻辑大致如下:每条数据都有一个id,先把所有数据的id组合成一个idList,然后通过 sql 的 in 语句,即
` in (idList中所有值) ` 这样的查询条件查询到对应id缺值列的值。并且为了防止数据量过大,做了一个分批处理,如果数据量超过10000,就以10000为一个批次进行分批查询。同时因为 oracle 数据库有 in 后跟的值的数量不能超过1000的限制,所以会在拼接 sql 时,每1000个值都会重新拼一个 in ,两个 in 之间通过 or 连接。最终得到的sql大致是这样:select * from table_name where id in (1,2,3....1000) or id in (1001,....2000) or ... 。
因为客户有33000多条数据,每10000条数据为一个批次的话,总共要执行四次这样的sql语句,前三个 sql 语句中 in 后的值的数量都为10000个,最后一个 sql 为最后省的3000多条,并且因为 in 后的值为 id,所以每次查出来的数据数量和 in 后 id 的数量应该是相等的。但是根据抓取到的数据数量来看,前三次查询出来的数据量均为30000条整。这我就有点怀疑人生了,咋还多查了这么老多,如果缺数据的话有可能查出来的实际数量少于10000,这咋还多了呢?我这id可是唯一字段,而且最关键的是查询出来的数据多了这么多,咋还会导致实际某些数据的列缺值呢?
水落石出
暂且不管查出来的数据量是多是少,查出来的数据量和预期不符应该是导致错误的主要原因,于是我将目光转移到 in 后的idList上面,因为使用arthas抓取 sql 语句时,抓取到的是sql大致是这样:
`select field_name from table_name where id in (?,?,?...) or id in (?,?,?...) ... 此处省略一万个问号)` 。此时我怀疑是不是 id 的值有问题,于是我抓取到所有id的值,通过excel进行排重后,没有发现数据重复或者是其他数据错误的问题。此时的我一脸疑惑,咋回事呢?sql 语句本身非常简单,id是一个唯一列,in 后的数据也都抓出来了,看起来都没啥问题,总不能是数据库有问题吧?等一下,数据库bug,可能还真是。我问:“咱项目用的是啥数据库啊”。“达梦”,现场人员回答。达梦数据库脱口而出的那一刻,对数据库的怀疑瞬间到达顶点,不是说看不起咱国产数据库,主要是目前咱国产数据库成立时间相对与市面主流数据库确实确实短了很多,有一些这样那样的问题在所难免。
于是我将所有10000个值拼接成sql,在客户数据库环境执行sql语句,得到的结果是:数据库的所有数据 - 33000多条。好了,破案了,应该就是当 in 后拼接的值的数量总数超过某个阈值时,数据库会直接返回所有数据。那么这个阈值具体是多少呢?我开始测试,从5000测试到8000到9000都没问题,此时我感觉这个阈值很可能就是10000本身,于是我把最后一个值去掉,此时 in 后数量总数为 9999,查询,得到结果也为9999,对了,阈值就是9999。
但是为什么程序中抓出来的数据量是30000?为什么查出来的数据量更多还会导致缺值呢?经过请教部门同事,了解到为了数据安全和服务器负载,平台对于一条 sql 语句查询到的数据量最多就是30000,超过30000直接截断了(可能是加了个limit?)确实,正常 sql 基本不会一次查这么多数据。所以剩下的就是一个数学逻辑问题,客户一共33000条数据,查询批次为每次10000条,也就是需要四次才能查询完所有数据,前三次分别查10000,最后一次3000多条,并且由于数据库bug,导致前三次均会查出数据库中的所有数据,然后由于平台的限制导致前三次每次都只能查出前30000条,后面3000多条是查不到的。最后一个批次中查询的数量虽然也是3000多条,但是不可能正好和数据库中存储的最后三千条一样。导致有一些数据没有被查到。
破案
终于破案了,客户的达梦数据库也是掏钱买的,最后把问题现象和原因给现场人员讲解了一下,让他们给达梦提bug去了。
虽然工作时长满打满算也就是一年 -- 实习半年,正式入职半年。但是已经经历过两次数据库bug了,第一次是oracle数据库还是mysql记不清了,那时刚刚实习没多久,也是sql的执行结果和预期不符,而且现象更加诡异,具体什么情况记不清了,就记得当时我完全不会,直接把问题报告给DBA老师了,就没再管,后来得知竟然是个数据库bug,难绷。说明不管是国产数据库还是老牌数据库都会有问题,只不过mysql在主流应用场景下更加稳定,毕竟这么多年了,问题早被发现完了。咱们国产数据库才多少年,出现问题也很正常,希望国产数据库继续努力,信创之路任重道远。