提高从MySQL迁移的Mongodb查询的性能

时间:2014-04-03 01:21:59

标签: mongodb performance mongodb-query

我正在学习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并不需要这个,除非有更多的多对多关系。

1 个答案:

答案 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中显示的,那么(我的数据可能有问题)似乎确实是最需要的约束。