我有一个mysql表,可能有数百万行数据 - 在某些极端情况下可达100mil。有一个我开发的应用程序,经常查询这些数据,我已经尽我所能来优化它 - 大多数情况下它的工作非常快,因为我们只搜索一小部分数据(与位置相关)
表格结构:
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和世界列数据被移动到单独的表中,并且这些表的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秒内返回结果。添加时间戳索引什么也没做 - 想法?
到目前为止,我真正看到的是,某些区域的速度略有提升,因为我们存储了重复的字符串,所以节省了很少的文件空间 - 但到目前为止还没有任何内容可以保证要求数百个用户拥有这么大的数据库转换数据的一天时间。
我可能会为其他一些方式建议吗?
答案 0 :(得分:0)
将所有文本列(操作类型,播放器,世界)移动到新表中。
这将减少数据库大小并保留此表中的引用号。
这将大大提高性能。
答案 1 :(得分: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)
集合,最坏的情况是,它们只能属于一个分区。因此,与原始查询相比,它更好。
奖励速度,如果您可以分析列值的频率分布并相应地制作分区。上面的分区是粗略的间隔。
答案 2 :(得分:0)
我会单独留下你的桌子以防止在你的结果集之后再次加入。您只需要一个索引,其中包含where的所有关键列,而不是每个关键列。我会尝试根据您首先获得的最小结果集来优化这一点,例如2200万条记录,我根据Block_ID = 2打赌相当多,而且基于玩家更少。
所以,我会在
上有一个索引create index multipart on prism_actions ( Player, Block_ID, Action_Type );
作为单个索引,而不是您目前拥有的单个字段。这允许引擎直接跳转到给定的玩家,现在从2200万,直到2000个条目,到现在阻止ID = 2到200,再到action_type =块中断...... 20个记录......显然只是记录计数的任意样本,但复合索引应该是你所需要的。