我为在线游戏提供简单的高分服务,并且它比预期更受欢迎。高分是一个Web服务,它使用带有简单表的MYSQL后端,如下所示。每个高分记录都存储在此表中的一行中。问题是,当行数> 140k时,我发现某些关键查询速度过慢,以至于很快就无法为请求提供服务。
主表如下所示:
+----------+---------------+------+-----+---------+----------------+ | 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)。现在,无论我使用什么偏移,所有查询都运行得非常快。解释说明没有进行额外的排序,看起来它很容易处理所有流量。
答案 0 :(得分:4)
看到没有接受者,我会试一试。我来自SQL Server背景,但同样的想法适用。
一些一般性意见:
100万行真的没那么多。我创建了一个包含1,000,000行样本数据的表,即使使用一个索引(游戏ASC,时间DESC和等级DESC),所有查询都会在不到1秒的时间内运行。
(我唯一不确定的部分是playerId。查询执行得非常好,似乎没有必要使用playerId。也许你可以在聚集索引的末尾添加它。)