暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

隐藏13年!MySQL这个函数刚刚修复,你的代码还安全吗?

数据库干货铺 2026-01-12
74

一行简单的SQL查询,在MySQL不同版本中竟然得出截然相反的结果。是BUG,还是隐藏陷阱?

近日,一位开发者在测试时偶然发现,同一个SQL查询语句在不同MySQL版本中返回了完全不同的结果。这背后涉及到一个存在了13年的BUG,直到不久前才被修复。

1. 一个令人困惑的发现

开发者在一个项目中执行了这样一条SQL语句:

    SELECT UNIX_TIMESTAMP('2040-01-01');

    在MySQL 8.0.25中,返回结果是0。但在MySQL 8.0.39中,却返回了正确的时间戳2208960000。

    更令人困惑的是,当尝试在MySQL 8.4中测试时,结果与8.0.39一致,都是正确的2208960000。

    为什么同一个函数在不同版本中行为如此不同?是配置问题,还是MySQL的隐藏BUG?

    2. 深入探究:UNIX_TIMESTAMP的真相

    要理解这个问题,首先要区分两个容易混淆的概念:UNIX_TIMESTAMP函数和TIMESTAMP数据类型。

    TIMESTAMP数据类型是MySQL中用于存储日期时间的数据类型,它有一个著名的限制——2038年问题。这是由于在底层,TIMESTAMP使用32位有符号整数存储自'1970-01-01 00:00:00' UTC以来的秒数,最大只能表示到2038-01-19 03:14:07。

    UNIX_TIMESTAMP函数则是一个日期时间函数,用于将日期时间值转换为Unix时间戳(整数)。理论上,在64位系统上,这个函数应该能够处理更广的时间范围。

    然而,在MySQL 8.0.28之前的版本中,即使运行在64位系统上,UNIX_TIMESTAMP()
    函数对2038年后的日期也会错误地返回0,实际上错误地应用了32位的限制。

    官方修复:8.0.28版本的重大更新

    MySQL官方在8.0.28版本中修复了这个BUG(编号#104787)。官方更新日志中明确写道:

    在64位平台上,UNIX_TIMESTAMP()
    函数现在支持从'1970-01-01 00:00:01'
    UTC到'3001-01-19 03:14:07'
    UTC的日期范围。在此之前,晚于'2038-01-19 03:14:07'
    UTC的日期会导致函数返回0。

    这个修复意味着:

    • MySQL 8.0.25及之前版本:UNIX_TIMESTAMP('2040-01-01')
      返回 0(错误)

    • MySQL 8.0.28及之后版本:UNIX_TIMESTAMP('2040-01-01')
      返回 2208960000(正确)


    3. 案例演示:版本差异对比

    让我们通过实际代码来看看这个差异:

      -- 在MySQL 8.0.25或更早版本中
      SELECT UNIX_TIMESTAMP('2040-01-01'); 
      -- 返回 0
      -- 在MySQL 8.0.28或更新版本中  
      SELECT UNIX_TIMESTAMP('2040-01-01'); 
      -- 返回 2208960000
      -- 在MySQL 8.4中
      SELECT UNIX_TIMESTAMP('2040-01-01'); 
      -- 返回 2208960000





      4. 一个重要警告:数据类型限制依然存在

      虽然UNIX_TIMESTAMP()
      函数现在可以正确处理2038年后的日期计算,但TIMESTAMP数据类型的2038年限制仍然存在。

      历史文章已经说明原因MySQL时间戳2038年灾难:你的数据还能撑过去吗?

      这是一个关键区别:

        -- 1. UNIX_TIMESTAMP函数可以计算2038年后的时间戳
        SELECT UNIX_TIMESTAMP('2040-01-01'); 
        -- 返回 2208960000(正确)
        -- 2. 但无法将2038年后的日期存入TIMESTAMP列
        CREATE TABLE test_table (id INT,
        event_time TIMESTAMP
        );
        -- 这行会失败或产生警告
        INSERT INTO
         test_table 
        VALUES
         (
        1
        , 
        '2040-01-01 00:00:00'
        );
        -- 3. 存储2038年后日期应该使用DATETIME
        CREATE TABLE
         future_events (id INT,
            event_time DATETIME
        );
        INSERT INTO
         future_events 
        VALUES
         (
        1
        , 
        '2040-01-01 00:00:00'
        ); 
        -- 成功




        5. 给开发者的建议

        基于这个发现,我们向所有MySQL开发者提出以下建议:

        • 检查MySQL版本:如果你在代码中使用UNIX_TIMESTAMP()
          函数处理未来日期,请确认你的MySQL版本是否为8.0.28或更高。如果不是,函数对2038年后日期的处理可能是错误的

        • 明确区分用途:使用UNIX_TIMESTAMP()
          进行日期计算,但存储未来日期时优先使用DATETIME
          类型

        • 代码审查:检查现有代码中是否有依赖UNIX_TIMESTAMP()
          处理未来日期的逻辑,特别是涉及长期订阅、未来事件等功能的部分

        • 测试覆盖:确保测试用例包含2038年前后的日期处理验证

        • 升级计划:如果还在使用8.0.28之前的版本,考虑制定升级计划,特别是当你的应用需要处理未来日期时


        6. 深入思考:为什么这个BUG存在这么久?

        这个BUG在MySQL中至少存在了13年,为什么直到现在才被修复?这反映出几个问题:

        • 2038年看似遥远:很多系统设计时都认为2038年还很遥远,直到最近几年,随着长期合同、订阅制服务的普及,处理2038年后日期的需求才真正出现。

        • 测试覆盖不足:很少有测试用例会包含2038年后的日期验证。

        • 混淆与误解:许多开发者混淆了UNIX_TIMESTAMP()
          函数和TIMESTAMP
          数据类型的限制。


        7. 小结

        MySQL 8.0.28中对UNIX_TIMESTAMP()
        函数的修复,虽然只是版本更新日志中的一小行,却解决了未来十多年时间处理的重要问题。作为开发者,我们需要:

        • 理解这个变化及其对现有系统的影响

        • 在适当的时候升级MySQL版本

        • 重新审视与时间处理相关的代码逻辑

        • 在设计新系统时,为2038年后的世界做好准备

        时间处理是软件开发中最复杂、最容易出错的领域之一。每一个时间相关的BUG修复,都让我们向更健壮的系统迈进一步。

        你的系统中是否也有类似的“时间炸弹”?欢迎在留言分享你的经历和见解。


        参考资料:

        • MySQL 8.0.28 Change Log: Bug #104787MySQL 8.4 Reference Manual: UNIX_TIMESTAMP() FunctionYear 2038 Problem: https://en.wikipedia.org/wiki/Year_2038_problem


        文章转载自数据库干货铺,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

        评论