我的应用类似于一个手机号码缴费的系统,根据手机号码的缴费订单之前销售的代理商进行返利。 当前的表结构如下: users: // 用户表,当前记录150 条 { _id: ObjectId; acceptOrder: [ObjectId]; // 订单 id }
orders: // 号码订单表,当前记录 600 条
{
_id: ObjectId;
haveNo: [ObjectId]; // 号码 id
}
rechargeOrders: // 充值订单表,当前记录 20k 条
{
_id: ObjectId;
_simcardNo: ObjectId; // 号码 id
fee: Number; // 充值金额
createdAt: Date; // 充值时间
paid: Boolean; // 话费是否已经支付
rebatePaid: Boolean; // 返利是否已经支付
}
当前需要按照用户的维度统计应结算的返利,代码(删减版)如下:
db.users.aggregate([
{$unwind: "$acceptOrder"},
{$project: {_id: "$_id", acceptOrder: "$acceptOrder"}},
{$lookup: {
from: "orders",
localField: "acceptOrder",
foreignField: "_id",
as: "orders"
}},
{$unwind: "$orders"},
{$project: {_id: "$_id", haveNo: "$orders.haveNo"}},
{$unwind: "$haveNo"},
{$lookup: {
from: "rechargeorders",
localField: "haveNo",
foreignField: "_simcardId",
as: "rechargeOrders"
}},
{$unwind: "$rechargeOrders"},
{$match: { "rechargeOrders.paid": true, "rechargeOrders.createdAt": {$lt: ISODate("2017-01-11T00:00:00.000+08:00") }}},
{$project: {_id: "$_id", fee: "$rechargeOrders.fee"}},
{$group: {
_id: {_id: "$_id", company: "$company"},
fee: {$sum: "$fee"},
count: {$sum: 1}
}}
])
当前的数据库版本为3.4.1。每个表的_id字段和一对多的数组都设置了索引。当前出现的问题是,在这样的数量级的情况下,使用上面的语句查询起来需要1700s左右。经过分析,瓶颈应该是在加上$group这句之后,在$group之前的语句最多只需要3s的时间。实在不知道应该如何优化?
。。。。。。同学你这pipeline太多了 看看能不能合并的 另外看看是否命中索引
@jiangzhuo 我也觉得pipeline的确太多了,但是好像业务逻辑就这样,用户->订单->号码->充值订单,我这还砍了一个号码的关联过程……莫非数据库不应该这么设计……
@mehunk 没有记错的话mongodb的 group 操作对索引的运用是比较差的 那么针对你这个业务场景,我的建议是,用$group之前的操作,加上一个以id为依据的排序,然后用返回的索引,自己维护一个map/array,在比对 cursor.next()中获取到的id之后,增加map/arraidy中对应的sum值。以你的情况来看的话,需要维护的内存是非常小的。
@aojiaotage 非常感谢你这么耐心细致的解答。我一会就按照你的方法试一下。不过就是觉得mongo既然提供了group,还需要自己用代码去实现有点别扭。 另外,我昨天在测试的过程中还发现了一个问题,下面这句,如果paid的条件是true的话,速度就非常快(3s以内),但是如果修改为false,速度就非常慢(具体的时间还会根据后面的createdAt的条件变化,但是基本上会慢10倍以上)。我给这个paid值加索引也丝毫不影响查询速度,通过开启explain查看,发现后面的match语句都没有使用索引,不知道为什么……
{$match: { "rechargeOrders.paid": true, "rechargeOrders.createdAt": {$lt: ISODate("2017-01-11T00:00:00.000+08:00") }}},