赞
踩
很懒,不爱写东西,这次BUG排查印象深刻,过程有点痛苦,仅作为笔记形式供自己和大家参考,避免在以后遇到同类问题时继续踩坑...
背景
云PASS平台需要统计各租户的客户端使用者每天发送的各种消息类型的消息量,根据使用量生成账单展示给租户方便客户对账。使用量统计服务(DATA服务)作为微服务架构中的独立服务,消费上游服务推送至kafka的消息记录,将消息处理后暂存至Redis,然后在空闲时间将数据保存至数据库。由于平台使用量较大,每天产生的kafka消息在10亿以上,平台30万租户,每天产生的数据库记录在300万左右,为了加快数据查询速度,采用水平分表将数据按年月分别保存至不同的物理表,DATA服务采用Sharding JDBC+JPA实现分表后数据查询的结果集归并。
周五快下班的时候接到产品和测试通知说按天导出Excel文件中的记录出现重复,一开始以为是数据库脏数据的问题,打开navicat查询数据库对应日期的记录,发现数据库只有一条数据记录,最奇葩的是导出的数据记录和数据库记录竟然不一致,一首凉凉送给自己,认为是程序数据处理出现问题,嘚吧嘚的开始Review数据查询出来后的处理方法...
一通操作猛如虎,定睛一看原地杵。上下左右前前后后Review了3遍,DBUG又调试跟进了1遍,很确定数据处理没有问题!!!难道是查询结果出错了?Apache顶级项目ShardingJDBC会犯这种低级错误?嘚嘚嘚,DBUG查询结果继续找呗!!!
程序打开查询SQL打印,比对JPA生成的查询SQL和传入的参数
刺激不刺激?惊喜不惊喜? 毫无问题!!!
既然JPA生成的SQL语句没有问题,难道是Sharding JDBC进行逻辑表至物理表映射的过程中出现了查询参数丢失?打开数据库查询语句日志记录,DEBUG抓取数据库真正执行的SQL!!!
Sharding JDBC正正经经执行逻辑表到物理表的映射,老老实实拼接SQL查询参数,毫无破绽!!!这个世界没有问题,有问题的是自己的程序代码!!!
一筹莫展,继续Review代码,左三圈右三圈还是找不到问题,我十分肯定的是查询返回的数据集有问题,别问我为什么,就是这么自信...
山重水复疑无路,柳暗花明又一村。打断点,在结果集上面执行表达式,好家伙,返回的结果真的有问题!这种骚操作真的是始料未及!!!
这是在逗我吗?等值查询返回别的消息类型在结果集里!!!幻觉吗?又执行别的查询语句,发现所有的查询都有这种问题...
到现在为止近4个小时过去了,我还没找到BUG原因,8点了下班周末继续... 一首凉凉送给自己
没招!继续比对重复数据找规律...佛祖保佑,还真让我看到了数据的规律,注意看数据记录上的这个ID,发现出问题数据大部分存在相同ID的数据,也就是说一次SQL查询,如果结果集中出现了重复的数据ID,只有第一条记录是来自数据库的,其他的记录都是第一条记录的复制品,刺激不刺激?忘记截图了....
DEBUG跟进源码,一通操作猛如虎,真的找到的对应的注释操作!!!
水平太有限,DEBUG源码过程的酸爽是促使我记录此次BUG查询的主要原因.....
前面已经介绍,这个项目采用了数据库的水平分表,这类统计数据按年月分表,表中的数据只保存一年,到期限了drop table释放磁盘空间,MYSQL delete删除记录是不会释放磁盘空间的,设计的无懈可击,毫无破绽可言!!!
但是忽略了一点,分表后的物理表的主键采用的是自增主键,这个自增主键在各个物理表是存在重复值的,爽歪歪。JPA Hibernate采用的是二级缓存机制,二级缓存SessionFactory级别是可配置的,程序中也没有开启,没毛病!!!
但是一级缓存Session级别是由Hibernate自己控制的,提供了session.clear()等方法清除缓存,但是ShardingJDBC从逻辑表到物理表的映射和中间结果集的归并都是不可见的,根本没有办法在这期间插入session.clear()...
好刺激,百思不得姐!继续阅读源码,真相大白!!!原来是根据主键确定当前Session中是否已经存在记录的,如果EntityManager.get(key)方法能找到数据,那么Hibernate就会认为该记录已经被Session托管,直接返回之前的数据记录,没毛病,和我认知的世界没冲突!!!
真的是超级简单,根据这个ID确定重复记录,我自定义SQL不查这个ID不久可以了?说干就干!!!
调试程序,拿到生成的Excel文件,和数据库比对,一模一样!!!
就不截图了,大吉大利!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。