You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌套查询WHERE子句未提升MySQL窗口函数查询性能求优化

优化含RANK窗口函数的MySQL高负载查询

我在MySQL中运行一个包含RANK窗口函数的高负载查询,原本试图通过嵌套查询的WHERE子句过滤retrieveDate来减少处理数据量,但执行EXPLAIN后发现,无论将过滤范围设为1周、1个月还是2个月,性能都没有任何提升,求优化方案。

原查询语句

SELECT 
    t1.*,
    RANK () OVER (
        PARTITION BY 
            t1.`country`, 
            t1.`product_id`, 
            t1.`retrieveDate`, 
            t1.`retrieveHour` 
        ORDER BY 
            t1.`retrieveDatetime` DESC) AS `ranking`
FROM (
    SELECT * FROM product_data
    WHERE retrieveDate > (CURRENT_DATE() - INTERVAL 1 WEEK)
) t1
WHERE t1.ranking = 1

EXPLAIN输出结果

{
    "query_block": {
      "select_id": 1,
      "cost_info": {
        "query_cost": "355813.66"
      },
      "windowing": {
        "windows": [
          {
            "name": "<unnamed window>",
            "using_filesort": true,
            "filesort_key": [
              "`product_id`",
              "`country`",
              "`retrieveDate`",
              "`retrieveHour`",
              "`retrieveDatetime` desc"
            ],
            "functions": [
              "rank"
            ]
          }
        ],
        "cost_info": {
          "sort_cost": "271814.81"
        },
        "table": {
          "table_name": "product_data",
          "access_type": "ALL",
          "rows_examined_per_scan": 815526,
          "rows_produced_per_join": 271814,
          "filtered": "33.33",
          "cost_info": {
            "read_cost": "2446.25",
            "eval_cost": "27181.48",
            "prefix_cost": "83998.85",
            "data_read_per_join": "553M"
          },
          "used_columns": [
            "product_id",
            "country",
            "category",
            "rank",
            "primaryCategory",
            "primaryCategoryRank",
            "retrieveDatetime",
            "createdAt",
            "updatedAt",
            "retrieveDate",
            "retrieveHour"
          ],
          "attached_condition": "(`product_data`.`retrieveDate` > <cache>((curdate() - interval 1 week)))"
        }
      }
    }
  }

现有索引

  • BTree复合索引:product_id(varchar, 位置1)、country(int, 位置2)、retrieveDate(date, 位置13)、retrieveHour(int, 位置14)、retrieveDatetime(datetime, 位置10)
  • 独立的retrieveDatetime单字段索引

优化方案

1. 调整复合索引顺序,适配业务逻辑

当前复合索引的字段顺序无法被窗口函数的PARTITION BY和ORDER BY有效利用,需要创建覆盖索引,字段顺序遵循:

  • 先放WHERE过滤字段:retrieveDate
  • 再放PARTITION BY分组字段:country、product_id、retrieveHour
  • 最后放ORDER BY排序字段:retrieveDatetime(DESC)

创建索引语句:

CREATE INDEX idx_prod_data_date_country_prod_hour_dt ON product_data 
(retrieveDate, country, product_id, retrieveHour, retrieveDatetime DESC);

该索引可直接满足过滤、分组、排序需求,消除全表扫描和文件排序(EXPLAIN中using_filesort会消失)。

2. 避免SELECT *,使用字段列表

原查询用SELECT *会读取大量冗余字段,增加IO开销。明确列出需要的字段,配合覆盖索引实现索引-only扫描,进一步提升性能。

修改后查询示例:

SELECT 
    t1.country,
    t1.product_id,
    t1.retrieveDate,
    t1.retrieveHour,
    t1.retrieveDatetime,
    -- 其他实际需要的字段
    RANK () OVER (
        PARTITION BY 
            t1.country, 
            t1.product_id, 
            t1.retrieveDate, 
            t1.retrieveHour 
        ORDER BY 
            t1.retrieveDatetime DESC) AS ranking
FROM (
    SELECT 
        country,
        product_id,
        retrieveDate,
        retrieveHour,
        retrieveDatetime
        -- 其他实际需要的字段
    FROM product_data
    WHERE retrieveDate > (CURRENT_DATE() - INTERVAL 1 WEEK)
) t1
WHERE t1.ranking = 1

3. 用分组聚合替代窗口函数(可选)

如果仅需每个分组的最新一条记录,可替换RANK函数为分组聚合,避免窗口函数的排序开销:

SELECT 
    pd.country,
    pd.product_id,
    pd.retrieveDate,
    pd.retrieveHour,
    pd.retrieveDatetime
    -- 其他实际需要的字段
FROM product_data pd
JOIN (
    SELECT 
        country,
        product_id,
        retrieveDate,
        retrieveHour,
        MAX(retrieveDatetime) AS max_datetime
    FROM product_data
    WHERE retrieveDate > (CURRENT_DATE() - INTERVAL 1 WEEK)
    GROUP BY country, product_id, retrieveDate, retrieveHour
) pd_max 
ON pd.country = pd_max.country
AND pd.product_id = pd_max.product_id
AND pd.retrieveDate = pd_max.retrieveDate
AND pd.retrieveHour = pd_max.retrieveHour
AND pd.retrieveDatetime = pd_max.max_datetime
WHERE pd.retrieveDate > (CURRENT_DATE() - INTERVAL 1 WEEK);

该写法可复用上述复合索引,分组聚合效率更高。

4. 验证索引效果

创建新索引后执行EXPLAIN,需确认:

  • access_type变为range或ref,不再是ALL(全表扫描)
  • Extra字段显示Using index(覆盖索引扫描),无Using filesort

内容的提问来源于stack exchange,提问作者Michael Du

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 05:15:44