Mongo的模型设计

标签: mongo  

数据模型

什么是数据模型?

1)数据模型是一组由符号、文本组成的集合,用以准确表达信息,达到有效交流、沟通 的目的。

2)Steve Hoberman 霍伯曼. 数据建模经典教程

数据模型设计的元素

1)实体 Entity

2)属性 Attribute

3)关系 Relationship

传统模型设计:从概念到逻辑到物理

概念模型 CDM 逻辑模型 LDM 物理模型 PDM
目的 描述业务系统要管理的对象 基于概念模型,详细列出 所有实体、实体的属性及 关系 根据逻辑模型,结合数据库 的物理结构,设计具体的表 结构,字段列表及主外键
特点 用概念名词来描述现实中 的实体及业务规则,如 “联系人” 基于业务的描述 和数据库无关 技术实现细节 和具体的数据库类型相关
主要使用者 用户 需求分析师 需求分析师 架构师及开发者 开发者 DBA

MongoDB 文档模型设计的三个误区

  1. 不需要模型设计
  2. MongoDB 应该用一个超级大文档来组织所有数据
  3. MongoDB 不支持关联或者事务

关于 JSON 文档模型设计

  • 文档模型设计处于是物理模型设计阶段 (PDM)
  • JSON 文档模型通过内嵌数组或引用字段来表示关系
  • 文档模型设计不遵从第三范式,允许冗余。

为什么人们都说 MongoDB 是无模式?

  • 严格来说,MongoDB 同样需要概念/逻辑建模
  • 文档模型设计的物理层结构可以和逻辑层类似
  • MongoDB 无模式由来: 可以省略物理建模的具体过程

关系模型 vs 文档模型

文档模型的设计原则:性能和易用

关系数据库 JSON 文档模型
模型设计层次 概念模型 逻辑模型 物理模型 概念模型 逻辑模型
模型实体 集合
模型属性 字段
模型关系 关联关系,主外键 内嵌数组,引用字段

文档模型设计

第一步: 建立基础文档模型

  1. 根据概念模型或者业务需求推导出逻辑模型 – 找到对象
  2. 列出实体之间的关系(及基数) - 明确关系
  3. 套用逻辑设计原则来决定内嵌方式 – 进行建模
  4. 完成基础模型构建

基础建模小结

  • 90:10 规则: 大部分时候你会使用内嵌来表示 1-1,1-N,N-N
  • 内嵌类似于预先聚合(关联)
  • 内嵌后对读操作通常有优势(减少关联)

第二步: 根据读写工况细化

  • 最频繁的数据查询模式
  • 最常用的查询参数
  • 最频繁的数据写入模式
  • 读写操作的比例
  • 数据量的大小

基于内嵌的文档模型,根据业务需求,

1)使用引用来避免性能瓶颈

2)使用冗余来优化访问性能

引用模式下的关联查询

Contacts
	name: "TJ Tang", 
	company: ”TAPDATA" 
	group_ids: [1,2,3...]
Groups
	groups_id:1
	name:“Friends”
db.contacts.aggregate([ 
{
	$lookup:
	{
		from: "groups",
		localField: "group_ids", 
		foreignField: "group_id", 
		as: "groups"
	} 
}
])

什么时候该使用引用方式?

  • 内嵌文档太大,数 MB 或者超过 16MB
  • 内嵌文档或数组元素会频繁修改
  • 内嵌数组元素会持续增长并且没有封顶

MongoDB 引用设计的限制

  • MongoDB 对使用引用的集合之间并无主外键检查

  • MongoDB 使用聚合框架的 $lookup 来模仿关联查询

  • $lookup 只支持 left outer join

  • $lookup 的关联目标(from)不能是分片表

第三步:套用设计模式

  • 文档模型: 无范式,无思维定式,充分发挥想象力
  • 设计模式: 实战过屡试不爽的设计技巧,快速应用

举例:一个 IoT 场景的分桶设计模式,可以帮助把存储空间降低 10 倍并且查询效率提 升数十倍.

模式集锦

分桶模式

场景 痛点 设计模式的方案及优点
时序数据 物联网 智慧城市 智慧交通 数据点采集频繁,数据量太多 利用文档内嵌数组,将一个时 间段的数据聚合到一个文档里。 大量减少文档数量 大量减少索引占用空间

列传行模式

场景 痛点 设计模式方案及优点
产品属性 ‘color’, ‘size’, ‘dimensions’, … 多语言(多国家)属性 文档中有很多类似的字段 会用于组合查询搜索,需要见 很多索引 转化为数组,一个索引解决所 有查询问题

版本字段

2019.01 版本1.0

{
	"_id" : ObjectId("5de26f197edd62c5d388babb"), 
	"name" : "TJ",
	"company" : "Tapdata",
}

2019.03 版本2.0

{
	"_id" : ObjectId("5de26f197edd62c5d388babb"), 
	"name" : "TJ",
	"company" : "Tapdata",
	"wechat": "abc123",
	"schema_version": "2.0"
}
场景 痛点 设计模式方案及优点
任何有版本衍变的数据库 文档模型格式多,无法知道其 合理性升级时候需要更新太多文档 增加一个版本号字段 快速过滤掉不需要升级的文档

近似计算模式

问题: 统计网页点击流量

每访问一个页面都会产生一次数据库计数 更新操作

统计数字准确性并不十分重要

解决方案: 用近似计算

每隔10 (X)次写一次

场景 痛点 设计模式方案及优点
网页计数 各种结果不需要准确的排名 写入太频繁,消耗系统资源 间隔写入,每隔10次或者100次 大量减少写入需求

预聚合模式

问题: 业绩排名,游戏排名,商品统计等精确统计

热销榜: 某个商品今天卖了多少,这个星期卖了多少,这个月卖了多少?

电影排行: 观影者,场次统计

传统解决方案: 通过聚合计算

痛点: 消耗资源多,聚合计算时间长

解决方案: 用预聚合字段

{
	product: "Bike", 
	sku: "abc123456", 
	quantitiy: 20394, 
	daily_sales: 40, 
	weekly_sales: 302, 
	monthly_sales: 1419
}

db.inventory.update(
	{_id:123},
	{$inc: {
		quantity: -1, 
		daily_sales: 1, 
		weekly_sales: 1, 
		monthly_sales: 1,
	}
})
场景 痛点 设计模式方案及优点
准确排名 排行榜 统计计算耗时,计算时间长 模型中直接增加统计字段 每次更新数据时候同时更新统计值

「真诚赞赏,手留余香」

请我喝杯咖啡?

使用微信扫描二维码完成支付

相关文章