【问题标题】:mysql query optimization for game游戏的mysql查询优化
【发布时间】:2013-02-09 10:19:33
【问题描述】:

我有这张桌子可以玩 2-4 人的纸牌游戏:

CREATE TABLE IF NOT EXISTS `cardgame` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `players` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `p1` mediumint(6) unsigned NOT NULL DEFAULT '0',
  `p2` mediumint(6) unsigned NOT NULL DEFAULT '0',
  `p3` mediumint(6) unsigned NOT NULL DEFAULT '0',
  `p4` mediumint(6) unsigned NOT NULL DEFAULT '0',
  `p1_state` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `p2_state` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `p3_state` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `p4_state` tinyint(1) unsigned NOT NULL DEFAULT '0',
  /* other rows */,
  `game_state` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=79218 DEFAULT CHARSET=utf8;

我必须检查所有我所在的游戏。 (示例中我的 user_id 是 1981)

mysql> EXPLAIN SELECT id FROM cardgame 
WHERE ((p1=1981 AND p1_state=1) OR (p2=1981 AND p2_state=1) 
OR (p3=1981 AND p3_state=1) OR (p4=1981 AND p4_state=1)) 
AND game_state < 7;
+----+-------------+----------+------+---------------+------+---------+------+-------+-------------+
| id | select_type | table    | type | possible_keys | key  | key_len | ref  | rows  | Extra       |
+----+-------------+----------+------+---------------+------+---------+------+-------+-------------+
|  1 | SIMPLE      | cardgame | ALL  | NULL          | NULL | NULL    | NULL | 79208 | Using where |
+----+-------------+----------+------+---------------+------+---------+------+-------+-------------+

我可以在哪些行上创建索引? p1, p1_state, p2.. p3.. p4, p4_state 行需要 index 加上 game_state,但不是太多了吗?

或者我必须以某种方式重新设计架构?

【问题讨论】:

    标签: mysql query-optimization database-schema


    【解决方案1】:

    或者我必须以某种方式重新设计架构?

    是的,normalise 你的架构:

    CREATE TABLE game_players (
      game_id  MEDIUMINT NOT NULL,
      position TINYINT   NOT NULL,
      player   MEDIUMINT NOT NULL,
      state    TINYINT UNSIGNED NOT NULL DEFAULT 0,
      PRIMARY KEY (game_id, position),
      UNIQUE (game_id, player), -- if a player can only be in each game once
      INDEX (player, state)
    )
    SELECT id, 1, p1, p1_state FROM cardgame
      UNION ALL
    SELECT id, 2, p2, p2_state FROM cardgame
      UNION ALL
    SELECT id, 3, p3, p3_state FROM cardgame
      UNION ALL
    SELECT id, 4, p4, p4_state FROM cardgame;
    
    ALTER TABLE cardgame 
      DROP p1, DROP p1_state,
      DROP p2, DROP p2_state,
      DROP p3, DROP p3_state,
      DROP p4, DROP p4_state;
    

    那么你的查询变成:

    SELECT g.id
    FROM   game_players p JOIN cardgame g ON p.game_id = g.id
    WHERE  p.player = 1981 AND p.state = 1
       AND g.game_state < 7;
    

    【讨论】:

    • 谢谢! JOIN 会比旧查询更快吗?
    • @heal:是的,它会的。您甚至可以通过将game_players 上的索引设置为覆盖索引(player, state, game_id) 并将(id, game_state) 上的索引添加到cardgame 表中来进一步提高性能:但我怀疑您会发现这样的优化是不必要的(而且只是添加多余的开销):记住,过早的优化是万恶之源
    • @eggyal:我认为你需要在表格中添加一个position 列,它应该接受从1到4的数字。玩家的位置,在许多纸牌游戏(扑克,桥牌)中,定义他们正在播放的顺序。还有一个UNIQUE 约束(game_id, position)
    • @ypercube:和以往一样,当我摔倒时你会抓住我! ;)
    • @ypercube,好主意,谢谢! (我现在不能对帖子和 cmets 投票)
    【解决方案2】:

    关于数据正确性的说明:

    @eggyal 在另一个答案中的重新设计删除了您列中的大部分DEFAULTs。这是一件好事。

    NOT NULL 的一大好处是防止您的程序创建没有正确指定所有列的行。拥有DEFAULT 会使它变得毫无用处。让我们看一个类似于您的纸牌游戏桌的桌子:

    CREATE TABLE IF NOT EXISTS `cardgame` (
        `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
        `player_names` varchar(20) NOT NULL,
        `players` tinyint(1) unsigned NOT NULL DEFAULT '0'
    );
    

    所以现在你创建一个这样的行:

    INSERT INTO cardgame (player_names) VALUES ('Bob and Doug');
    

    您没有指定id,因为它是AUTO_INCREMENT,并且您正确指定了player_names,但是糟糕,您忘记指定players。现在,players 列中的值为 0,这是错误数据。在不指定玩家数量的情况下,您应该能够在表格中添加一行,对吧? DEFAULT '0' 删除了该限制,更糟糕的是,将无效数量的玩家放入列中。有 0 个玩家的游戏永远都不正确,对吧?但是您的DEFAULT 在其中输入了无效数量的玩家。即使您将DEFAULT 更改为像2 这样的有效数字,您也不知道它是正确的。您必须始终指定玩家人数,对吗? DB 不知道。

    因此,永远不要指定DEFAULT,除非它是一个不需要每次都指定的特定列。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-04
      • 2011-01-22
      • 2011-07-07
      • 2018-12-21
      • 2010-12-15
      相关资源
      最近更新 更多