为什么 whereDate() 可能会影响性能以及如何修复
你是否曾经运行过一个 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
的索引。
曾经超时的查询现在在几毫秒内运行,因为索引的使用。