MongoDB 设计模式集锦 - 解决具体问题。
-
一个好的设计模式可以显著地 提升数据读写的效率,降低资源的需求。
-
更多MongoDB的设计模式:
表现形式类 数据访问类 组织结构类 列转行 子集 分桶 文档版本 近似处理 预聚合
下面开始具体的问题
问题1:大文档,很多字段,很多索引
- 索引虽然提高了查询性能,但是在写入时 索引过多 会严重影响性能。
# 记录电影的各地区的上映日期
{
title: "Dunkirk",
...
release_HK: "2017/07/20",
release_USA: "2017/07/21",
release_RUS: "2017/07/20",
release_CHN: "2017/09/01",
...
}
# 需要很多索引
{release_HK: 1}
{release_USA: 1}
{release_RUS: 1}
{release_CHN: 1}
...
- 解决方案:列转行
{
title: "Dunkirk",
...
releases: [
{country: "HK", date: "2017/07/20"},
{country: "USA", date: "2017/07/21"},
{country: "RUS", date: "2017/07/20"},
{country: "CHN", date: "2017/09/01"}
...
]
}
# 索引只需一个组合索引。
db.movies.createIndex({"releases.country":1,"releases.date":1})
-
列转行 总结:
场景 痛点 设计模式方案及优点 产品属性'color', 'size', 'dimensions',...
多语言(多国家)属性;文档中有很多同类型的类似字段;
会用于组合查询搜索,需要建立很多索引;转化为数组,一个索引解决所有的查询问题。
问题2:Mongo中的灵活模型,如何管理文档的不同版本?
- Mongo的模型虽然灵活了支持任意的属性增减,但是带来的问题就是数据的不确定性。
- 解决方案:版本字段
# 2019.01 版本1.0
{
"_id": ObjectId("5de26f197edd62c5d388babb"),
"name": "TJ",
"company": "Tapdata",
}
# 2019.03 版本2.0
{
"_id": ObjectId("5de26f197edd62c5d388babb"),
"name": "Tom",
"company": "Tapdata",
"wechat": "tom23333", # 新增了字段
"schema_version": "2.0" # 版本号
}
-
版本字段 总结:
场景 痛点 设计模式方案及优点 任何有版本衍变的数据库。 文档模型格式多,无法知道其合理性;
升级时需要更新太多文档;增加一个版本号字段;
快速过滤掉不需要升级的文档;
升级时候对不同版本的文档做不同的处理;
问题3:统计网页点击流量
-
每访问一个网页都会产生一次数据库计数更新操作,统计数字准确性并不十分重要。
-
解决方案:近似计算
# 每次 +1, 加10次。 for i in range(0,10) {$inc:{views:1}} # 使用近似计算,十次访问每次随机,随机数触发一次 + 10。 # 近似地每隔 10次(或n次)写一次。 for i in range(0,10) if random(0,9) == 0 increment by 10
-
近似计算 总结:
场景 痛点 设计模式方案及优点 网页计数
各种结果不需要准确的排名。写入太频繁,消耗系统资源。 间隔写入,每隔 10次或100次,大量减少写入需求。
问题4:业绩排名,游戏排名,商品统计等精确排名
-
解决方案:预聚合字段 $inc
{ id: 1003, product: "Bike", quantitiy:200394, # 库存 daily_sales:40, # 日销量 weekly_sales:302, # 周销量 monthly_sales:1420,# 月销量 } db.invertory.update( {id: 1003}, {$inc:{ quantity:-1, daily_sales:1, weekly_sales:1, monthly_sales:1 }} )
-
预聚合 总结:
场景 痛点 设计模式方案及优点 准确排名
排行榜
商品热销榜,某商品的日销量/周销量/月销量
电影排行,观影人次场次统计聚合计算消耗资源多,计算时间长。 模型中直接增加统计字段,每次更新数据时候同时更新统计值。