原
记录mysql5.7.30下group by 和sum函数一起使用在生产上产生的bug,差点把人搞崩溃了
版权声明:本文为博主原创文章,请尊重他人的劳动成果,转载请附上原文出处链接和本声明。
本文链接:https://www.91mszl.com/zhangwuji/article/details/1306
由于某些原因,现在不支持支付宝支付,如需要购买源码请加博主微信进行购买,微信号:13248254750
生产现象:这是一个BI报表功能,我们使用的BI工具是webdatarocks,官方网址是:https://www.webdatarocks.com/ 每次进入BI报表查询,相同的sql每次的结果都不一样,如第一次看到的是上海行销公司2021-01月份的销量为1000,第二次查询的时候可能就变成了800,也有可能不变,结果每次都很随机,并且毫无规律。下面来讲讲我的排查之路,写程序多年,第一次碰到如此棘手的问题。顶着来自ceo的压力,项目的压力,一路前行,披荆斩棘,终于完美的解决了这个bug,从入坑到出坑用了两天半的时间,这段时间简直是度日如年,我的sql如下:
SELECT
date_month,
YEAR,
MONTH,
QUARTER,
vendor_no,
district_name,
company_name,
brand_name,
brand_static,
taste_name,
taste_static,
specifi_name,
specifi_static,
price_priod,
price_priod_static,
product_sys,
sum(sales_volume) AS salesVolume,
sum(sales_amount) AS salesAmount
FROM
m_sales_volume_monthly
WHERE
date_month >= '2019-10' AND date_month <= '2020-03' AND vendor_no IN ('M')
GROUP BY
date_month,
YEAR,
MONTH,
QUARTER,
vendor_no,
district_name,
company_name,
brand_name,
brand_static,
taste_name,
taste_static,
specifi_name,
specifi_static,
price_priod,
price_priod_static,
product_sys
下面说下我的排查思路:
1)webdatarocks报表不支持,这么大的数据量,我们一下子查询3年的数据,大概70M左右,一下子把这么大的数据量加载到报表工具里面,导致数据错乱。理由:webdatarocks官网已经明确的说明了,报表里面的数据不要超过1M,现在我们的数据是70M远远超过了官方给出的1M。官方给出的1M说明地址:https://www.webdatarocks.com/doc/licensing-terms-copyright/,这个在后面验证了不是这个问题。

2)数据库脏读。通过命令查看数据库的隔离级别
show variables like '%iso%';

备注:顺便提一下数据库的隔离级别。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|
读未提交(read uncommitted) | 可能 | 可能 | 可能 |
读已提交 (read committed) | 不能 | 可能 | 可能 |
可重复读 (repeateable read) | 不能 | 不能 | 可能 |
串行化 (serializable) | 不能 | 不能 | 不能 |
确定数据库是REPEATABLE-READ ,不是read committed,不存在同一个select 查询多次可能返回不同的结果
3)sql查询出来的数据是放到ArrayList里面的,ArrayList非线程安全。我们改为:
List<xx> biList=Collections.synchronizedList(new ArrayList<>());
更改了后,发现问题依旧存在。
4)数据库类型问题,销量字段数据库之前是double类型,我们改成decimal,更改后,测试发现问题依然没有解决。
5)四舍五入问题,我们在后端销量字段进行四舍五入保留两位小数,然后返回给前端,还是依然没有解决。
6)怀疑返回给前端这么大的数据,前端存在数据丢失的情况。此时开始验证,后端将返回给前端的数据打印到log文件里面,在拿着这个数据和前端返回的数据进行对比(使用对比工具Beyond Compare)。说明:我这个json文件有50M,目前还有比较好的json工具能格式化这个json,基本都是一打开就卡死(Dadroit JSON Viewer这个工具能打开我50M的json,但是没办法进行复制,我需要将后端返回的json和前端返回的json进行格式化,然后在进行对比),最后我想到的办法是吧这个大文件进行切换,然后分批进行对比。发现后端返回给前端的json数据和前端打印出来的json数据都是一样的,到此数据丢失的情况排除了。
7)基本上排除了能想到的所有情况,此时此刻,突然有个想法,是不是mysql5.7.30 这个版本bug,然后开始翻mysql的官方文档。地址:https://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-31.html,我看到了这段话
The server sometimes mistakenly removed a subquery with a GROUP BY when optimizing a query, even in some cases when this subquery was used by an outer select. This could occur when the subquery also used an aggregate function. (Bug #28240054)

大概意思是说当group by 和sum函数一起使用进行查询时某些情况下会查询出来的数据有问题。感觉突然发现了新大陆,然后开始让运维的小伙伴帮忙在服务器上新安装一个mysql版本为5.7.32,然后将数据全部迁移到5.7.32版本的数据库,然后进行测试,发现无论查询多少次,每次查询出来的结果都是相同的,到此就完美的解决了这个问题,在此感谢公司运维的小伙伴,感谢钉钉群里一位素未谋面的网友,同时也感谢自己遇到问题和顶着压力没有放弃,一步步的排除问题,没有因为外界的压力让自己方寸大乱。
总结:如果是新项目尽量用高版本的数据库,如果是老项目只能一步步的去踩坑。