扩展高分数据库

Jak*_*ski 7 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).现在,无论我使用什么偏移,所有查询都运行得非常快.解释说明没有进行额外的排序,看起来它很容易处理所有流量.

ano*_*non 4

既然没有人接受,我就试一试。我有 SQL Server 背景,但同样的想法也适用。

一些一般性观察:

  • ID 列几乎没有意义,并且不应该参与任何索引,除非您没有告诉我们其他表/查询。事实上,它甚至不需要出现在您的最后一个查询中。您可以执行 COUNT(*)。
  • 您的聚集索引应该针对最常见的查询。因此,游戏 ASC、时间 DESC 和排名 DESC 的聚集索引效果很好。对于这样的历史表,按时间 DESC 排序通常是一个好主意,因为您通常对最新的内容感兴趣。您也可以尝试一个单独的索引,其排名按另一个方向排序,尽管我不确定这会有多大好处。
  • 您确定需要 SELECT * 吗?如果您可以选择更少的列,您也许能够创建一个包含 SELECT 和 WHERE 所需的所有列的索引。

100 万行确实不算多。我创建了一个像您这样的表,其中包含 1,000,000 行示例数据,即使使用一个索引(游戏 ASC、时间 DESC 和排名 DESC),所有查询的运行时间也不到 1 秒。

(我唯一不确定的部分是playerId。查询执行得很好,所以playerId似乎不是必需的。也许您可以将它添加到聚集索引的末尾。)