业务说明

这样一个业务场景,有一个抽奖活动,抽奖活动中如果完成一次分享的任务,就会增加一次抽奖机会。
前端判断用户分享成功后,会调用一次保存分享记录的接口,将分享记录入库。
完成后,前端会调用获取调用查询【活动任务可完成次数】接口,如果任务可完成次数为0,则置灰分享按钮。

本文原文链接地址: http://nullpointer.pw/数据库与应用服务器时间不同步踩坑记.html

如图所示:

定位问题

测试给我提了一个 BUG,在测试环境,完成分享任务后,重新调用【活动任务可完成次数】后,发现接口返回可完成次数仍然是 1,
但是过了好几分钟后,重新进入活动页,接口返回了可完成次数为 0,分享按钮也已经置灰不可点击。

这就很奇怪了,一开始怀疑是缓存的问题,结果并不是,在测试环境远程 Debug 后发现,每次分享成功后,查询数据库分享任务的完成次数竟然没有查询到数据,
而数据中确实存在了分享的记录,将查询 SQL 和查询参数取出来放到 Navicat 中一查,结果是因为时间区间筛选掉了分享的那条记录。
SQL 如下:

1
2
SELECT * from t_share_record where uid = "1234567" and activity_id = 38867247
and create_time between '2020-01-08 00:00:00' and '2020-01-08 19:18:29'

而数据库中的那条分享记录的create_time 是 2020-01-08 19:19:21, 这条记录不在筛选的时间区间内,因而被过滤掉了。

问题原因

时序图如下:

可以看到,分享保存记录为第 8 步,但是重新查询任务可完成接口是第 10 步,但目前的情况则是,第10步中查询时的时间竟然要早于第 8 步数据入库的时间。难道插入数据竟然要这么久?也不至于耗费近一分钟吧!

于是怀疑是数据库时间问题,在MySQL执行查询命令

1
select now();

然后,在部署应用的服务器上执行命令

1
date

查看结果,果然二者时间相差了近一分钟。最终的原因就是:因为数据库和应用服务器不在同一台服务器上,而其他测试人员修改了应用服务器的时间,导致两个服务器时间不一致从而出现这个问题。想到这里,马上向运维确认线上服务器时间与数据库时间是否同步,还好,回答我的是同步的,心里的石头算放下了。

解决问题

查看插入分享记录的 Mybatis 中XML如下

1
insert into t_share_record(uid, activity_id, create_time) values(#{uid}, #{activityId}, now())

这样写是没问题的,但是根据时间段筛选数据时,时间区间却又是从应用服务器生成传递参数传入SQL的,Mybatis 中XML如下:

1
2
SELECT * from t_share_record where uid = #{uid} and activity_id = #{activityId}
and create_time between #{beginTime} and #{endTime}

为了避免以上这种因为应用服务器与数据库时间不一致导致的问题,这里的SQL筛选时间的结束时间也通过数据库服务器获取。

1
2
SELECT * from t_share_record where uid = #{uid} and activity_id = #{activityId}
and create_time between #{beginTime} and now()

当然,为了防止出现更多类似的问题,应用服务器与数据库服务器的时间一定要通过时间同步设置保持完全一致才是最合理的。

希望对你有所帮助!