MySQL 日期函数 NOW 和 SYSDATE 的区别

FreeGuideOnline 最新 2026-07-05

MySQL 日期函数 NOW 与 SYSDATE:核心区别与实战解析

在 MySQL 中获取当前日期和时间,你最常接触的两个函数莫过于 NOW()SYSDATE()。表面上看,它们都能返回当前的日期时间值,但深入理解它们的执行机理差异,对于编写准确、可靠的 SQL 语句至关重要,尤其是在涉及事务、复制和长时运行语句时。本教程将带你从现象到本质,彻底搞清楚这对“双胞胎”函数的区别。

一、表象:看似相同的返回值

我们先通过一组简单的查询来观察二者的基本行为。

SELECT NOW(), SYSDATE();

在绝大多数情况下,单次执行的输出结果几乎相同,都类似于 2025-03-22 14:35:10

SELECT NOW(), SLEEP(2), NOW();     -- 两个 NOW() 返回相同时间
SELECT SYSDATE(), SLEEP(2), SYSDATE(); -- 两个 SYSDATE() 可能返回不同时间

这里的差异开始显现:在同一个 SQL 语句中,多次调用 NOW() 返回的是语句开始执行时的时间常量;而多次调用 SYSDATE() 返回的是函数实际被调用时的实时时间。 这是理解两者区别的第一个关键点。

二、核心机制:执行时间与调用时间的对决

两种函数的本质区别在于它们对“当前时间”的获取时机不同。

2.1 NOW():语句级别的稳定快照

NOW() 返回的是 该语句开始执行的时刻 的时间。在语句执行期间,无论调用多少次,它都返回同一个不变的时间点。你可以将其视为一个在语句初始化时就被确定好的常量。

这一特性让 NOW() 在以下场景极具优势:

  • 数据一致性:在 INSERT 多行数据或批量更新时,同一个操作的所有行拥有完全一致的时间戳,便于后续审计和比对。
  • 复制安全:在主从复制中,基于语句的复制(Statement-Based Replication)能够安全地重现 NOW() 的值,因为它是一个确定的值。
  • 索引友好:在查询条件中使用 NOW(),其稳定性允许优化器更好地利用索引。

2.2 SYSDATE():函数级别的实时跟踪

SYSDATE() 返回的是 函数实际被执行时 的操作系统时间。如果一条语句耗时较长,或者内部调用了 SLEEP() 等阻塞函数,后续的 SYSDATE() 调用会反映出真实的时间流逝。

这一特性使其适用于:

  • 精确计时:在存储过程或长脚本中测量代码片段的实际耗时。
  • 记录真实执行瞬间:但需要注意,这也意味着它在同一条语句内可能是不确定的,这为复制和查询缓存带来了隐患。

三、深入理解决定性的系统变量:sysdate-is-now

MySQL 通过一个名为 sysdate-is-now 的特殊系统变量,提供了强制让 SYSDATE() 表现得像 NOW() 的开关。该变量在官方文档中被标记为“弃用”,但依然广泛存在。

  • 默认状态 (--sysdate-is-now=OFF 或不设置)SYSDATE() 表现出实时性,与 NOW() 不同。
  • 设置为 ON (--sysdate-is-now=ON)SYSDATE() 变为 NOW() 的别名,同样返回语句执行时的时间,失去实时性。

你可以通过以下方式查看当前会话的设置:

SHOW VARIABLES LIKE 'sysdate-is-now';

最佳实践建议: 不要修改该变量。因为它可能在未来版本被移除,且混用两种行为的语义会导致代码逻辑混乱。明确你的需求:如果需要语句级快照,就用 NOW();如果确实需要实时调用时间,就用 SYSDATE()

四、实战案例:不同场景下的行为对比

下面通过几个典型场景,直观展示差异可能带来的影响。

4.1 批量插入时的差异

假设有一个日志表,我们需要为每一行插入的时间戳打标记。

CREATE TABLE event_log (
    id INT AUTO_INCREMENT PRIMARY KEY,
    event_name VARCHAR(50),
    log_time_now DATETIME,
    log_time_sysdate DATETIME
);

-- 插入三条记录,中间故意延迟
INSERT INTO event_log (event_name, log_time_now, log_time_sysdate)
VALUES 
('Event A', NOW(), SYSDATE()),
('Event B', NOW(), SLEEP(1), SYSDATE()),
('Event C', NOW(), SYSDATE());

查询结果:

SELECT event_name, log_time_now, log_time_sysdate FROM event_log;

你会观察到:

  • log_time_now 的三条记录完全一致,均为整个 INSERT 语句开始执行时的时间。
  • log_time_sysdate 的记录中,'Event B' 的值比 'Event A' 和 'Event C' 晚大约1秒,因为它调用了 SLEEP(1),让 SYSDATE() 的调用时机推迟了。

4.2 触发器中的行为

在触发器里,NOW()SYSDATE() 的差异同样明显。触发器作为触发语句的一部分,其执行时间归属于触发语句本身。

CREATE TABLE trigger_test (
    id INT AUTO_INCREMENT PRIMARY KEY,
    insert_time_now DATETIME,
    trigger_time_now DATETIME,
    trigger_time_sysdate DATETIME
);

DELIMITER //
CREATE TRIGGER before_insert_test
BEFORE INSERT ON trigger_test
FOR EACH ROW
BEGIN
    SET NEW.trigger_time_now = NOW();
    SET NEW.trigger_time_sysdate = SYSDATE();
END //
DELIMITER ;

-- 执行插入时会自行设定 insert_time_now 列
INSERT INTO trigger_test (insert_time_now) VALUES (NOW());

执行后,insert_time_nowtrigger_time_now 永远相等,因为它们都基于同一语句开始时间。trigger_time_sysdate 则记录了触发器函数体被执行时那一刻的精确时间,可能完全相同,但在高并发或复杂触发条件下可能出现微秒级差异。

五、性能与复制影响:不可忽视的副产品

5.1 性能考量

  • NOW() 被视为确定性函数,在查询优化阶段可以参与常数折叠、缓存等优化。在只读视图中使用尤为安全。
  • SYSDATE() 在默认设置下是非确定性的,这会导致:
    • 无法使用查询缓存:任何包含 SYSDATE() 的语句结果都不会被缓存。
    • 阻碍索引使用:在 WHERE 条件中使用 SYSDATE() 时,优化器不会将其视为常量,可能放弃索引扫描。

5.2 复制影响

对于基于语句的复制(SBR):

  • NOW() 是安全的,主库上执行时的值会被记录并传递到从库。
  • SYSDATE() 由于不确定性,可能造成主从数据不一致。MySQL 在将 SYSDATE() 写入 binlog 时会自动将其替换为 NOW()(除非启用了 log_bin_use_v1_row_events 等行级复制特性),但这实际上改变了原始语句的语义。因此,在复制环境中,应尽量避免在关键 SQL 中依赖 SYSDATE() 的实时行为,首选基于行的复制(RBR)或直接使用 NOW()

六、选择指南:何时使用哪个函数?

场景需求 推荐函数 理由
为数据行添加创建/更新时间戳,要求整条语句一致 NOW() 保证同一事务或语句内所有时间戳相同,维护数据一致性。
记录日志,反映真实的调用顺序和间隔 SYSDATE() 能捕捉到毫秒级差异,适合调试和性能分析。
WHERE 条件中过滤当前时间的数据 NOW() 确定性常量,可利用索引,性能更佳。
主从复制环境下的写操作 NOW() 避免不确定性导致的复制问题。
测量存储过程内部代码块的执行耗时 SYSDATE() 在不同位置调用,计算差值可以获得真实流逝时间。

七、总结:一句话记住它们的区别

  • NOW():在语句开始时一次性赋值,语句内恒定,是稳定的时间常量。
  • SYSDATE():在函数每次被调用时返回实时系统时间,是动态的时间探针。

掌握这一区别后,你可以更精准地控制 SQL 中的时间逻辑。下次遇到需要当前时间的场景,先问自己一句:“我需要的是语句级别的统一快照,还是时刻变化的绝对实时?” 答案自明。

延伸练习: 尝试开启 MySQL 的通用查询日志(General Log),观察同一个存储过程中多次调用 NOW()SYSDATE() 时,日志中记录的具体语句和执行时间,加深理解。