MongoDB进阶设计模式

使用了mongodb这么长时间,似乎遇到了瓶颈。本文通过几篇博客再次学习mongo,希望有新的认识。温故而知新~

MongoDB的主体认识:

  • 高可用
  • 分布式
  • 灵活模式
  • 文档数据库

传统关系模型(SQL)和文档模型的区别

文档模型的优点

  • 读写效率高:Data Locality
  • 可扩展能力强: 无关联易分库
  • 动态模式:灵活应付不同的数据模式
  • 模型自然:最接近于对象模型

MongoDB文档模式设计的基本策略

优先考虑内嵌,再去考虑引用

MongoDB设计模式原则

  • 为应用程序服务,而不是为了存储优化
  • 为实现最佳性能而设计

经典的模式设计案例

电商(ECommerce)

设计考量

  • 一个购物车数据项不会太大,一般项数少于100
  • 数据自动过期(15-30分钟无交互)
  • 用冗余的方式来提供读取性能

参考数据模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"_id": ObjectId("*")
"userid": 1234,
"last_activity": ISODate(...), // 使用TTL来自动删除未付款的购物车
"status": "active",
"items":[
{
"itemid": 1234,
"title": "Milk",
"price": 5.00,
"quantity": 1,
"img_url": "milk,jpg"
},
{
"itemid": 4567,
"title": "Eggs",
"price": 3.00,
"quantity": 1,
"img_url": "eggs.jpg"
}
]
}

对模型的操作

  • 添加商品到购物车

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    db.cart.update({
    "_id": ObjectId("*")
    }, {
    $push:{
    "items":{
    "itemid": 2345,
    "title": "Bread",
    "price": 2.00,
    "quantity": 1,
    "img_url": "bread.jpg"
    }
    },
    $set:{
    "last_activity": ISODate()
    }
    })
  • 更新某个商品的数量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    db.carts.update({
    "id": ObjectId("*")
    "items.itemid": 4567
    },{
    $set:{
    "item.$.quantity": 5,
    "last_activity": ISODate()
    }
    }
    )
  • 统计商品总数(聚合运算

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    db.cart.createIndex({"items.itemid": 1})
    db.cart.aggregate(
    { $match: {"items.itemid": 8910}}, # 筛选出购物车里id为8910的商品
    { $unwind: "$items" }, # 展开items数组,每个数组的元素变成一个文档
    { $group: {
    "_id": "$items.itemid",
    "amount": { "$sum" : "$items.quantity" }
    }
    }#使用聚合运算$sum吧每一件商品的数量求和
    )

社交(Social)

设计考量

  • 维护朋友关系-关注、被关注
  • 朋友圈设计、名人效应

经典文档设计的问题

1
2
3
4
5
6
{
"id": "Yuan",
"fullname": "Zhu Zheng Yuan",
"followers": ["Oscar", "Mandy"],
"following": ["Mandy", "Bert"]
}

 如果TJ我是一个明星,他们关注我的人可能有千万。一个千万级的数组会有两个问题:
1.有可能超出一个文档最大16M的硬性限制; 2. MongoDB数组太大会严重影响性能。
 解决方案是建立一个专门的集合来描述关注关系。

1
2
3
4
5
> db.follower.find()
{"_id": ObjectId(), "user": "Yuan", "following": "Mandy"}
{"_id": ObjectId(), "user": "Yuan", "following": "Bert"}
{"_id": ObjectId(), "user": "Oscar", "following" : "Yuan"}
{"_id": ObjectId(), "user": "Mandy", "following": "Yuan"}

微博墙的实现

微博墙:列表显示所有关注用户最新状态

解决方案:

  • 扇出读(常规玩法):当你需要去获得所有你关注用户的最新更新的时候,你就去到每一个你关注用户的数据区,把最新的一些数据取回来。[最慢服务器响应时间决定了总体的响应时间]
  • 扇出写(土豪玩法):当被关注用户发布微博的时候,一条数据会写多次:直接写到每一个关注你的粉丝的墙上。[写入需求会被放大]

参考资料:

  1. MongoDB 进阶模式设计:http://www.mongoing.com/mongodb-advanced-pattern-design
请zzy824喝杯咖啡
0%