编程

为什么 whereDate() 可能会影响性能以及如何修复

110 2025-04-08 17:23:00

你是否曾经运行过一个 Laravel 作业,突然开始超时,你盯着你的 SQL 想知道为什么?

下例是我调试一个长时运行的利息计算作业,直到我发现了罪魁祸首:

->whereDate('created_at', '>=', $from)
->whereDate('created_at', '<=', $to)

WHERE DATE(`created_at`) >= '2024-06-10'
  AND DATE(`created_at`) <= '2024-06-17'

看起来很无辜,对吧?但是 whereDate()created_at 字段包装在 DATE() 中,这会禁用你的索引。

这意味着 MySQL 必须扫描每一行以进行匹配,即使你的 created_at 字段已被索引。

即使你一直在使用 Eloquent,当你使用 ->whereDate() 时,它仍然会扫描整个表,因为在底层它生成的是这样的原始 SQL:

->whereDate('created_at', '>=', $from)

生成:

WHERE DATE(created_at) >= '2024-06-10'

这就禁用了索引。

如何修复?使用完整的时间戳:

->where('created_at', '>=', $from->startOfDay())
->where('created_at', '<=', $to->endOfDay())

这样原始 SQL 为:

created_at BETWEEN '2024-06-10 00:00:00' AND '2024-06-10 23:59:59'

而且确实使用了 created_at 的索引。

曾经超时的查询现在在几毫秒内运行,因为索引的使用。