提高MySQL索引效率-多个索引中的列?

提高MySQL索引效率-多个索引中的列?,mysql,sql,Mysql,Sql,试图在海量数据上更有效地使用索引 我有一个开源应用程序,它将数百万条记录记录到MySQL数据库中。我在web开发中使用mysql数据库已有多年,我对选择有效字段类型、索引为何/如何有用等基本知识非常了解,但我们的应用程序日志中的数据量以及很难准确预测将查询哪些列这一事实让我有点不知所措 应用程序按玩家记录事件。我们有一个非常先进的清除系统,但有些服务器非常繁忙,仅八周后就有5000万条记录 在这种情况下,如果使用现有索引,查询可能仍需要30-90秒 主表架构(减去现有索引): 其中条件通常包括:

试图在海量数据上更有效地使用索引

我有一个开源应用程序,它将数百万条记录记录到MySQL数据库中。我在web开发中使用mysql数据库已有多年,我对选择有效字段类型、索引为何/如何有用等基本知识非常了解,但我们的应用程序日志中的数据量以及很难准确预测将查询哪些列这一事实让我有点不知所措

应用程序按玩家记录事件。我们有一个非常先进的清除系统,但有些服务器非常繁忙,仅八周后就有5000万条记录

在这种情况下,如果使用现有索引,查询可能仍需要30-90秒

主表架构(减去现有索引):

其中
条件通常包括:

  • world\u id/x/y/z
    坐标(查询用户周围的所有默认半径,因此几乎总是使用坐标)
  • epoch
    (所有查询默认为最后三天,用户需要覆盖更长时间)
  • action\u id
    和/或
    player\u id
    (有一半的时间,用户都在寻找谁做了特定的动作,或者特定的玩家导致了什么动作。)
  • 剩余的查询可以是任意组合、块id值与播放器或动作组合等
分组依据
-默认情况下,应用程序按特定字段分组,这样用户就不会看到同一播放器/动作/区块的100个重复事件,他们只会看到一条带有计数的记录

action\u id
player\u id
block\u id
日期(从UNIXTIME(历元))

ORDER BY
始终是
prism_data.epoch DESC,x ASC,z ASC,y ASC,id DESC
epoch
可以让用户首先看到最近的事件。其余的都是为了让“回滚”引擎按正确的顺序运行

下面是一个不带订单/组的查询示例

SELECT *
FROM prism_data 
INNER JOIN prism_players p ON p.player_id = prism_data.player_id
INNER JOIN prism_actions a ON a.action_id = prism_data.action_id
INNER JOIN prism_worlds w ON w.world_id = prism_data.world_id 
LEFT JOIN prism_data_extra ex ON ex.data_id = prism_data.id 
WHERE w.world = 'DeuxTiersMondes'
AND (prism_data.x BETWEEN 668 AND 868)
AND (prism_data.y BETWEEN -33 AND 167)
AND (prism_data.z BETWEEN 358 AND 558);
LIMIT 1000;
使用索引:
index
location
world\u id
x
z
y
查找1000行仍然需要15秒(或查找所有64735行需要50秒)

该查询的解释如下:

+----+-------------+------------+--------+---------------+----------+---------+--------------------------------+------+--------------------------+
| id | select_type | table      | type   | possible_keys | key      | key_len | ref                            | rows | Extra                    |
+----+-------------+------------+--------+---------------+----------+---------+--------------------------------+------+--------------------------+
|  1 | SIMPLE      | w          | ref    | PRIMARY,world | world    | 767     | const                          |    1 | Using where; Using index |
|  1 | SIMPLE      | prism_data | ref    | location      | location | 4       | minecraft.w.world_id           | 6155 | Using index condition    |
|  1 | SIMPLE      | a          | eq_ref | PRIMARY       | PRIMARY  | 4       | minecraft.prism_data.action_id |    1 | NULL                     |
|  1 | SIMPLE      | p          | eq_ref | PRIMARY       | PRIMARY  | 4       | minecraft.prism_data.player_id |    1 | NULL                     |
|  1 | SIMPLE      | ex         | ref    | data_id       | data_id  | 4       | minecraft.prism_data.id        |    1 | NULL                     |
+----+-------------+------------+--------+---------------+----------+---------+--------------------------------+------+--------------------------+
在我看来,寻找这种特定的价值观应该快得多。我们甚至没有在这个查询中进行排序/分组

我的问题:

我认为为我上面列出的每个常见情况设计一个索引是最有意义的。i、 e.一个索引组合了
世界id/x/y/z
,一个索引组合了
动作id/player\u id
,还有一个索引组合了
时代
。对于某些查询,这很有效,但对于其他查询,则不然。对于使用
world\u id、player\u id和epoch
的查询,它只选择
world\u id/x/y/z
索引

  • 我是否可以/应该在多个索引中包含一列?可能一个索引用于完整位置,另一个索引用于
    世界号/玩家号/纪元
    ?我真的不知道mysql使用什么逻辑来选择最适合的索引,但我假设如果一个索引使用mysql需要的更多列,它会选择那个。如果这对我的查询有帮助的话,在write上稍微提高一下性能是值得的
  • 我应该创建一个包含我分组依据/排序依据的所有字段的索引吗?我的解释经常显示
    使用filesort
    ,我知道这是影响性能的一个主要问题
  • 即使在组合索引中,在大多数字段上使用单个索引有什么好处吗
抱歉读了这么久


我正在用不同的索引设置对5个最常见的查询进行大量分析,但我感觉可能缺少一些基础知识。在我继续之前,我宁愿让一些真正的专家教我一些我缺少的东西。

MySQL可以使用复合索引,如果提供了索引的前n列。因此,如果在a、b、c、d列上有一个复合索引,那么MySQL可以在提供a、b列的情况下使用该索引。OTOH,如果您在查询中只提供了B、C、D列,MySQL将无法使用索引。根据查询中可能使用的列的组合,在多个索引中包含列可能有意义,也可能没有意义。不要忘记,为每个列/索引插入/更新/删除一行需要额外的成本

我认为MySQL没有对索引进行排序,所以我怀疑索引对排序性能没有帮助,但我不确定


根据您使用数据的方式,研究表的分区可能是有意义的,可能是按历元划分的。

这只是一个简短的说明,因为这种情况会反复出现:由于您(很可能)不需要该表中的数据,所以不需要在
prism_worlds
上进行连接。你基本上是在问数据库“给我每个世界的名字,这个名字等于‘某物’”。改用标量子查询

prism_worlds.world
上创建一个唯一索引,并像

SELECT *
FROM prism_data 
WHERE prism_data.world_id = (SELECT w.world_id FROM prism_worlds AS w WHERE w.world = 'DeuxTiersMondes')
LIMIT 1000;
优化器将计算出
prism\u data.world\u id
被约束为单个常量值。MySQL将提前运行一个查询来计算这个值,并在整个查询过程中使用它。有关执行的
const
-子查询,请参见
EXPLAIN


关于
prism_data.x
.y
.z
:您可能需要为此创建一个几何列和一个空间索引。如果需要坚持使用分离值,则可能需要将整个世界几何体分离为固定大小的体素(由单个int表示),并使用简单几何体确定哪个位置属于哪个体素


我个人的解决方案是,在这个表上添加无数的查询时,不要考虑太多。索引会让它变慢变大。使用cron作业来填充
SELECT *
FROM prism_data 
WHERE prism_data.world_id = (SELECT w.world_id FROM prism_worlds AS w WHERE w.world = 'DeuxTiersMondes')
LIMIT 1000;
SELECT prism_data.id,
       prism_data.action_id,
       prism_data.world_id
  FROM prism_data 
 INNER JOIN prism_worlds w ON w.world_id = prism_data.world_id 
 WHERE w.world = 'DeuxTiersMondes'
   AND (prism_data.x BETWEEN 668 AND 868)
   AND (prism_data.y BETWEEN -33 AND 167)
   AND (prism_data.z BETWEEN 358 AND 558)
 ORDER BY prism_data.id DESC
 LIMIT 1000;
 (world_id, x, y, z, id, action_id, world_id)