我使用MongoDB作为队列,使用PHP-Queue作为获取数据的方法。这是一个POC,我在OSX机器上运行。我发现Mongo的性能非常慢,即findmodify函数。我在PHP端进行了大量测试,PHP处理只占大约5%的时间。当我填写Mongo集合时,例如10,000条消息,它填充非常快,大约3-5秒。但是当我清空它时,它需要 250秒。这个时间只有大约10秒在php端。检查mongod进程,它永远不会超过大约60MB,但CPU在整个时间内都会超过90%。我已经为集合编制了索引,下面是消息数据和索引的示例。
示例消息(这是队列中10,000条类似消息之一):
{
"_id": ObjectId("526c47d5c5008c1d5cd63ef8"),
"payload": {
"0": {
"EVENT_HEADER_KEY": NumberInt(9094775),
"event_name": "Account Change",
"source_name": "Work",
"event_category_name": "Complex Events",
"EVENT_TIMESTAMP": "Aug 17 2013 12:00:00:000AM",
"PARENT_HEADER_KEY": null,
"year": NumberInt(2013),
"month": NumberInt(10),
"Company_Name": "ACME PRODUCTS, INC.",
"Company_Email": "blabla",
"Company_Phone": "555-555-5555",
"First_Name": "Jon",
"Last_Name": "Doe",
"ID_NUMBER": "111111111",
"created_by": "Load Job Name",
"created_at": "Oct 18 2013 04:07:31:140PM",
"product_analytical_category": "blabla",
"_Event_Type": "blabla",
"CUSTOMER_ID": "111111111"
}
},
"running": false,
"resetTimestamp": ISODate("2038-01-19T03:14:07.0Z"),
"earliestGet": ISODate("1970-01-01T00:00:00.0Z"),
"priority": 0,
"created": ISODate("2013-10-26T22:53:09.440Z")
}
此集合的索引,似乎是自动创建的:
{
"_id": NumberInt(1)
}
检查mongo.log,我可以看到,当我清空队列时,对于大约70条消息,每条消息只需要大约1毫秒,然后opid会改变,然后会有300-900毫秒的延迟它以每条消息大约1毫秒的相同速度继续使用新的opid。这些opid更改占250秒处理时间的大约50-100秒,因此还有更多进行。
摘录自mongo.log:
**Sat Oct 26 15:15:25.189** [conn4] warning: ClientCursor::yield can't unlock b/c of recursive lock ns: test.abe top: { **opid: 20064**, active: true, secs_running: 0, op: "query", ns: "test", query: { findandmodify: "abe", query: { running: false, earliestGet: { $lte: new Date(1382825725143) } }, update: { $set: { resetTimestamp: new Date(1382825785000), running: true } }, fields: { payload: 1 }, sort: { priority: 1, created: 1 } }, client: "127.0.0.1:53045", desc: "conn4", threadId: "0x119024000", connectionId: 4, locks: { ^: "w", ^test: "W" }, waitingForLock: false, numYields: 0, lockStats: { timeLockedMicros: {}, timeAcquiringMicros: { r: 0, w: 3 } } }
**Sat Oct 26 15:15:25.190** [conn4] warning: ClientCursor::yield can't unlock b/c of recursive lock ns: test.abe top: { **opid: 20064**, active: true, secs_running: 0, op: "query", ns: "test", query: { findandmodify: "abe", query: { running: false, earliestGet: { $lte: new Date(1382825725143) } }, update: { $set: { resetTimestamp: new Date(1382825785000), running: true } }, fields: { payload: 1 }, sort: { priority: 1, created: 1 } }, client: "127.0.0.1:53045", desc: "conn4", threadId: "0x119024000", connectionId: 4, locks: { ^: "w", ^test: "W" }, waitingForLock: false, numYields: 0, lockStats: { timeLockedMicros: {}, timeAcquiringMicros: { r: 0, w: 3 } } }
**Sat Oct 26 15:15:25.507** [conn4] warning: ClientCursor::yield can't unlock b/c of recursive lock ns: test.abe top: { **opid: 20141**, active: true, secs_running: 0, op: "query", ns: "test", query: { findandmodify: "abe", query: { running: false, earliestGet: { $lte: new Date(1382825725501) } }, update: { $set: { resetTimestamp: new Date(1382825785000), running: true } }, fields: { payload: 1 }, sort: { priority: 1, created: 1 } }, client: "127.0.0.1:53045", desc: "conn4", threadId: "0x119024000", connectionId: 4, locks: { ^: "w", ^test: "W" }, waitingForLock: false, numYields: 0, lockStats: { timeLockedMicros: {}, timeAcquiringMicros: { r: 0, w: 3 } } }
这些10,000条消息的整个日志基本相同。将有一个长序列的findandmodify(),每个消息只需要1毫秒,然后opid会发生变化,并且有一个延迟可能需要几乎一秒钟。我不知道这是否表明任何重要的事情,但我是Mongo的新手,我正在努力找到任何看起来很有希望的模式。
更新:
查询检查字段'running'是否为false,它还检查earliestGet字段是否比epoch更新(即1-1-1970)。我在这些字段中添加索引无济于事。由于这些字段对于集合中的所有消息(“假”和1970年1月1日)都是相同的,这可能就是为什么我对它们的索引只会增加查询时间。我不知道我该怎么做才能让它正常工作。它似乎应该抓住它发现的比1970年1月1日更新的第一条记录,但显然Mongo仍然经历了整个集合,这使得查询太慢而不实用。此外,即使我没有选择标准,我仍然得到202秒的响应时间 - 更快,但仍然是不可接受的。我还看到那些“产量无法解锁b / c的递归锁定ns:”消息,我认为只会在查询未索引的字段时显示。
答案 0 :(得分:4)
您缺少一个非常关键的索引,该索引将用于findAndModify
命令的查询和排序部分。如果没有该索引,则强制每个命令扫描整个集合,然后对完整的结果集进行排序,这是低效的。现在你说“我已将索引编入索引”,但你只提到了'_id'索引,它始终存在并无法帮助你。
建议:至少在运行的字段和earliestGet上添加复合索引。有一个索引可能包含排序字段可能帮助,但由于我认为每个查询的匹配文档数量相对较小,因此内存排序可能不是一个因素。 / p>
命令:
db.abe.ensureIndex({running:1, earliestGet:1})
在评论讨论中发现,正在运行的,最早的GetGet索引根本没有选择性 - 但由于您要排序只获取第一个匹配的文档,另一种方法是在排序列上添加索引:
命令:
db.abe.ensureIndex({ priority: 1, created: 1 })
答案 1 :(得分:1)
如果没有更详细的描述你在修改阶段到底做了什么,很难给出明确的答案。从日志来看,您似乎可以执行以下更新:
db.abc.findAndModify(
query: { running: false, earliestGet: { $lte: new Date(1382825725143) } },
update: { $set: { resetTimestamp: new Date(1382825785000), running: true } }
)
且earliestGet
字段和running
字段没有索引。由于基数较低,running
上的添加索引不应该产生真正的差异,但earliestGet
上缺少索引可能是一个真正的问题。
关于warning: ClientCursor::yield can't unlock b/c of recursive lock ns:
消息,您可以看到以下问题:MongoDB: Geting "Client Cursor::yield can't unlock b/c of recursive lock" warning when use findAndModify in two process instances