繁体   English   中英

使用大型表上的多个where条件提高mysql查询的性能

[英]Improving performance of mysql query with multiple where conditions on large tables

我有一个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)

完整的查询可以接受.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秒运行,这太长了。

我将研究如何更好地设计索引,但是我对当前的数据库模式和索引基本满意。

但是,当这样一起使用时,我该如何尝试使条件更快?

更新

我花了一些时间将表转换为外键格式。 玩家,action_type和world列数据已移动到单独的表中,并且这些ID的ID存储在原始表中。 花了几个小时来迁移数据。

但是,我正在重新运行先前使用的相同查询,尽管我看到某些查询的速度有所提高,但在其他查询中却看不到什么变化。

上面的.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秒内返回结果。 为时间戳添加索引没有任何作用-想法?

到目前为止,我真正看到的是某些区域的速度略有提高,由于我们存储重复的字符串,因此节省了很小的文件空间-但到目前为止,没有什么可以保证让数百名拥有如此大数据库的用户花费一天的时间了时间转换数据。

对我可能将内容编入索引的其他方式有什么建议吗?

将所有文本列(动作类型,玩家,世界)移动到新表中。

这将减小数据库大小,并将引用编号保留在此表中。

这将大大提高性能。

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) ,最坏的情况是,它们只能属于一个分区。 因此,与原始查询相比,它更好。

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

我将不理会您的表,以免在结果集之后需要再次加入。 您只需要一个索引以及where的所有关键列,而不是每个索引都单独存在。 我会尝试根据您首先达到的最小结果集(例如2200万条记录)来优化此设置,我敢打赌,基于Block_ID = 2的结果很多,而基于玩家的数据则更少。

所以,我将有一个索引

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM