需求

社交类的APP需求,一般都会引入“朋友圈”功能,这个产品特性有一个非常重要的功能就是评论体系。
先整理下需求:

  • 这个APP希望点赞和评论信息都要包含头像信息:
  1. 点赞列表,点赞用户的昵称,头像;
  2. 评论列表,评论用户的昵称,头像;
  • 数据查询则相对简单:
  1. 根据分享ID,批量的查询出10条分享里的所有评论内容;

建模

不好的

跟据上面的内容,先来一个非常非常"朴素"的设计:

{
  "_id": 41,
  "username": "小白",
  "uid": "100000",
  "headurl": "http://xxx.yyy.cnd.com/123456ABCDE",
  "praise_list": [
    "100010",
    "100011",
    "100012"
  ],
  "praise_ref_obj": {
    "100010": {
      "username": "小一",
      "headurl": "http://xxx.yyy.cnd.com/8087041AAA",
      "uid": "100010"
    },
    "100011": {
      "username": "mayun",
      "headurl": "http://xxx.yyy.cnd.com/8087041AAB",
      "uid": "100011"
    },
    "100012": {
      "username": "penglei",
      "headurl": "http://xxx.yyy.cnd.com/809999041AAA",
      "uid": "100012"
    }
  },
  "comment_list": [
    "100013",
    "100014"
  ],
  "comment_ref_obj": {
    "100013": {
      "username": "小二",
      "headurl": "http://xxx.yyy.cnd.com/80232041AAA",
      "uid": "100013",
      "msg": "good"
    },
    "100014": {
      "username": "小三",
      "headurl": "http://xxx.yyy.cnd.com/11117041AAB",
      "uid": "100014",
      "msg": "bad"
    }
  }
}

可以看到,comment_ref_objpraise_ref_obj两个字段,有非常重的关系型数据库痕迹,猜测,这个系统之前应该是放在了普通的关系型数据库上,或者设计者被关系型数据库的影响较深。而在MongoDB这种文档型数据库里,实际上是没有必要这样去设计,这种建模只造成了多于的数据冗余。

另外一个问题是,url占用了非常多的信息空间,这点在压测的时候会有体现,带宽会过早的成为瓶颈。同样username信息也是如此,此类信息相对来说是全局稳定的,基本不会做变化。并且这类信息跟随评论一起在整个APP中流转,也无法结局”用户名修改“的需求。

根据这几个问题,重新做了优化的设计建议。

推荐的设计

{
  "_id": 41,
  "uid": "100000",
  "praise_uid_list": [
    "100010",
    "100011",
    "100012"
  ],
  "comment_msg_list": [
    {
      "100013": "good"
    },
    {
      "100014": "bad"
    }
  ]
}

对比可以看到,整个结构要小了几个数量级,并且可以发现url,usrname信息都全部不见了。那这样的需求应该如何去实现呢?

从业务抽象上来说,url,username这类信息实际上是非常稳定的,不会发生特别大的频繁变化。并且这两类信息实际上都应该是跟uid绑定的,每个uid含有指定的url,username。是最简单的key,value模型。所以,这类信息非常适合做一层缓存加速读取查询。

进一步的,每个人的好友基本上是有限的,头像,用户名等信息,甚至可以在APP层面进行缓存,也不会消耗移动端过多容量。但是反过来看,每次都到后段去读取,不但浪费了移动端的流量带宽,也加剧了电量消耗。

总结

MongoDB的文档模型固然强大,但绝对不是等同于关系型数据库的粗暴聚合,而是要考虑需求和业务,合理的设计。有些人在设计时,也会被文档模型误导,三七二十一一股脑的把信息塞到一个文档中。反而最后会带来各种使用问题。