扩展高分数据库

时间:2011-02-01 03:51:51

标签: mysql

我为在线游戏提供简单的高分服务,并且它比预期更受欢迎。高分是一个Web服务,它使用带有简单表的MYSQL后端,如下所示。每个高分记录都存储在此表中的一行中。问题是,当行数> 140k时,我发现某些关键查询速度过慢,以至于很快就无法为请求提供服务。

主表如下所示:

  • id是每个得分记录的唯一键
  • 游戏是提交分数的游戏的ID号(目前,总是等于“1”,很快就会支持更多游戏)
  • name是该播放器提交的显示名称
  • playerId是给定用户的唯一ID
  • 得分是数字得分表示ex 42,035
  • 时间是提交时间
  • rank是一个大整数,它对给定游戏的得分提交进行唯一排序。它是 人们常常在某个分数上打成平手,因此在这种情况下,首先提交的人会打破平局。因此该字段的值大致等于“得分* 100000000 +(MAX_TIME - 时间)”
+----------+---------------+------+-----+---------+----------------+
| Field    | Type          | Null | Key | Default | Extra          |
+----------+---------------+------+-----+---------+----------------+
| id       | int(11)       | NO   | PRI | NULL    | auto_increment |
| game     | int(11)       | YES  | MUL | NULL    |                |
| name     | varchar(100)  | YES  |     | NULL    |                |
| playerId | varchar(50)   | YES  |     | NULL    |                |
| score    | int(11)       | YES  |     | NULL    |                |
| time     | datetime      | YES  |     | NULL    |                |
| rank     | decimal(50,0) | YES  | MUL | NULL    |                |
+----------+---------------+------+-----+---------+----------------+

索引如下所示:

+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table     | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| pozscores |          0 | PRIMARY  |            1 | id          | A         |      138296 |     NULL | NULL   |      | BTREE      |         |
| pozscores |          0 | game     |            1 | game        | A         |        NULL |     NULL | NULL   | YES  | BTREE      |         |
| pozscores |          0 | game     |            2 | rank        | A         |        NULL |     NULL | NULL   | YES  | BTREE      |         |
| pozscores |          1 | rank     |            1 | rank        | A         |      138296 |     NULL | NULL   | YES  | BTREE      |         |
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

当用户请求高分时,他们通常从“按等级降序列表排序”中的任意点请求大约75个高分。这些请求通常是“所有时间”或仅仅是过去7天内的分数。

典型的查询如下所示: "SELECT * FROM scoretable WHERE game=1 AND time>? ORDER BY rank DESC LIMIT 0, 75;"并以0.00秒运行

但是,如果您在列表末尾请求 "SELECT * FROM scoretable WHERE game=1 AND time>? ORDER BY rank DESC LIMIT 10000, 75;"并在0.06秒内运行

"SELECT * FROM scoretable WHERE game=1 AND time>? ORDER BY rank DESC LIMIT 100000, 75;"并在0.58秒内运行。

似乎这会很快开始,因为每天都会提交几千个新分数!

此外,还有两种其他类型的查询,用于在排名顺序列表中按ID查找特定玩家。 它们看起来像这样:

"SELECT * FROM scoretable WHERE game=1 AND time>? AND playerId=? ORDER BY rank DESC LIMIT 1"

后跟

"SELECT count(id) as count FROM scoretable WHERE game=1 AND time>? AND rank>[rank returned from above]"

我的问题是:如何才能使这个可扩展的系统?我很快就能看到行数增长到数百万。我希望选择一些智能指数会有所帮助,但这种改善只是微不足道。

更新:  这是一个解释线:

mysql> explain SELECT * FROM scoretable WHERE game=1 AND time>0 ORDER BY rank DESC LIMIT 100000, 75;
+----+-------------+-----------+-------+---------------+------+---------+------+--------+-------------+
| id | select_type | table     | type  | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+-----------+-------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | scoretable| range | game          | game | 5       | NULL | 138478 | Using where |
+----+-------------+-----------+-------+---------------+------+---------+------+--------+-------------+

找到解决方案!

由于这个线程的一些指针,我已经解决了这个问题。执行聚簇索引正是我所需要的,所以我将表转换为在mysql中使用InnoDB,它支持聚簇索引。接下来,我删除了id字段,并将主键设置为(游戏ASC,排名DESC)。现在,无论我使用什么偏移,所有查询都运行得非常快。解释说明没有进行额外的排序,看起来它很容易处理所有流量。

1 个答案:

答案 0 :(得分:4)

看到没有接受者,我会试一试。我来自SQL Server背景,但同样的想法适用。

一些一般性意见:

  • ID列几乎没有意义,除非您没有告诉我们其他表/查询,否则不应参与任何索引。实际上,它甚至不需要在您的上一个查询中。你可以做COUNT(*)。
  • 您的聚集索引应针对最常见的查询。因此,游戏ASC,时间DESC和等级DESC上的聚集索引运行良好。按时间排序对于像这样的历史表,DESC通常是一个好主意,你通常会对最新的东西感兴趣。你也可以尝试一个单独的索引,排名按另一个方向排序,但我不确定这将带来多大好处。
  • 您确定需要SELECT *吗?如果可以选择较少的列,则可以创建一个索引,其中包含SELECT和WHERE所需的所有列。

100万行真的没那么多。我创建了一个包含1,000,000行样本数据的表,即使使用一个索引(游戏ASC,时间DESC和等级DESC),所有查询都会在不到1秒的时间内运行。

(我唯一不确定的部分是playerId。查询执行得非常好,似乎没有必要使用playerId。也许你可以在聚集索引的末尾添加它。)