为什么 MariaDB 停止对更大的日期范围使用索引?

问题描述 投票:0回答:1

我正在为 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,查询将按预期执行。 🚀

我尝试过的:

  • 增加 eq_range_index_dive_limit – 没有效果。
  • 调整了 read_buffer_size 和 tmp_table_size – 没有改进。

我的问题:

如何将 MariaDB 配置为始终首选此类查询的索引,即使对于较大的日期范围也是如此?我不想在每个查询中使用 FORCE INDEX 手动强制索引。

非常感谢任何建议或见解! 😊

编辑:1

数据库版本: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'"
    }
  }
}
indexing mariadb database-performance
1个回答
0
投票

这是优化器内置的设计考虑因素。

优化器查看索引的 BTree 以估计与范围匹配的行数。 如果超过某个阈值(通常约为 20%),它将决定表扫描速度会更快。 注意:没有实际的方法来获得更好的截止。

为什么这很重要? 让我解释一下索引是如何工作的(至少在你的例子中)。

  1. 向下钻取索引的 B+Tree 到范围内的第一个值 ('2024-01-01 00:00:00')。
  2. 向前遍历索引,直到结束值。
  3. 对于找到的每个索引条目,钻取数据的 BTree 以查找所需的行 (
    SELECT *
    )。

注意来回和许多 BTree 查找。 这些都需要付出一些代价;很难知道有多少。

进行表扫描的另一种方法是查看表中的每一行,本质上是通过 B+Tree 进行顺序读取。 无需来回;无需深入 BTree。

结论:任何这些情况都会使使用索引比表扫描慢:

  • 大量 I/O 来获取数据块。
  • 所需的行分散在桌子周围。
  • 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
将避免排序,从而改变它使用的决策过程。

© www.soinside.com 2019 - 2024. All rights reserved.