在我们的订单管理系统中,发现一个诡异的现象:某天的订单数据在测试环境中查询正常,但在生产环境中总是缺少最近8小时的数据。
具体来说,当我们查询"2023-07-08"的订单时:
更奇怪的是,直接使用MySQL命令行查询生产数据库时,数据却是完整的。
首先检查代码中的查询逻辑,看起来没有问题:
SELECT * FROM orders
WHERE order_date >= '2023-07-08 00:00:00'
AND order_date <= '2023-07-08 23:59:59';
检查数据库时区设置:
SHOW VARIABLES LIKE '%time_zone%';
发现生产环境和测试环境确实不同:
开启Spring Boot的SQL日志:
logging:
level:
com.example.mapper: debug
发现实际执行的SQL是:
SELECT * FROM orders
WHERE order_date >= '2023-07-08 08:00:00'
AND order_date <= '2023-07-09 07:59:59';
为什么时间范围被自动调整了8小时?
检查数据库连接字符串:
jdbc:mysql://localhost:3306/order_db?useUnicode=true&characterEncoding=utf8
发现没有显式设置时区,但Connector/J有默认行为:
问题在于:Java应用将本地时间字符串转换为数据库时区时间时,发生了意外的时区转换:
修改数据库连接字符串,显式指定时区:
// 生产环境使用UTC
jdbc:mysql://localhost:3306/order_db?serverTimezone=UTC
// 或者统一为上海时间
jdbc:mysql://localhost:3306/order_db?serverTimezone=Asia/Shanghai
在Java代码中使用明确的时间表示:
// 使用ZonedDateTime代替LocalDateTime
ZonedDateTime start = ZonedDateTime.of(2023, 7, 8, 0, 0, 0, 0, ZoneId.of("Asia/Shanghai"));
ZonedDateTime end = ZonedDateTime.of(2023, 7, 8, 23, 59, 59, 0, ZoneId.of("Asia/Shanghai"));
// 或者在SQL中使用参数化查询
@Query("SELECT * FROM orders WHERE order_date BETWEEN :start AND :end")
List<Order> findByDate(@Param("start") Date start, @Param("end") Date end);
修改表结构,存储时间戳而非datetime:
ALTER TABLE orders
MODIFY COLUMN order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP;
// 推荐的时间处理方式
public void processOrder(OrderRequest request) {
// 前端传递ISO格式时间:2023-07-08T00:00:00+08:00
ZonedDateTime zonedDateTime = ZonedDateTime.parse(request.getOrderTime());
// 转换为UTC时间存储
Instant instant = zonedDateTime.toInstant();
order.setOrderDate(Date.from(instant));
}
这个Bug教会我们:时间处理从来都不是简单的问题,在分布式系统中尤其如此。明确时区、统一约定、显式配置,是避免这类问题的关键。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。