记一次 MySQL timestamp 精度问题的排查 → 过程有点曲折

这篇具有很好参考价值的文章主要介绍了记一次 MySQL timestamp 精度问题的排查 → 过程有点曲折。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

开心一刻

  下午正准备出门,跟正刷着手机的老妈打个招呼

  我:妈,今晚我跟朋友在外面吃,就不在家吃了

  老妈拿着手机跟我说道:你看这叫朋友骗缅北去了,tm血都抽干了,多危险

  我:那是他不行,你看要是吴京去了指定能跑回来

  老妈:还吴京八经的,特么牛魔王去了都得耕地,唐三藏去了都得打出舍利,孙悟空去了都得演大马戏

  我:那照你这么说,唐僧师徒取经走差地方了呗

  老妈:那可没走错,他当年搁西安出发,他要是搁云南出发呀,上午到缅北,下午他就到西天

  我:哈哈哈,那西游记就两级呗,那要是超人去了呢?

  老妈:那超人去了,回来光剩超,人留那了

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

问题复现

  我简化下业务与项目

  数据库: MySQL 8.0.25 

  基于 spring-boot 2.2.10.RELEASE 搭建 demo :spring-boot-jpa-demo

  表: tbl_user 

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  测试代码:

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折
/**
 * @description: xxx描述
 * @author: 博客园@青石路
 * @date: 2024/1/9 21:42
 */
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class UserTest {

    @Resource
    private UserRepository userRepository;

    @Test
    public void get() {
        DateTimeFormatter dft = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
        Timestamp lastModifiedTime  = Timestamp.valueOf(LocalDateTime.parse("2024-01-11 09:33:26.643", dft));

        // 1.先保存一个user
        User user = new User();
        user.setUserName("zhangsan");
        user.setPassword("zhangsan");
        user.setBirthday(LocalDate.now().minusYears(25));
        user.setLastModifiedTime(lastModifiedTime);
        log.info("user.lastModifiedTime = {}", user.getLastModifiedTime());
        userRepository.save(user);
        log.info("user 保存成功,userId = {}", user.getUserId());

        // 2.然后再根据id查询这个user
        Optional<User> userOptional = userRepository.findById(user.getUserId());
        if (userOptional.isPresent()) {
            log.info("从数据库查询到的user,user.lastModifiedTime = {}", userOptional.get().getLastModifiedTime());
        }
    }
}
View Code

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  这么清晰的代码,大家都能看懂吧?

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  我们来看下日志输出

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  保存的时候, lastModifiedTime 的值是 2024-01-11 09:33:26.643 ,从数据库查询得到的却是: 2024-01-11 09:33:27.0 

  是不是被震惊到了?

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

曲折排查

  先确认下 MySQL 表中存的值是多少

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  数据库表中的值就是 2024-01-11 09:33:27 ,此刻我只想来一句:卧槽!

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  这说明数据入库有问题,而不是读取有问题

  我们来梳理下数据入库经历了哪些环节

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  那问题肯定出在 Spring Data JPA 至 mysql-connector-java 之间

   MySQL 肯定是没问题的!

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  源码跟踪

  既然问题出在 Spring Data JPA 与 mysql-connector-java 之间,那么我们就直接来个一穿到底,翻了它的源码老底

  大家请坐好,我要开始装逼了

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

   JPA 用的少,一时还不知道从哪里开始去跟源码,但不要慌,楼主有 葵花宝典 :杂谈篇之我是怎么读源码的,授人以渔

  断点追踪源码,一时用一时爽,一直用一直爽

  直接在 userRepository.save(user) 前面打个断点,然后一步一步往下跟,我就不细跟了,我只在容易跟丢的地方指出来,给你们合适的方向

  当断点到 SessionImpl#firePersist 方法时

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  我们应该去跟 PersistEventListener::onPersist 了,一路跟下去,会来到 AbstractSaveEventListener#performSaveOrReplicate 方法

  里面有如下代码

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  添加的 Action 的实际类型是: EntityIdentityInsertAction 

  这里涉及到了 hibernate 的 事件机制 ,简单来说就是 EntityIdentityInsertAction 的 execute 方法会被调用

  所以我们继续从 EntityIdentityInsertAction#execute 跟,会来到 GetGeneratedKeysDelegate#executeAndExtract 

  重点来了,大家打起精神

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  继续跟进 session.getJdbcCoordinator().getResultSetReturn().executeUpdate( insert ) 的 executeUpdate 

  它长这样

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  如果不是断点跟的话

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  你知道接下来跟谁吗?

  当然,非常熟悉源码的人(比如我),肯定知道跟谁

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  但是用了断点,大家都知道跟谁了

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  继续往下跟,当我们来到 ClientPreparedStatement#executeInternal 时,真相已经揭晓

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  此时已经来到了 mysql-connector-java ,发送给 MySQL Server 的 SQL 是:

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

   last_modified_time 精度没丢

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  那问题出在哪?

  还能出在哪, MySQL 呗!

  说好的 MySQL 没问题的了?

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  MySQL 时间精度

  用排除法,排的只剩 MySQL 了,直接执行 SQL 试试

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  哦豁,敢情前面的源码分析全白分析了,我此刻的心情你们懂吗

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  这必须得找 MySQL 要个说法,真是太狗了

  我们去 MySQL 官方文档找找看(注意参考手册版本要和我们使用的 MySQL 版本一致)

  大家不要通篇去读,那样太费时间,直接 search 用起来

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  The DATE, DATETIME, and TIMESTAMP Types 有这么一段比较关键

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  我给大家翻译一下

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  继续看 Fractional Seconds in Time Values,内容不多,大家可以通篇读完

   MySQL 的 TIME , DATETIME 和 TIMESTAMP 都支持微妙级别(6位数)的小数位

  精度直接在括号中指定,例如: CREATE TABLE t1 (t TIME(3), dt DATETIME(6)) 

  小数位的范围是 0 到 6。0 表示没有小数部分,如果小数位缺省,则默认是0(SQL规范规定的默认是 6,MySQL8 默认值取 0 是为了兼容 MySQL 以前的版本

  当插入带有小数部分的 TIME , DATETIME 或 TIMESTAMP 值到相同类型的列时,如果值的小数位与精度不匹配时,会进行四舍五入

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  四舍五入的判断位置是精度的后一位,比如精度是 0,则看值的第 1 位小数,来决定是舍还是入,如果精度是 2,则看值的第 3 位小数

  简单来说:值的精度大于列类型的精度,就会存在四舍五入,否则值是多少就存多少

  当发生四舍五入时,既不会告警也不会报错,因为这就是 SQL 规范

  那如果我不想要四舍五入了,有没有什么办法?

   MySQL 也给出了支持,就是启用 SQL mode :TIME_TRUNCATE_FRACTIONAL

  启用之后,当值的精度大于列类型的精度时,就是直接按列类型的精度截取,而不是四舍五入

  那这么看下来,不是 MySQL 的锅呀, MySQL 表示这锅我不背

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  那是谁的锅?

  只能说是开发人员的锅,为什么不按 MySQL 使用说明书使用?

  我要强调的是,产生这次问题的代码不是我写的,我写的代码怎么可能有 bug 

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

总结

  1、 源码 debug 堆栈

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  2、MySQL 时间精度

     MySQL 的 TIME , DATETIME 和 TIMESTAMP 类型都支持微妙级别(6位数)的精度

    默认情况下会四舍五入,若想直接截断,则需要开启 SQL mode : TIME_TRUNCATE_FRACTIONAL 

  3、规范

    阿里巴巴的开发手册中明确指出不能用: java.sql.Timestamp 

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

    另外很多公司的 MySQL 开发规范会强调:没有特殊要求,时间类型用 datetime 

    主要出于两点考虑:1、 datetime 可用于分区,而 timestamp 不行,2、 timestamp 的范围只到 2038-01-19 03:14:07.499999 

    有的开发小伙伴可能会问:如果到了 2038-01-19 03:14:07.499999 之后, timestamp 该怎么办?

    我只能说:小伙子你想的太远了, 2038 跟我们有什么关系,影响我们送外卖吗?

补充

  关于上面讲到的 timestamp 不能分区,进行一下补充(感谢 @xiaohuazi 指正)

  它能分区,但是和  DATE  和 DATETIME  有一丢丢区别

  MySQL 5.7 说明如下

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

  MySQL 8.0 说明如下

记一次 MySQL  timestamp 精度问题的排查 → 过程有点曲折

   timestamp 类型的列只能基于 UNIX_TIMESTAMP 函数进行分区文章来源地址https://www.toymoban.com/news/detail-789622.html

到了这里,关于记一次 MySQL timestamp 精度问题的排查 → 过程有点曲折的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 记一次Native memory leak排查过程

    路由计算服务是路由系统的核心服务,负责运单路由计划的计算以及实操与计划的匹配。在运维过程中,发现在长期不重启的情况下,有TP99缓慢爬坡的现象。此外,在每周例行调度的试算过程中,能明显看到内存的上涨。以下截图为这两个异常情况的监控。 TP99爬坡 内存爬坡

    2024年02月11日
    浏览(35)
  • 记一次Elasticsearch GeoIpDownloader的启动异常排查过程

    最近碰到了Elasticsearch GeoIpDownloader相关的一个异常,花费了不少精力排查,故此记录一下,希望碰到同样问题的童鞋们少走弯路。 这个异常是在Elasticsearch启动的过程中报的error,如下所示,从提示信息来看是因为GeoIpDownloader更新数据库失败导致。 GeoIpDownloader是用于下载地图数

    2024年02月02日
    浏览(34)
  • 记一次.Net Core程序启动失败的排查过程

    阅文时长 | 2分钟 字数统计 | 3212字符 主要内容 | 1、引言背景 2、排查.NetCore启动失败详细过程 3、声明与参考资料 『记一次.Net Core程序启动失败的排查过程』 编写人 | SCscHero 编写时间 | 2021/12/23 PM2:6 文章类型 | 系列 完成度 | 已完成 座右铭 每一个伟大的事业,都有一个微不足

    2024年02月05日
    浏览(42)
  • JAVA开发(记一次504 gateway timeout错误排查过程)

    一、问题与背景: 最近在发布一个web项目,在测试环境都是可以的,发布到生产环境通过IP访问也是可以的,但是通过域名访问就出现504 gateway timeout。通过postman去测试接口也是一样。ip和端口都可以通,域名却不行,百思不得其解。通过一顿百度搜索,解析说通过nginx配置文

    2024年02月11日
    浏览(33)
  • 记一次 Mockito.mockStatic 泄漏导致的单元测试偶发报错排查过程

    相信用 Java 写过单元测试的读者们对 Mockito 不会陌生。至于 Mockito 是什么,为什么要用 Mockito,本文不再赘述。本文记录了一次在 Apache ShardingSphere 项目中,由 Mockito.mockStatic 使用不当导致的单元测试偶发报错排查过程。 Mockito 自 3.4.0 起新增了一个方法 Mockito.mockStatic ,支持对

    2024年02月10日
    浏览(46)
  • 记一次服务器被挖矿的排查过程:xmrig挖矿病毒

    【阿里云】尊敬的aliyun98****8825: 经检测您的阿里云服务(ECS实例)i-0jl8awxohyxk****axz5存在挖矿活动。根据相关法规、政策的规定,请您于2023-07-18 00时前完成挖矿问题整改,否则您的服务将被关停,详情请查看邮件或阿里云站内消息通知。 若您有其他问题,可登陆阿里云官网在

    2024年02月11日
    浏览(39)
  • 记一次docker启动失败的问题排查

    以前在虚拟机上安装了一个docker,可以正常使用的,今天突然宿主机机器内存条坏了,换了内存条后启动机器,再使用 systemctrl start docker 启动docker,最后使用 docker start containID 启动报错 网上没有找到相应的描述,仔细分析看是 write /proc/sys/kernel/shmmni 报错了,错误原因是 in

    2024年02月14日
    浏览(51)
  • 【记一次线上事故的排查思路】- CPU飙升问题排查

    由于项目排期较紧,临时从其他组调来三个开发资源帮我一起做项目,难免上线的时候大家的需求一块上线。 问题来了,上线三天后,线上CPU总是莫名奇妙的突然飙升,飙升后CPU并未降下来,而是一直处在高点。 由于是线上导致的问题,CPU超限后,会自动重启项目,未能保

    2024年01月23日
    浏览(41)
  • 记一次Apache HTTP Client问题排查

    通过日志查看,存在两种异常情况。 第一种:开始的时候HTTP请求会报超时异常。 762663363 [2023-07-21 06:04:25] [executor-64] ERROR - com.xxl.CucmTool - CucmTool|sendRisPortSoap error,url:https://xxxxxx/realtimeservice/services/RisPort org.apache.http.conn.HttpHostConnectException: Connect to xxx [/xxx] failed: 连接超时 第二种

    2024年02月12日
    浏览(49)
  • 记一次jedis连接池顽固问题排查与修改

    这辈子不想再看到jedisBrokenPipe!!   测试环境运行16天后报错信息: 05:42:32.629 [http-nio-8093-exec-2] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - [log,175] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is redis.clients.jedis.exceptions.JedisCon

    2023年04月21日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包