【问题标题】:MongoDb performance slow even using index即使使用索引,MongoDb 性能也很慢
【发布时间】:2013-04-20 09:21:59
【问题描述】:

我们正在尝试使用 mongo 为我们的用户构建一个通知应用程序。我们在 10GB RAM、150GB SAS HDD 15K RPM、4 Core 2.9GHZ xeon intel XEN VM 上创建了 1 个 mongodb。

数据库架构:-

{
  "_id" : ObjectId("5178c458e4b0e2f3cee77d47"),
  "userId" : NumberLong(1574631),
  "type" : 2,
  "text" : "a user connected to B",
  "status" : 0,
  "createdDate" : ISODate("2013-04-25T05:51:19.995Z"),
  "modifiedDate" : ISODate("2013-04-25T05:51:19.995Z"),
  "metadata" : "{\"INVITEE_NAME\":\"2344\",\"INVITEE\":1232143,\"INVITE_SENDER\":1574476,\"INVITE_SENDER_NAME\":\"123213\"}",
  "opType" : 1,
  "actorId" : NumberLong(1574630),
  "actorName" : "2344"
}

DB stats :-
db.stats()
{
    "db" : "UserNotificationDev2",
    "collections" : 3,
    "objects" : 78597973,
    "avgObjSize" : 489.00035699393925,
    "dataSize" : 38434436856,
    "storageSize" : 41501835008,
    "numExtents" : 42,
    "indexes" : 2,
    "indexSize" : 4272393328,
    "fileSize" : 49301946368,
    "nsSizeMB" : 16,
    "dataFileVersion" : {
        "major" : 4,
        "minor" : 5
    },
    "ok" : 1
}

索引 :- 用户 ID 和 _id

我们正在尝试为一位用户选择最新的 21 条通知。

db.userNotification.find({ "userId" : 53 }).limit(21).sort({ "_id" : -1 });

但是这个查询花费了太多时间。 Fri Apr 26 05:39:55.563 [conn156] query UserNotificationDev2.userNotification query: { query: { userId: 53 }, orderby: { _id: -1 } } cursorid:225321382318166794 ntoreturn:21 ntoskip:0 nscanned:266025 keyUpdates:0 numYields:2 个锁(微秒) r:4224498 nreturned:21 reslen:10295 2581ms

即使计数也需要很多时间。

Fri Apr 26 05:47:46.005 [conn159] command UserNotificationDev2.$cmd command: { count: "userNotification", query: { userId: 53 } } ntoreturn:1 keyUpdates:0 numYields: 11 locks(micros) r:9753890 reslen:48 5022ms

我们在查询中做错了吗?

请帮忙!!!

还建议我们的架构是否不正确地存储用户通知。我们已经尝试过嵌入通知,例如用户,然后在该文档下为该用户提供通知,但文档限制限制我们仅存储约 50k 通知。所以我们改成这个。

【问题讨论】:

  • 你能在你的发现上运行explain(),尤其是db.userNotification.find({ "userId" : 53 })
  • 你也可以运行 getIndexes() 吗?

标签: mongodb notifications indexing performance


【解决方案1】:

您正在通过 userId 进行查询,但没有在任何地方对其进行索引。我的建议是在{ "userId" : 1, "_id" : -1 } 上创建一个索引。这将创建一个以 userId 开头的索引树,然后是 _id,这几乎正是您的查询正在执行的操作。这是加快查询速度的最简单/最灵活的方法。

另一种更节省内存的方法是将您的用户 ID 和时间戳作为字符串存储在 _id 中,例如 _id : "USER_ID:DATETIME。例如:

{_id : "12345:20120501123000"}
{_id : "15897:20120501124000"}
{_id : "15897:20120501125000"}

注意 _id 是一个字符串,而不是 MongoId。那么你上面的查询就变成了一个正则表达式:

db.userNotification.find({ "_id" : /^53:/ }).limit(21).sort({ "_id" : -1 });

正如预期的那样,这将按降序返回 userId 53 的所有通知。内存效率的部分是两方面的:

  1. 您只需要一个索引字段。 (索引与数据竞争内存,并且通常是几个 gig)
  2. 如果您的查询经常与获取较新的数据有关,则当索引太大而无法容纳整个数据时,右平衡索引可让您最常在内存中工作。

回复:计数。计数确实需要时间,因为它会扫描整个集合。

回复:您的架构。对于您的数据集,我猜这是利用您的记忆的最佳方式。当对象变大并且您的查询扫描多个对象时,它们将需要全部加载到内存中(当我在 2GB RAM 机器上使用 2000 个 2MB 对象进行排序时,我已经让 OOM 杀手杀死了我的 mongod 实例)。对于大型对象,您的 RAM 使用量将大幅波动(更不用说它们在一定程度上受到限制)。使用您当前的模式,mongo 将更轻松地仅加载您正在查询的数据,从而减少交换和更一致的内存使用模式。

【讨论】:

  • 这不太正确。 count 将使用索引。并且在复合索引中存储一个与两个字段一样大的字段并不会真正为您节省很多,而且您将失去使用该索引进行排序的能力。
  • @AsyaKamsky - 感谢您提供信息。据我所知,在
  • 他们甚至在 2.4 之前就在使用索引,但效率不高。想象一下在特定范围内遍历和读取索引 b-tree 中的条目与仅计算其中有多少条目之间的性能差异。
【解决方案2】:

最重要的是,您目前似乎没有索引来支持查询用户的最新通知。

你需要一个关于 userId 的复合索引,_id。这将支持仅按 userId 查询的查询,但它们也被按 _id 排序/限制的 userId 查询使用。

当您添加 {userId:1, _id:-1} 索引时,不要忘记仅删除 userId 上的索引,因为它会变得多余。

就 count() 确保您使用的是 2.4.3(最新版本)而言,count() 使用索引的方式有了显着改进,从而带来了更好的性能。

【讨论】:

    【解决方案3】:

    我刚刚尝试复制您的问题。在 userNotifications 中创建了 140.000.000 个插入。 如果没有 userId 上的索引,我得到了 3-4 秒的响应。在我在 userId 上创建索引后,时间下降到几乎是即时响应。

    db.userNotifications.getIndexes()
    

    [ { “v”:1, “钥匙” : { “_id”:1 }, "ns" : "test.userNotifications", “名称”:“id” }, { “v”:1, “钥匙” : { “用户 ID”:1 }, "ns" : "test.userNotifications", “名称”:“用户 ID_1” } ]

    另一件事是:当您的选择发生时,系统是否会不断写入 mongo userNotification 集合?如果发生这种情况,Mongo 会锁定整个集合。如果是这样
    我会在主从之间拆分读写(请参阅复制)并进行一些分片。顺便提一句。您的应用使用什么语言?

    【讨论】:

    • 您不能在主要和次要之间“拆分”读取和写入,因为次要执行的写入次数必须与主要次要完全相同。
    【解决方案4】:

    一种选择是尝试分片,然后您可以在分片之间平均分配通知,这样当您需要选择时,您将扫描更小的数据子集。但是需要决定你的分片将使用什么。对我来说,它看起来像 operationType 或 userName 但我不太了解您的数据。另一件事是你为什么按_id排序?

    【讨论】:

    • 因为我需要用户最近插入的通知
    • 当它们没有最佳索引时,分片不是答案。
    猜你喜欢
    • 1970-01-01
    • 2012-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多