mysql - 扩展高分数据库

标签 mysql

我有一个简单的在线游戏高分服务,它比预期更受欢迎。得分最高的是一个 Web 服务,它使用 MYSQL 后端,并带有一个简单的表,如下所示。每个高分记录都作为一行存储在此表中。问题是,对于超过 140k 行,我发现某些关键查询速度减慢太多,以至于服务请求很快就会变得太慢。

主表如下所示:

  • id 是每个分数记录的唯一键
  • game是提交分数的游戏的ID号(目前始终等于“1”,不过很快就会支持更多游戏)
  • name 是该玩家提交的内容的显示名称
  • playerId 是给定用户的唯一 ID
  • score 是一个数字分数表示形式,例如 42,035
  • time为提交时间
  • 排名是一个大整数,它对给定游戏的分数提交进行唯一排序。这是 人们通常会在一定分数上打平,因此在这种情况下,平局将由谁先提交而打破。因此该字段的值大致等于“score * 100000000 + (MAX_TIME - 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 BYrank DESC LIMIT 0, 75;” 并在 0.00 秒内运行。

但是,如果您请求接近列表末尾 “SELECT * FROM Scoretable WHERE game=1 AND time>? ORDER BYrank DESC LIMIT 10000, 75;” 运行时间为 0.06 秒。

“SELECT * FROM Scoretable WHERE game=1 AND time>? ORDER BYrank DESC LIMIT 100000, 75;” 并在 0.58 秒内运行。

由于每天都会提交数千个新分数,这似乎很快就会开始花费很长时间!

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

“SELECT * FROM Scoretable WHERE game=1 AND time>? AND playerId=? ORDER BYrank DESC LIMIT 1”

后跟

“从得分表中选择计数(id)作为计数,其中游戏= 1和时间>?AND排名>[从上面返回的排名]”

我的问题是:如何使其成为一个可扩展的系统?我可以看到行数很快就会增长到几百万。我希望选择一些智能索引会有所帮助,但改善幅度很小。

更新: 这是一条解释行:

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)。现在,无论我使用什么偏移量,所有查询都运行得非常快。解释显示没有进行任何额外的排序,并且看起来它可以轻松处理所有流量。

最佳答案

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

一些一般性观察:

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

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

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

关于mysql - 扩展高分数据库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4858693/

相关文章:

php - MySQL:如果匹配两列则忽略插入

mysql查询获取一年中没有数据的几周的行

php - 再次编辑一行时出现问题,它在 php codeigniter 中插入新行

mysql - mysql高级排序,根据action id排序

mysql - 选择计算 SQL 的正值/负值

PHP 为下一个查询获取上一个 MySQL 查询的 ID,如何?

mysql - SQL求和计算困惑

php - PDO/PHP : How to echo a specific metakey value

php - 通过ajax同时在多个数据库中执行更新查询

MySQL:如何获得一个字段与前一个字段相比的变化百分比