【问题标题】:Temporary table bogging down database -- how to optimize?临时表卡住数据库——如何优化?
【发布时间】:2012-06-05 22:34:38
【问题描述】:

如果这个问题太具体,我提前道歉,但我认为这是一个相当典型的场景:joingroup bys 陷入数据库和解决它的最佳方法。我的具体问题是我需要根据以下内容创建记分牌:

  • plays (userid,gameid,score) 40M 行
  • 游戏 (gameid) 100K 行
  • app_games (appid,gameid) 即,游戏被分组到应用程序中,应用程序的总分是所有相关游戏的总和

用户可以玩多次,并记录他们在每场比赛中的最佳成绩。制定查询很容易,我已经做了几个变体,但是在负载下它们有一种令人讨厌的趋势,即在“复制临时表”中锁定 30-60 秒。

我能做什么?是否有我应该调整的服务器变量,或者有没有办法重新制定查询以使其更快?我使用的查询的派生版本如下(减去用户表连接来获取名称):

    select userID,sum(score) as cumscore from  
        (select userID, gameID,max(p.score) as score 
        from play p join app_game ag using (gameID)  
        where ag.appID = 1 and p.score>0
        group by userID,gameID ) app_stats 
    group by userid order by cumscore desc limit 0,20;

或作为临时表:

    drop table if exists app_stats;
    create temporary table app_stats 
        select userID,gameID,max(p.score) as score 
        from play p join app_game ag using (gameID)  
        where ag.appID = 1 and p.score>0
        group by userid,gameID;
    select userID,sum(score) as cumscore from app_stats group by userid 
        order by cumscore desc limit 0,20;

我的索引如下:

show indexes from play;
+-------+------------+----------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name             | Seq_in_index | Column_name      | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------+------------+----------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+
| play  |          0 | PRIMARY              |            1 | playID           | A         |    38353712 |     NULL | NULL   |      | BTREE      |         |
| play  |          0 | uk_play_uniqueID     |            1 | uniqueID         | A         |    38353712 |     NULL | NULL   | YES  | BTREE      |         |
| play  |          1 | play_score_added     |            1 | dateTimeFinished | A         |    19176856 |     NULL | NULL   | YES  | BTREE      |         |
| play  |          1 | play_score_added     |            2 | score            | A         |    19176856 |     NULL | NULL   |      | BTREE      |         |
| play  |          1 | fk_playData_game     |            1 | gameID           | A         |       76098 |     NULL | NULL   |      | BTREE      |         |
| play  |          1 | user_hiscore         |            1 | userID           | A         |      650062 |     NULL | NULL   | YES  | BTREE      |         |
| play  |          1 | user_hiscore         |            2 | score            | A         |     2397107 |     NULL | NULL   |      | BTREE      |         |
+-------+------------+----------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+

【问题讨论】:

    标签: mysql group-by query-optimization temp-tables


    【解决方案1】:

    我怀疑创建临时表时的两个查询基本上都需要遍历表中的所有数据(同样在你的 do-everything-at-once 查询中)。如果您有大量数据,这将需要一些时间。

    我会维护一个单独的表格,其中包含每个玩家的 ID 和总分。每当您更新播放表时,也要更新汇总表。如果它们不同步,只需停止汇总表并从播放表重新创建数据。 (或者如果你已经在你的基础设施中使用了 redis,你可以在那里维护摘要——它具有使这个特定的东西变得非常快的功能。

    【讨论】:

    • 我同意存储玩家总数。
    • 什么样的功能?我们使用 memcached,因此我可以将整个内容缓存为一个数组,然后只需通过获取/设置相同的元素来更改需要更改的元素。出于某种原因,我还没有看到过这样的 memcached 推荐(尽管我已经考虑过了)——有点像 nosql 类型 db 的一半。
    • Memcached 对此不太好(如果您需要实时更新结果),因为您无法轻松获得结果列表。 redis 有“排序集”,非常适合您的用例,如果我没记错的话,这实际上是他们文档中的示例:redis.io/topics/data-types#sorted-sets
    • 但是,如果您不想向系统 (redis) 添加新组件,那么只需有一个额外的表格,您可以按照我在答案中的建议汇总总数。
    • @mmdanziger 记得接受其中一个答案;我认为否则最终堆栈溢出不会让你发布新的(或者人们不回应,不确定它是如何工作的)。 :-)
    【解决方案2】:

    与其制作临时表,不如尝试制作一个视图。您可以像查询普通表一样查询它,但它也会在视图中的任何数据发生更改时更新。这比每次删除表并重新创建它要快得多。

    【讨论】:

    • 我以前从未使用过视图,现在我会查找它们。
    • 为什么视图会让事情变得更快? MySQL 没有物化视图。
    猜你喜欢
    • 2011-08-12
    • 2018-04-07
    • 1970-01-01
    • 2020-10-19
    • 2016-11-11
    • 2020-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多