我正在为 MariaDB 中的一个查询优化问题摸不着头脑。我有一个包含 300 000 000 行的表,并且在 SEND_DATE 列上有一个索引,并且以下查询运行得非常快:
SELECT
*
FROM
MESSAGE
WHERE
SEND_DATE >= '2024-01-01 00:00:00'
AND SEND_DATE < '2024-06-01 00:00:00';
执行时间:0.044秒。 🚀 (加上获取时间 ~150 秒)
但是,当我将日期范围增加一个月时,如下所示:
SELECT
*
FROM
MESSAGE
WHERE
SEND_DATE >= '2024-01-01 00:00:00'
AND SEND_DATE < '2024-07-01 00:00:00';
查询突然显着变慢——执行时间跃升至 466 秒。 😱 (加上获取时间〜150秒)
MariaDB 似乎停止使用索引并切换到全表扫描。
有趣的是,当我使用 FORCE INDEX 强制索引时,查询再次快速运行,即使对于更大的日期范围也是如此:
SELECT
*
FROM
MESSAGE FORCE INDEX (INDEX_SEND_DATE)
WHERE
SEND_DATE >= '2024-01-01 00:00:00'
AND SEND_DATE < '2024-07-01 00:00:00';
使用 FORCE INDEX,查询将按预期执行。 🚀
如何将 MariaDB 配置为始终首选此类查询的索引,即使对于较大的日期范围也是如此?我不想在每个查询中使用 FORCE INDEX 手动强制索引。
非常感谢任何建议或见解! 😊
数据库版本:12.4.10
我做了分析格式=JSON 对于较小的日期范围:
{
"query_block": {
"select_id": 1,
"r_loops": 1,
"r_total_time_ms": 68531,
"table": {
"table_name": "MESSAGE",
"access_type": "range",
"possible_keys": ["INDEX_SEND_DATE"],
"key": "INDEX_SEND_DATE",
"key_length": "5",
"used_key_parts": ["SEND_DATE"],
"r_loops": 1,
"rows": 24805704,
"r_rows": 1.38e7,
"r_total_time_ms": 65458,
"filtered": 100,
"r_filtered": 100,
"index_condition": "MESSAGE.SEND_DATE >= '2024-01-01 00:00:00' and MESSAGE.SEND_DATE < '2024-05-01 00:00:00'"
}
}
}
对于更广泛的日期范围:
{
"query_block": {
"select_id": 1,
"r_loops": 1,
"r_total_time_ms": 632820,
"table": {
"table_name": "MESSAGE",
"access_type": "ALL",
"possible_keys": ["INDEX_SEND_DATE"],
"r_loops": 1,
"rows": 241889492,
"r_rows": 2.99e8,
"r_total_time_ms": 582567,
"filtered": 24.389,
"r_filtered": 8.9888,
"attached_condition": "MESSAGE.SEND_DATE >= '2024-01-01 00:00:00' and MESSAGE.SEND_DATE < '2024-08-01 00:00:00'"
}
}
}
这是优化器内置的设计考虑因素。
优化器查看索引的 BTree 以估计与范围匹配的行数。 如果超过某个阈值(通常约为 20%),它将决定表扫描速度会更快。 注意:没有实际的方法来获得更好的截止。
为什么这很重要? 让我解释一下索引是如何工作的(至少在你的例子中)。
SELECT *
)。注意来回和许多 BTree 查找。 这些都需要付出一些代价;很难知道有多少。
进行表扫描的另一种方法是查看表中的每一行,本质上是通过 B+Tree 进行顺序读取。 无需来回;无需深入 BTree。
结论:任何这些情况都会使使用索引比表扫描慢:
innodb_buffer_pool_size
操作系统对于任务来说太小了。看看有没有
SELECT * FROM Messages
WHERE SEND_DATE >= '2024-01-01'
AND SEND_DATE < '2024-08-01'
ORDER BY SEND_DATE;
工作起来会更快。 请注意,优化器可能会决定
ORDER BY
将避免排序,从而改变它使用的决策过程。