3

我有一个 mysql 表,它可能包含数百万行数据——在某些极端情况下高达 1 亿行。我开发的一个应用程序经常查询这些数据,并且我已经尽我所能来优化它 - 在大多数情况下它工作得非常快,因为我们只搜索数据的一个非常小的子集(与位置相关) .

表结构:

CREATE TABLE `prism_actions` (
  `id` int(11) unsigned NOT NULL auto_increment,
  `action_time` timestamp NOT NULL default CURRENT_TIMESTAMP,
  `action_type` varchar(25) NOT NULL,
  `player` varchar(16) NOT NULL,
  `world` varchar(255) NOT NULL,
  `x` int(11) NOT NULL,
  `y` int(11) NOT NULL,
  `z` int(11) NOT NULL,
  `block_id` mediumint(5) unsigned NOT NULL,
  `block_subid` mediumint(5) unsigned NOT NULL,
  `data` varchar(255) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `x` (`x`),
  KEY `action_type` (`action_type`),
  KEY `player` (`player`),
  KEY `block_id` (`block_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1;

对于我们在 WHERE 语句中最常使用的字段,我有几个基本索引,当在只有一个条件的查询中使用时 - 它非常快。

我正在运行这些测试的示例表有 2200 万条记录。

例子:

SELECT prism_actions.id FROM prism_actions WHERE prism_actions.action_type = 'block-break' LIMIT 1000;
1000 rows in set (0.00 sec)

SELECT prism_actions.id FROM prism_actions WHERE prism_actions.block_id = 2 LIMIT 1000;
1000 rows in set (0.01 sec)

我的问题是,对于我们在查询中使用的每个条件(大多数查询通常有几个条件),查询需要更长的时间。

SELECT prism_actions.id FROM prism_actions WHERE prism_actions.action_type = 'block-break' AND prism_actions.block_id = 2 LIMIT 1000;
1000 rows in set (0.79 sec)

完整查询可以接受 0.79 秒,但这只是使用部分条件。

一个真正的查询更像是:

SELECT prism_actions.id FROM prism_actions WHERE prism_actions.action_type = 'block-break' AND prism_actions.player = 'viveleroi' AND prism_actions.block_id = 2 LIMIT 1000;
1000 rows in set (2.22 sec)

在一个条件下我们运行0.01,两个我们运行,0.79三个我们在2.2几秒钟内运行,这太长了。

我将研究如何更好地设计我的索引,但我对当前的数据库架构和索引非常满意。

但是,当这样一起使用时,我可以尝试使条件更快吗?

更新

我花时间将表格转换为外键格式。player、action_type 和 world 列数据被移动到单独的表中,并且它们的 ID 存储在原始表中。花了几个小时来迁移数据。

但是,我正在重新运行我之前使用过的相同查询,虽然我看到一些查询速度有所提高,但我发现其他查询几乎没有变化。

上面 0.79 秒查询的转换版本运行速度大致相同:

SELECT prism_actions.id FROM prism_actions WHERE prism_actions.actiontype_id = 1 AND prism_actions.block_id = 2 LIMIT 1000;
1000 rows in set (0.73 sec)

block_id col 仍然具有来自原始表模式的索引。

以 player_id 为条件的查询运行非常缓慢,因此我在列中添加了一个索引,现在查询速度非常快。

但是,在以真实用户的几个查询示例并针对此表结构更新它们之后,我发现速度没有变化。

SELECT prism_actions.id FROM prism_actions WHERE (prism_actions.actiontype_id = 2 OR prism_actions.actiontype_id = 1) AND (prism_actions.player_id = 1127) AND prism_actions.action_time >= '2013-02-22 07:47:54' LIMIT 1000;

以前拍5.83 sec的,现在拍的5.29 sec

编辑 - 似乎是时间戳。从上面的查询中取出时间戳条件会在 0.01 秒内返回结果。为时间戳添加索引什么都不做 - 想法?

到目前为止,我真正看到的只是某些区域的速度略有提高,由于我们存储了重复的字符串,因此节省了少量文件空间——但到目前为止,还没有什么值得让拥有这么大数据库的数百名用户花费一天的时间时间转换数据。

对我可能索引东西等的其他方式有什么建议吗?

4

3 回答 3

0

MySQL v5.5:您可以创建PARTITION BY RANGE COLUMNS为:

CREATE TABLE `prism_actions` (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `action_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `action_type` varchar(25) NOT NULL,
  `player` varchar(16) NOT NULL,
  `world` varchar(255) NOT NULL,
  `x` int(11) NOT NULL,
  `y` int(11) NOT NULL,
  `z` int(11) NOT NULL,
  `block_id` mediumint(5) UNSIGNED NOT NULL,
  `block_subid` mediumint(5) UNSIGNED NOT NULL,
  `data` varchar(255) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `x` (`x`),
  KEY `action_type` (`action_type`),
  KEY `player` (`player`),
  KEY `block_id` (`block_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1
PARTITION BY RANGE COLUMNS(action_type, player, block_id)(
PARTITION p0 VALUES LESS THAN ('dddddddd','dddddddd',1000000),
PARTITION p1 VALUES LESS THAN ('gggggggg','gggggggg',2000000),
PARTITION p2 VALUES LESS THAN ('jjjjjjjj','jjjjjjjj',3000000),
PARTITION p3 VALUES LESS THAN ('mmmmmmmm','mmmmmmmm',4000000),
PARTITION p4 VALUES LESS THAN ('pppppppp','pppppppp',5000000),
PARTITION p5 VALUES LESS THAN ('ssssssss','ssssssss',6000000),
PARTITION p6 VALUES LESS THAN ('uuuuuuuu','uuuuuuuu',7000000),
PARTITION p7 VALUES LESS THAN (MAXVALUE,MAXVALUE,MAXVALUE)
);

最坏的情况是对于任何给定的集合(action_type, player, block_id),它们只能属于一个分区。因此,与原始查询相比,它更好。

加分速度,如果您可以分析列值的频率分布并相应地进行分区。上面的分区是粗略的间隔。

于 2013-03-01T17:56:20.563 回答
0

移动所有文本列(动作类型、玩家、世界)所有这些都是文本列到一个新表。

这将减少数据库大小并保留此表中的参考编号。

这将显着提高性能。

于 2013-03-01T17:16:24.073 回答
0

我会单独留下你的桌子,以防止在你的结果集之后需要再次加入。您只需要一个索引,其中包含 where 的所有键列,而不是单独的每一个。我会尝试根据您首先遇到的最小结果集来优化它,例如 2200 万条记录,我敢打赌基于 Block_ID = 2 的记录不少,而基于玩家的记录则更少。

所以,我会有一个索引

create index multipart on prism_actions ( Player, Block_ID, Action_Type );

作为单一索引,而不是您目前拥有的单个字段。这允许引擎直接跳转到给定的玩家,现在从 2200 万,下降到 2000 个条目,block ID = 2 现在下降到 200,下降到 action_type = block break.... 20 条记录...显然只是记录计数的任意样本,但复合索引应该是您所需要的。

于 2013-03-01T20:21:48.387 回答