当前位置: 首页 > article >正文

深入解析 MySQL 中的时间函数:NOW() 与 SYSDATE() 的奥秘

在 MySQL 中,NOW() 和 SYSDATE() 是两个常用的时间函数,它们都能返回当前时间,但背后的行为逻辑却截然不同。本文将结合 MySQL 8 的源码实现,深入探讨它们的核心区别、适用场景,以及如何通过设计优化性能和保证数据一致性。


一、基础用法与直观区别

1. NOW():事务的“时间快照”
 
-- 事务内多次调用 NOW(),返回相同的时间
START TRANSACTION;
SELECT NOW();  -- 返回时间 T1
SELECT SLEEP(2);
SELECT NOW();  -- 仍返回 T1
COMMIT;

特点

  • 在事务或查询开始时记录时间,后续调用均返回该缓存值。
  • 确保事务内时间一致性,适合需要原子性操作的场景。
2. SYSDATE():实时的“系统时钟”
 
-- 每次调用返回实时时间
SELECT SYSDATE();  -- 返回时间 T1
SELECT SLEEP(2);
SELECT SYSDATE();  -- 返回时间 T1+2

特点

  • 每次调用直接读取操作系统时间,结果随时间推移变化。
  • 适合需要精确实时性的场景(如监控日志),但事务中可能导致数据逻辑问题。

二、核心区别与设计原理

1. 事务一致性 vs 实时性
  • NOW()
    在查询开始时通过 THD(线程描述符)缓存时间,确保整个事务内所有操作基于同一时间基准。

     
    // 源码逻辑:从 THD 中获取缓存的查询开始时间
    void THD::set_time() {
      start_utime = my_micro_time();  // 记录系统时间到线程上下文
    }
  • SYSDATE()
    每次调用均触发系统调用(如 gettimeofday()),获取实时时间。

     
    // 源码逻辑:直接读取系统时间
    bool Item_func_sysdate_local::get_date(MYSQL_TIME *now_time) {
      const ulonglong tmp = my_micro_time();  // 实时获取系统时间
      thd->time_zone()->gmt_sec_to_TIME(now_time, tmp / 1000000);
    }
2. 性能开销
  • NOW()
    缓存机制减少系统调用,性能更高。
    适用场景:高频查询、事务处理。

  • SYSDATE()
    频繁系统调用增加 CPU 开销。
    适用场景:低频但需要高精度实时性的操作。

3. 时间精度控制

两者均支持指定小数位数(如 NOW(3) 返回毫秒级时间),但实现方式不同:

  • NOW(n):从缓存的微秒时间戳截断到指定精度。
     
    // 源码逻辑:截断微秒部分
    my_timeval THD::query_start_timeval_trunc(uint8 decimals) {
      my_timeval tv = {start_time.tv_sec, start_time.tv_usec};
      timeval_trunc(&tv, decimals);  // 如 decimals=3 → 保留毫秒
      return tv;
    }
  • SYSDATE(n):直接获取实时时间并截断。

三、源码解析:时间如何被缓存和传递?

1. NOW() 的时间缓存机制
  • 步骤 1:查询初始化
    执行 SQL 前,调用 THD::set_time() 记录时间到线程上下文。

     
    // sql/sql_class.cc
    void THD::set_time() {
      start_utime = my_micro_time();  // 微秒级时间戳
      my_micro_time_to_timeval(start_utime, &start_time);  // 转为 timeval 结构
    }
  • 步骤 2:调用 NOW()
    从 THD 中读取缓存的 start_time,生成格式化时间。

     
    // sql/item_timefunc.cc
    longlong Item_func_now::val_date_temporal() {
      MYSQL_TIME_cache tm;
      tm.set_datetime(thd->query_start_timeval_trunc(decimals), decimals, time_zone());
      return tm.val_packed();
    }
2. SYSDATE() 的实时获取
  • 每次调用执行系统调用,无缓存:
     
    // sql/item_timefunc.cc
    bool Item_func_sysdate_local::get_date(MYSQL_TIME *now_time) {
      const ulonglong tmp = my_micro_time();  // 实时获取时间
      thd->time_zone()->gmt_sec_to_TIME(now_time, tmp / 1000000);
    }

四、如何选择:NOW() 还是 SYSDATE()

1. 事务场景
  • 使用 NOW()
    确保事务内时间一致,例如订单创建时间、批量数据处理。

     
    INSERT INTO orders (create_time, ...) 
    VALUES (NOW(), ...);  -- 同一事务内所有订单的 create_time 相同
  • 避免 SYSDATE()
    可能导致时间跳跃,破坏事务原子性。

2. 监控与审计
  • 使用 SYSDATE()
    记录操作发生的精确时间,如登录日志、安全审计。
     
    INSERT INTO access_log (event_time, ...) 
    VALUES (SYSDATE(), ...);  -- 记录实时操作时间
3. 高并发场景
  • 优先 NOW()
    减少系统调用,提升性能。

五、总结

特性NOW()SYSDATE()
事务一致性✅ 同一事务内时间不变❌ 每次调用实时更新
性能✅ 高效(缓存机制)❌ 较高开销(频繁系统调用)
适用场景事务处理、高频查询实时监控、审计日志
精度控制支持(如 NOW(3)支持(如 SYSDATE(3)

理解 NOW() 和 SYSDATE() 的区别,能够帮助开发者根据场景选择更合适的函数,从而优化系统性能并避免潜在逻辑错误。MySQL 通过缓存机制和实时获取的平衡设计,为不同需求提供了灵活的时间处理方案。


最后的小测试
尝试在事务中混用 NOW() 和 SYSDATE(),观察结果差异。例如:

 
START TRANSACTION;
SELECT NOW(), SYSDATE(), SLEEP(2);
SELECT NOW(), SYSDATE();
COMMIT;

你会发现:NOW() 保持不变,而 SYSDATE() 随时间推移递增!这就是 MySQL 时间处理的精妙之处。


http://www.kler.cn/a/568932.html

相关文章:

  • TCP的四次挥⼿为什么是四次?为什么不能是三 次
  • 【计算机网络——概述】
  • 深搜专题7:最大质数
  • 【基于Raft的KV共识算法】-序:Raft概述
  • JavaEE基础之- 过滤器和监听器Filter and Listener
  • Deepseek 模型蒸馏
  • 每日OJ_牛客_NC316体育课测验(二)_拓扑排序_C++_Java
  • FPGA开发,使用Deepseek V3还是R1(3):系统级与RTL级
  • Ubuntu 下 nginx-1.24.0 源码分析 - ngx_init_cycle 函数 - 详解(8)
  • Ubuntu 20.04 安装 Node.js 20.x、npm、cnpm 和 pnpm 完整指南
  • LangPrompt提示词
  • 基于单片机的GPS定位系统设计
  • ETF期权的结算价如何结算?
  • 深度解析Ant Design Pro 6开发实践
  • 【MySQL】(2) 库的操作
  • 基于STM32的智能家居中控系统
  • 【定昌Linux系统】部署了java程序,设置开启启动
  • AndroidStudio下载旧版本方法
  • 16.5 LangChain LCEL 流式处理解密:构建实时交互式大模型应用的引擎
  • 【实战 ES】实战 Elasticsearch:快速上手与深度实践-2.1.2字段类型选择:keyword vs text、nested对象