我们的系统是运营商级并且非常强大,它已经过负载测试,每秒处理5000个事务,并且对于每个事务,文档被插入到单个MongoDB集合中(在此应用程序中没有更新或查询,它是写的 - 只要)。 这相当于每天约700MM的文件,这是我们的基准。
MongoDB部署尚未分片,我们有1x replicaset,1个master和2个slave,所有这些都是ec2上的m2.2xlarge类型实例。 每个实例都由1TB RAID0条带支持,该条带由8个卷组成(无PIOPS)。 我们使用带有c ++本机BSON解析器的node-mongodb-native驱动程序来获得最佳写入性能,并尝试相应地对文档结构建模。
{
_id: ObjectID(),
a: ‘string’,
b: ‘string’,
c: ‘string’ or <int>,
g: ‘string’ or <not_exist>,
t: ISODate(),
h: <int>,
d: <int>,
m: <int>,
y: <int>
}
col.ensureIndex({ c: 1, a: 1, y: 1, m: 1, d: 1, h: 1 });
col.aggregate([
{ $match: { c: 'customer_1', y: 2013, m: 11 } },
{ $group: { _id: { c: '$c', y: '$y', m: '$m' }, a: { $addToSet: '$a' }, t: { $sum: 1 } } },
{ $unwind: '$a' },
{ $group: { _id: { c: '$_id.c', y: '$_id.y', m: '$_id.m', t: '$t' }, a: { $sum: 1 } } },
{ $sort: { '_id.m': 1 } },
{
$project: {
_id: 0,
c: '$_id.c',
y: '$_id.y',
m: '$_id.m',
a: 1,
t: '$_id.t'
}
},
{ $group: { _id: { c: '$c', y: '$y' }, monthly: { $push: { m: '$m', a: '$a', t: '$t' } } } },
{ $sort: { '_id.y': 1 } },
{
$project: {
_id: 0,
c: '$_id.c',
y: '$_id.y',
monthly: 1
}
},
{ $group: { _id: { c: '$c' }, yearly: { $push: { y: '$y', monthly: '$monthly' } } } },
{ $sort: { '_id.c': 1 } },
{
$project: {
_id: 0,
c: '$_id.c',
yearly: 1
}
}
]);
[
{
"yearly": [
{
"y": 2013,
"monthly": [
{
"m": 11,
"a": 3465652,
"t": 9844935
}
]
}
],
"c": "customer_1"
}
]
63181ms
{
"cursor" : "BtreeCursor c_1_a_1_y_1_m_1_d_1_h_1",
"isMultiKey" : false,
"n" : 9844935,
"nscannedObjects" : 0,
"nscanned" : 9844935,
"nscannedObjectsAllPlans" : 101,
"nscannedAllPlans" : 9845036,
"scanAndOrder" : false,
"indexOnly" : true,
"nYields" : 27,
"nChunkSkips" : 0,
"millis" : 32039,
"indexBounds" : {
"c" : [ [ "customer_1", "customer_1" ] ],
"a" : [ [ { "$minElement" : 1 }, { "$maxElement" : 1 } ] ],
"y" : [ [ 2013, 2013 ] ],
"m" : [ [ 11, 11 ] ],
"d" : [ [ { "$minElement" : 1 }, { "$maxElement" : 1 } ] ],
"h" : [ [ { "$minElement" : 1 }, { "$maxElement" : 1 } ] ]
}
}
鉴于插入频率很高,我们需要随着时间的推移执行远程聚合查询。考虑到应用程序可以在一小时内插入30MM文档,是时间桶的良好实践吗?
我们理解MongoDB可以在几秒钟内查询数十亿个文档:
MapReduce(并行)会更好吗?
答案 0 :(得分:7)
你想知道为什么你的聚合如此“长”。除了@Avish在他的回答中提出的观点(你正在做一些不必要的步骤),你还必须考虑你的物质资源和时间。
以下是“解释”的一部分:
"n" : 9844935, "nscannedObjects" : 0, "nscanned" : 9844935, "scanAndOrder" : false, "indexOnly" : true, "nYields" : 27, "millis" : 32039,
值得注意的是,聚合需要32秒(而不是50秒),因为它从索引本身获取了所有信息,所以它从来不需要获取单个文档。它不需要做任何内存中的排序。但它确实要产生27次......为什么会这样?读取器进程产生的原因有两个 - 一个是写入等待(写入器优先,长时间运行读取必须产生它们)或者页面错误 - 所有操作必须在它们尝试访问的任何数据时产生不在RAM中(这是为了防止进程阻止其他人在等待将数据加载到RAM中时进行工作)。
想到的问题是:DB冷吗?索引是否适合RAM?那些读取必须与之抗争的同时发生了写作吗?
我会检查索引是否适合RAM,运行the "touch" command以确保它 在RAM中,简化我的聚合管道,不做不必要的工作,然后再次运行,连续几次,看看时间如何。
答案 1 :(得分:0)
我不明白为什么您需要$unwind
a
值,以及为什么您需要在任何时候按总计分组。这似乎也是错误的,因为对于每个未解除的a
值,您将输出为整个时间段计算的相同t
值。
据我所知,您的查询应如下所示:
col.aggregate([
// Pre-filter
{ $match: { /* ... */ } },
// Pre-sort to aid in grouping
{ $sort: { c: 1, y: 1, m: 1, a: 1 },
// Group by month, customer and `a` to find unique `a` values and their totals
{ $group: {
_id: { c: '$c', y: '$y', m: '$m', a: '$a' },
t: { $sum: 1 }
}
},
// Not sure if another sort is required at this point, I'd assume MongoDB
// is smart enough to understand we're grouping by a subset of the original
// grouping key
// Group by month and customer to count unique `a` values and grand total
{ $group: {
_id: { c: '$_id.c', y: '$_id.y', m: '$_id.m' },
a: { sum: 1 }, // number of unique `a` values in group
t: { sum: '$t' } // rolled-up total of all `a`-totals in group
},
// You can tack on further groupings by year and customer here,
// although I believe these would be better done in the UI layer
]);
所以基本上,带有展开和重新分组的管道的开头以及中间排序可能会减慢你的速度。看看这个版本是否表现更好,并尝试在分组之间添加排序,如果它们有帮助的话。
答案 2 :(得分:0)
我建议在该订单上尝试y,m和d(年,月,日期是?)的索引,因为那些已知为int,而当前的那个c可以是一个int或一个字符串。由于数据是基于时间的,因此也可能更有意义。