MySQL采用order by limit分段取数据汇总错误问题探究
背景
前几天接同事报问题:源数据一致的情况下,生产环境报表生成时而对,时而缺少确定的值。
查看源码,报表生成采用SQL语句,
create_time >= xxx and create_time <= yyy order by create_time limit offset, 1;
每次取指定时间段的一条记录,然后累加指定字段生成。(先不考虑这种方式的效率)
结论
当多条记录的create_time相同时,数据按create_time排序的结果是不恒定的(没有幂等性);
导致有时某些记录被取出多次,某些记录没有取到,从而表现为报表时而对,时而缺少固定的值。
解决方案
分段取数据情况下,排序依据需要是具体唯一性的值,如数据库主键。保证每次排序结果恒定,幂等。
补充:分段取数据,通常有时间参数,且索引最好能命中时间参数;所以排序最好是时间参数, 数据库主键一起
过程
看代码
每次取一条,逻辑虽然奇葩,但细看代码无bug。
看日志
没有日志,想办法。以下是想办法过程:
-
初想用Btrace,不熟Btrace埋点语法和如何使用非JDK类,遂放弃。
-
MyBatis Mapper文件默认有logger,级别是info;SpringBoot Admin可以动态开启指定logger;遂用SpringBoot Admin将对应Mapper日志级别设置为Trace。
-
K8S指定pod日志重定向到文件 kubectl logs -f --tail=10 pod名称 > mapper.log
-
手动触发报表生成逻辑,grep、awk采集mapper.log中sql的参数和结果。
-
excel排序对比数据库源数据和mapper.log采集的数据,找出差异:某条记录被取2次,某条记录未被取。
-
分析差异,被取2次记录A和未被取记录B的create_time相同,导致前一次获取排序A、B,后一次获取排序B、A;A被取2次,B未被取到。