一起MySQL时间戳精度引发的血案
写在前面
最近工作中遇到两例mysql时间戳相关的问题,一个是mysql-connector-java和msyql的精度不一致导致数据查不到;另一例是应用服务器时区错误导致数据查询不到。通过这篇文章,希望能够解答关于mysql中时间戳的几个问题:
- mysql中的DATETIME精度为什么只支持到秒?
- mysql中的DATETIME类型跟时区有关吗?
- mysql设计表的时候,表示时间的字段改如何选择?
案例分析 DATETIME的精度问题
前段时间,将负责的应用的mysql-connector-java的版本从5.1.16升级到5.1.30,在做功能回归的时候发现,使用了类似上面的SQL的用例的运行时数据会有遗漏,导致功能有问题。
考虑到我负责的应用中,有个功能需要用到类似下面这种SQL,即使用时间戳作为查询的条件,查询在某个时间戳之后的所有数据。

经过排查发现:mysql-connector-java在5.1.23之前会将秒后面的精度丢弃再传给MySQL服务端,正好我们使用的mysql版本中DATETIME的精度是秒;在我将mysql-connector-java升级到5.1.30后,从java应用通过mysql-connector-java将时间戳传到MySQL服务端的时候,就不会将毫秒数丢弃了,从mysql-connector-java的角度看是修复了一个BUG,但是对于我的应用来说却是触发了一个BUG。
如果你面对这个问题,你会怎么修复呢?
我们当时想了三种方案:
- 将mybatis的Mapper接口中的时间戳参数的类型,从java.util.Date改成java.sql.Date;
在传入Mapper接口之前,将传入的时间戳按秒取正,代码如下

在查询之前,将传入的时间戳减1秒;
经过验证,方案1会,java.util.Date转过去的java.sql.Date对象会将日期之后的精度全部丢掉,从而导致查询出更多不必要的数据;方案3是可以的,就是可能会查出多一两条数据;方案2也是可以的,相当于从代码上对mysql-connector-java的特性做了补偿。最终我选择的是方案2。
案例复现
利用homebrew安装MySQL,版本是8.0.15,装好后建一个表,用来存放用户信息,SQL如下:

使用spirngboot + mybatis作为开发框架,定义一个用户实体,代码如下所示:

定义该实体对应的Mapper,代码如下:

设置连接mysql相关的配置,代码如下:

编写测试代码,先插入一条数据,然后用时间戳作为查询条件去查询,代码如下:
运行单测,如我们的设想,确实是没有查询出数据来,结果如下:

然后修改代码,利用上面的代码将查询的时间戳按秒取正,代码如下:

再次运行单测,如我们的设想,这次可以查询出数据来了。
不过,这里有个小插曲,我在最开始设计表的时候,使用的SQL语句是下面这样的,

聪明如你一定发现了,这里的datetime已经支持小数点后更小的时间精度了,最多支持6位即最多可以支持到微妙级别。这个特性是什么时候引入的呢,我去查阅了[MySQL的官方文档][9],发现这个特性是在mysql 5.6.4之后开始支持的。

知识点总结
经过了前面的实际案例分析和案例复现,想必读者已经对mysql中DATETIME这个类型有了一定的认识,接下来跟我一起看下,我们从这个案例中可以总结出哪些经验。
- mysql-connector-java的版本和mysql的版本需要配套使用,例如5.6.4之前的版本,就最好不要使用mysql-connector-java的5.1.23之前的版本,否则就可能会遇到我们这次遇到的问题。
- MySQL中用来表示时间的字段类型有:DATE、DATETIME、TIMESTAMP,它们之间有相同点,各自也有自己的特性,我总结了一个表格,如下所示:

- DATETIME类型在MySQL中是以“YYYYMMDDHHMMSS”格式的整数存放的,与时区无关,使用8个字节的空间;
- TIMESTAMP类型可以保存的时间范围要小很多,显示的值依赖时区,MySQL的服务器、操作系统以及客户端连接都有时区的设置。
- 一般情况下推荐使用DATETIME作为时间戳字段,不推荐使用bigint类型来存储时间。
- 在开发中,应该尽量避免使用时间戳作为查询条件,如果必须要用,则需要充分考虑MySQL的精度和查询参数的精度等问题。
参考资料
- https://dev.mysql.com/doc/refman/8.0/en/datetime.html
- 《高性能MySQL》
***
本号(javaadu)专注于后端技术、JVM问题排查和优化、Java面试题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,期待你能在这里有所收获。
一起MySQL时间戳精度引发的血案的更多相关文章
- 转:一个Sqrt函数引发的血案
转自:http://www.cnblogs.com/pkuoliver/archive/2010/10/06/1844725.html 源码下载地址:http://diducoder.com/sotr ...
- 一个Sqrt函数引发的血案(转)
作者: 码农1946 来源: 博客园 发布时间: 2013-10-09 11:37 阅读: 4556 次 推荐: 41 原文链接 [收藏] 好吧,我承认我标题党了,不过既然你来了, ...
- 事务隔离级别引发的"血案"
事务引发的"血案"见的多了也麻木了,这回遇到个事务隔离级别的"案子",坑了我小半天的时间...也怪自己细节不牢.. 敲着代码遇到这么一个怪事情: class X ...
- 一个无锁消息队列引发的血案(五)——RingQueue(中) 休眠的艺术
目录 (一)起因 (二)混合自旋锁 (三)q3.h 与 RingBuffer (四)RingQueue(上) 自旋锁 (五)RingQueue(中) 休眠的艺术 (六)RingQueue(中) 休眠的 ...
- 【转载】一个Sqrt函数引发的血案
转自:http://www.cnblogs.com/pkuoliver/archive/2010/10/06/sotry-about-sqrt.html 源码下载地址:http://diducoder ...
- 一个Sqrt函数引发的血案
源码下载地址:http://diducoder.com/sotry-about-sqrt.html 好吧,我承认我标题党了,不过既然你来了,就认真看下去吧,保证你有收获. 我们平时经常会有一些数据运算 ...
- [WCF]缺少一行代码引发的血案
这是今天作项目支持的发现的一个关于WCF的问题,虽然最终我只是添加了一行代码就解决了这个问题,但是整个纠错过程是痛苦的,甚至最终发现这个问题都具有偶然性.具体来说,这是一个关于如何自动为服务接口(契约 ...
- dubbox微服务实例及引发的“血案”
Dubbo 是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成. 主要核心部件: Remoting: 网络通信框架 ...
- Integer.parseInt 引发的血案
Integer.parseInt 处理一个空字符串, 结果出错了, 程序没有注意到,搞了很久, 引发了血案啊!! 最后,终于 观察到了, 最后的部分: Caused by: java.lang.NoC ...
随机推荐
- vagrant 创建虚拟机时遇到问题
问题1 : ceph-node3: Warning: Authentication failure. Retrying.. 问题分析: ssh 认证失败,在向虚拟机拷贝内容时权限不足. 解决办法: ...
- 转载:MyBatis mapper.xml中使用静态常量或者静态方法
转自:https://my.oschina.net/wtslh/blog/682704 今天偶然之间刷到了这样一篇博客,有点意外 mybatis 还可以这样使用ONGL常量的方式,该方式针对 xml的 ...
- 分布式ID系列(3)——数据库自增ID机制适合做分布式ID吗
数据库自增ID机制原理介绍 在分布式里面,数据库的自增ID机制的主要原理是:数据库自增ID和mysql数据库的replace_into()函数实现的.这里的replace数据库自增ID和mysql数据 ...
- Go和Python学习计划
计划虽然不一定能实现,但还是要有的,万一实现了呢. 一.学习Go 1.先看尚雪谷https://www.bilibili.com/video/av48141461/?p=12的go语言全套,把基础的过 ...
- JavaScript循环出现的问题——用闭包来解决
在for循环中,数组长度为3,我本来是想对每个循环的元素绑定一个点击事件的,结果点击后控制台输出全部为1. for (var i = 0; i < data.data.length; i++) ...
- Web很脆弱,SQL注入要了解
SQL注入 所谓SQL注入,就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令. 通过一下的例子更形象的了解SQL注入: 有一个Login ...
- RuntimeError: one of the variables needed for gradient computation has been modified by an inplace
vgg里面的 ReLU默认的参数inplace=True 当我们调用vgg结构的时候注意 要将inplace改成 False 不然会报错 RuntimeError: one of the variab ...
- Apache 配置 https
本人当前的Apache版本是: 由于我是yum安装的http,默认的http配置文件我就不多说了, 下面开始记录一下自己的线上配置过程: 1,进入/etc/httpd/conf.d目录,新建证书放 ...
- Ng-Matero 0.1 发布了!
Ng-Matero 0.1 终于发布了!周末折腾了两天,结果最后发版还是出了点意外,好在今天补了一版. 距离 Ng-Matero 发布第一版已经过去了一个多月,然后很颓废地休息了半个多月,最近项目的关 ...
- 常见rpm包和yum包命令
1.rpm包 在 安装.升级.卸载服务程序时要考虑到其他程序.库的依赖关系,在进行校验.安装. 卸载.查询.升级等管理软件操作时难度都非常大. RPM 机制则为解决这些问题而设计的.RPM 有点像 W ...