排查了一则MySQL
中datetime
写入 诡异进位 的问题,通过查阅官方文档的方式找到了问题原因。顺带巩固下基础知识。
背景:
问题由最近QA
同事给我提的一个工单引起。
相关表结构:
|
|
其中我们业务时间字段类型为 c1
datetime 类型。
过程:
- 前端传入 某天最后毫秒的13位时间戳
- 后端通过
LocalDateUtils.convertMilliToLocalDateTime(timestamp)
传到dao
,进行sql
写入
示例时间戳: 1691769599999 2023-08-11T23:59:59.999
问题表现:
写入到mysql后,数据变成了 2023-08-12T00:00:00
。
分析:
最佳实践应该是找mysql官方文档,根据我们使用的5.7 找到了mysql关于datetime类型的描述: https://dev.mysql.com/doc/refman/5.7/en/fractional-seconds.html
而我们表中字段设置为datetime,未保留毫秒精度,因此传入 2023-08-11T23:59:59.999
时四舍五入进位,变成了 2023-08-12T00:00:00
。
顺便复习下rdbms中各种数据类型后面可指定长度、精度的情况:
- 整数类型:
- TINYINT:可选长度参数指定显示宽度。
- SMALLINT:可选长度参数指定显示宽度。
- MEDIUMINT:可选长度参数指定显示宽度。
- INT:可选长度参数指定显示宽度。
- BIGINT:可选长度参数指定显示宽度。
- 浮点数类型:
- FLOAT:可选精度参数指定小数位数。
- DOUBLE:可选精度参数指定小数位数。
- 定点数类型:
- DECIMAL:必需的精度和标度参数分别指定总位数和小数位数。
- 日期和时间类型:
- DATE:无需额外参数。
- TIME:可选的小数秒精度参数指定秒的小数位数。
- DATETIME:可选的小数秒精度参数指定秒的小数位数。
- TIMESTAMP:可选的小数秒精度参数指定秒的小数位数。
- 字符串类型:
- CHAR:必需的长度参数指定字符串的固定长度。
- VARCHAR:必需的长度参数指定字符串的最大长度。
- BINARY:必需的长度参数指定二进制字符串的固定长度。
- VARBINARY:必需的长度参数指定二进制字符串的最大长度。
- ENUM:必需的值列表参数指定列允许的值。
- SET:必需的值列表参数指定列允许的值。
- TEXT:无需额外参数。
- BLOB:无需额外参数。
- 其他类型:
- BOOLEAN:无需额外参数。
- JSON:无需额外参数。
解决:
方案1:
数据库字段设置为 datetime(3),保留毫秒三位精度。
方案2:
前端或后端将时间戳进行特殊处理,比如 -1s 。
推荐使用方案1。