我正在学习MongoDB,并且正在考虑将我的数据密集型(MySQL)应用程序移植到它上面。我努力提高MySQL方面的性能。
每个数据库中有大约90k的记录,我在MySQL和MongoDB中运行相同的查询。我已经阅读了MongoDB索引,因为它们的工作方式非常相似,所以我在MongoDB中添加了与MySQL相同的“主”索引。
然而,Mongo几乎慢了三倍。我听说很多关于mongo的速度通常要快得多,即使不是这样,也不应该慢三倍。
我有什么遗失的吗?
MongoDB中的此查询以 0.085 秒:
返回1000条记录db.prismData.find(
{
"x":{
"$gt":306,
"$lt":366
},
"y":{
"$gt":35,
"$lt":95
},
"z":{
"$gt":122,
"$lt":182
},
"epoch":{
"$gte":1396226195
},
"world":"world"
})
.sort( { "epoch" : -1 , "x" : 1 , "z" : 1 , "y" : 1 , "id" : -1} )
.limit(1000);
上述查询的解释:
{
"cursor" : "BtreeCursor world_1_x_1_z_1_y_1_epoch_1_action_1",
"isMultiKey" : false,
"n" : 1000,
"nscannedObjects" : 7773,
"nscanned" : 8041,
"nscannedObjectsAllPlans" : 7881,
"nscannedAllPlans" : 8149,
"scanAndOrder" : true,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 84,
"indexBounds" : {
"world" : [
[
"world",
"world"
]
],
"x" : [
[
306,
366
]
],
"z" : [
[
122,
182
]
],
"y" : [
[
35,
95
]
],
"action" : [
[
{
"$minElement" : 1
},
{
"$maxElement" : 1
}
]
]
},
"server" : "removed"
}
MySQL运行整个查询,返回 0.03秒中的1000条记录。
SELECT id,
epoch,
action_id,
player,
world_id,
x,
y,
z,
block_id,
block_subid,
old_block_id,
old_block_subid,
DATA
FROM prism_data
INNER JOIN prism_players p ON p.player_id = prism_data.player_id
LEFT JOIN prism_data_extra ex ON ex.data_id = prism_data.id
WHERE world_id =
(SELECT w.world_id
FROM prism_worlds w
WHERE w.world = 'world')
AND (prism_data.x BETWEEN 427 AND 487)
AND (prism_data.y BETWEEN 36 AND 96)
AND (prism_data.z BETWEEN -14 AND 46)
AND prism_data.epoch >= 1396225265
ORDER BY prism_data.epoch DESC,
x ASC,
z ASC,
y ASC,
id DESC LIMIT 1000;
这个sql的解释:
+----+-------------+------------+--------+----------------+----------+---------+----------------------------------+-------+----------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+----------------+----------+---------+----------------------------------+-------+----------------------------------------------------+
| 1 | PRIMARY | prism_data | ref | epoch,location | location | 4 | const | 43925 | Using index condition; Using where; Using filesort |
| 1 | PRIMARY | p | eq_ref | PRIMARY | PRIMARY | 4 | prism_daily.prism_data.player_id | 1 | NULL |
| 1 | PRIMARY | ex | ref | data_id | data_id | 4 | prism_daily.prism_data.id | 1 | NULL |
| 2 | SUBQUERY | w | const | world | world | 767 | const | 1 | Using index |
+----+-------------+------------+--------+----------------+----------+---------+----------------------------------+-------+----------------------------------------------------+
模式的唯一区别在于,一些重复数据(如人名和事件名称)存储在文档中,而不是使用mysql中的外键进行标准化。基于我所读到的,mongo并不需要这个,除非有更多的多对多关系。
答案 0 :(得分:3)
很难提供任何非常主观的东西,因为我们没有你的数据和大的匹配大小,即使作为样本也很难分享。
在您的排序和选择中会指出您想到的两件事。似乎该集合中最大的减速器是“世界”和“时代”领域。因此,它们应该首先在索引中,如:
db.prismData.ensureIndex({
"epoch" -1,
"world": 1,
"x": 1,
"z": 1,
"y": 1,
"id": -1
})
然后,您的查询应该或多或少地反映该顺序以及排序,但根据索引顺序可能甚至不需要:
db.prismData.find(
{
"epoch":{
"$gte":1396226195
},
"world":"world",
"x":{
"$gt":306,
"$lt":366
},
"z":{
"$gt":122,
"$lt":182
},
"y":{
"$gt":35,
"$lt":95
}
})
.sort( { "epoch" : -1 , "world": 1, "x" : 1 , "z" : 1 , "y" : 1 , "id" : -1 } )
.limit(1000);
所以你真的试图将其限制为使用索引中的“最小”数据集,因此在特定时间戳“first”之后查找事物是有意义的,然后通过下一个逻辑键进行约束,即“世界” ,然后扫描范围的剩余部分。
我希望至少那时“epoch”字段实际上是在indexBounds中显示的,那么(我的数据可能有问题)似乎确实是最需要的约束。