3

我们正在尝试使用 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 通知。所以我们改成这个。

4

4 回答 4

3

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

另一种更节省内存的方法是将您的 userId 和时间戳作为字符串存储在 _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. 您只需要一个索引字段。(索引与数据竞争内存,通常大小为几吉格)
  2. 如果您的查询通常是关于获取较新的数据,那么当索引太大而无法容纳整个数据时,右平衡索引可以让您最常在内存中工作。

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

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

于 2013-04-26T12:21:06.887 回答
0

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

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

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

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

于 2013-04-28T21:26:59.610 回答
0

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

db.userNotifications.getIndexes()

[ {“v”:1,“key”:{“_id”:1},“ns”:“test.userNotifications”,“name”:“ id ”},{“v”:1,“key”: {“userId”:1},“ns”:“test.userNotifications”,“name”:“userId_1”}]

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

于 2013-04-26T11:07:05.943 回答
0

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

于 2013-04-26T09:16:13.573 回答