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

MySQL大表更新玩家周排名超时 求高效优化方案

优化周排名更新查询,避免超时问题

你的核心问题在于原查询使用了关联子查询,每一行数据都需要执行两次嵌套子查询来计算排名——对于10万行的表来说,这会产生百万级的重复查询,自然会超时。加上如果没有合适的索引,数据库需要全表扫描多次,性能雪上加霜。下面是几个高效的优化方案,按推荐程度排序:

1. 优先使用窗口函数(MySQL 8.0+ 推荐)

MySQL 8.0及以上支持窗口函数,能一次性完成分组排名计算,效率比关联子查询高几个数量级。根据你的示例(相同分数的玩家共享排名,不跳过名次),应该用DENSE_RANK()函数:

WITH ranked_players AS (
    SELECT 
        player_id,
        yearweek,
        DENSE_RANK() OVER (PARTITION BY yearweek ORDER BY ranking_points DESC) AS new_ranking_pos
    FROM players_weekly_rankings
)
UPDATE players_weekly_rankings pr
JOIN ranked_players rp 
    ON pr.player_id = rp.player_id 
    AND pr.yearweek = rp.yearweek
SET pr.ranking_pos = rp.new_ranking_pos;

为什么这更高效?

  • 窗口函数只需要一次全表扫描就能完成所有分组的排名计算,而原查询每一行都要单独计算。
  • 配合合适的索引(下文会提到),扫描效率会进一步提升。

2. 为旧版本MySQL使用变量+临时表(MySQL 5.x)

如果你的MySQL版本不支持窗口函数,可以用用户变量来逐行计算排名,再通过临时表批量更新:

-- 创建临时表存储计算好的排名(添加主键加速关联)
CREATE TEMPORARY TABLE temp_ranks (
    player_id INT,
    yearweek VARCHAR(7),
    new_ranking_pos INT,
    PRIMARY KEY (player_id, yearweek)
);

-- 初始化变量
SET @current_yearweek = '';
SET @current_points = NULL;
SET @rank = 0;

-- 计算每个玩家的排名并插入临时表
INSERT INTO temp_ranks (player_id, yearweek, new_ranking_pos)
SELECT 
    player_id,
    yearweek,
    -- 按年周分组,相同分数保持排名,不同分数递增
    CASE 
        WHEN yearweek != @current_yearweek THEN 
            @rank := 1
        WHEN ranking_points != @current_points THEN 
            @rank := @rank + 1
        ELSE 
            @rank
        END AS new_ranking_pos,
    -- 更新变量状态
    @current_yearweek := yearweek,
    @current_points := ranking_points
FROM players_weekly_rankings
ORDER BY yearweek, ranking_points DESC;

-- 批量更新原表
UPDATE players_weekly_rankings pr
JOIN temp_ranks tr 
    ON pr.player_id = tr.player_id 
    AND pr.yearweek = tr.yearweek
SET pr.ranking_pos = tr.new_ranking_pos;

-- 清理临时表
DROP TEMPORARY TABLE temp_ranks;

3. 必须添加的索引优化

不管用哪种方案,都需要给表添加复合索引,让数据库快速按年周分组并按分数排序:

CREATE INDEX idx_yearweek_points ON players_weekly_rankings(yearweek, ranking_points DESC);

这个索引能让数据库在计算排名时,直接按yearweek分组,同时按ranking_points降序获取数据,避免全表扫描。

4. 可选:分批更新减少压力

如果单次更新10万行仍然超时,可以按yearweek分批处理,每次只更新一个周的数据:

-- 示例:先更新2020/01的排名
WITH ranked_players AS (
    SELECT 
        player_id,
        DENSE_RANK() OVER (ORDER BY ranking_points DESC) AS new_ranking_pos
    FROM players_weekly_rankings
    WHERE yearweek = '2020/01'
)
UPDATE players_weekly_rankings pr
JOIN ranked_players rp ON pr.player_id = rp.player_id
SET pr.ranking_pos = rp.new_ranking_pos
WHERE pr.yearweek = '2020/01';

-- 依次处理其他yearweek

这种方式能降低单次更新的资源占用,避免因锁表或内存不足导致超时。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 20:32:56