同一笔跨服寄件,为什么寄出和签收差了 8 小时?
2026 年 9 月底,我在把 OneWayVault 的跨服物资流转记录接入 account.igng.net 网页个人中心时,发现了一个特别诡异的现象:明明玩家在老服寄出物品、几秒钟后就切到新服签收了,可网页流水单上显示的寄出时间和签收时间却整整差了 8 个小时!
一开始我以为是前端渲染时区写错了,可对着数据库原表一查,发现同一行记录里的几个时间字段居然自己就差了 8 小时。
揪出真凶:TIMESTAMP(6) 遇上双写入者时区错位
顺着插件代码和 MySQL 配置一路扒到底层,才发现踩中了一个极其经典的 MySQL TIMESTAMP(6) 双写入者时区陷阱。
原来,当时表里的创建时间等字段用的是建表默认值 DEFAULT CURRENT_TIMESTAMP(6),由数据库引擎自动生成,取的是数据库服务器所在的 +08:00 东八区墙钟;而插件的 JDBC 连接串里偏偏配了一句 serverTimezone=UTC,但没有开启强制会话时区同步!
这就导致了一个荒诞的结果:当 Java 代码在业务层用 Instant.now() 算出一个标准的 UTC 时间数字、通过 PreparedStatement 显式写进另一个字段时,MySQL 当前会话其实还停留在系统的 +08:00 时区里。于是 MySQL 老老实实地把 Java 传过来的 UTC 钟面数字当成了“北京时间”存了进去,一进一出凭空把时间往过去拨了 8 个小时,同一张表里硬生生混进了相差 8 小时的两套时间帧。
V9 迁移:全改 DATETIME(6) 并用双边带算法救回历史数据
查清病根后,2026 年 9 月 27 日(Commit 3b83775),我编写并执行了 OneWayVault 的 V9 数据库迁移。
首先,把所有表里容易随会话时区自动换算的 TIMESTAMP(6) 列全部改成了字面量固定的 DATETIME(6);接着,在 HikariCP 数据库连接池的 connectionInitSql 初始化钩子里硬性写死 SET SESSION time_zone = '+00:00',确保任何一条连接连上数据库的第一件事就是把会话时区锁在 UTC。
而对于已经存进表里的那些历史错位记录,我写了一段基于 7~9 小时双边带窗口的校准迁移脚本,自动比对同一事务生命周期内相邻状态的时间差,把被误拨慢了 8 小时的历史时间戳全部无损修复了回来。