MongoDB文档模型设计的三个误区
1、不需要模型设计
2、MongoDB应该用一个超级大文档来组织所有数据
3、MongoDB不支持关联或者事物
文档模型的设计原则
性能和易用
关系模型VS文档模型
关系数据库 | JSON文档模型 | |
---|---|---|
模型设计层次 | 概念模型 逻辑模型 物理模型 | 概念模型 逻辑模型 |
模型实体 | 表 | 集合 |
模型属性 | 列 | 字段 |
模型关系 | 关联关系,主外键 | 内嵌数组,引用字段 |
使用一些设计模型对模型进行优化
1、分桶设计。
场景:物联网场景下的海量数据处理 - 飞机监控数据。对十万架飞机进行日记记录,每分钟记录一次,一年产生的数据量将以TB为单位。
一个文档记录一架飞机一分钟的记录
{
"_id":"20200509204243:C919",
"name":"C919",
"date":"2020-05-09",
"time":"20:42:43",
"events": {
"a": "xx",
"b": "xx",
"c": "xx"
}
}
数量 | 每分钟一个文档 |
---|---|
文档条数 | 100000 * 365 * 24 * 60 = 526亿 |
索引大小 | 526亿 * 19B = 930GB(粗略地将_id当成索引) |
文档平均大小 | 132B |
数据大小 | 526亿 * 132B = 6461GB |
使用分桶设计进行优化,一个文档记录一架飞机一个小时的数据
{
"_id":"20200509204243:C919",
"name":"C919",
"date":"2020-05-09",
"time":"20:42:43",
"events": [
{
"a": "xx",
"b": "xx"
},
{
"a": "xx",
"b": "xx"
},
......
]
}
数量 | 每小时一个文档 |
---|---|
文档条数 | 100000 * 365 * 24 = 8.76亿 |
索引大小 | 8.76亿 * 19B = 15GB(粗略地将_id当成索引) |
文档平均大小 | 660B |
数据大小 | 8.76亿 * 660B = 538GB |
小结
场景 | 痛点 | 设计模式的方案及优点 |
---|---|---|
时序数据 物联网 智慧城市 智慧交通 | 数据点采集频繁,数据量太多 | 利用文档内嵌数组,将一个时间段的数据聚合到一个文档里 大量减少文档数量 大量减少索引占用空间 |
2、列转行
场景:大文档,很多字段,很多索引
{
"title":"xxx",
...
"release_USA":"2020-05-04",
"release_UK":"2020-05-09",
"release_China":"2020-05-01",
...
}
使用列转行
{
"title":"xxx",
...
releases: [
{"country":"USA", "date":"2020-05-04"},
{"country":"UK", "date":"2020-05-09"},
{"country":"China", "date":"2020-05-01"},
...
]
}
// 创建一个联合索引
db.<集合>.createIndex({"releases.country":1}, {"releases.date":1})
小结
场景 | 痛点 | 设计模式的方案及优点 |
---|---|---|
产品属性比较多 | 文档中有很多类似的字段 会使用组合查询搜索,需要建立很多索引 | 转换为数组,一个索引解决所有查询问题 |
3、添加版本号
场景:管理不同版本的模型
version 1.0
{
"_id":"123",
"name":"xxx",
"age":"xxx"
}
version 2.0
{
"_id":"123",
"name":"xxx",
"age":"xxx",
"city":"xxx",
"schema_version":"2.0" // 添加一个版本说明
}
小结
场景 | 痛点 | 设计模式的方案及优点 |
---|---|---|
任何有版本衍变的数据库 | 文档模型格式多,无法知道其合理性 升级时候需要更新太多文档 | 增加一个版本号字段 快速过滤掉不需要升级的文档 升级时候对不同版本的文档做不同的处理 |
4、使用近似值
场景:网页点击数统计(不需要精确的数据)。每访问一个页面都会产生一次数据库计数更新操作,这种场景下写的操作次数占总操作次数的大部分,可以使用近似值来替代,每隔十次进行写入操作。
小结
场景 | 痛点 | 设计模式的方案及优点 |
---|---|---|
网页计数 各种结果不需要准确的排名 | 写入太频繁,消耗系统资源 | 间隔写入,每隔10次或者100 大量减少写入需求 |
5、用预聚合字段
场景:业绩排名、游戏排名(需要精确的数据)。
{
"product":"xxx",
"quantity":123456,
"daily_sales":33,
"weekly_sales":230,
"monthly_sales":1200,
}
db.<集合>.update(
{"id":"xxx"},
{$inc:{
quantity: -1,
daily_sales: 1,
weekly_sales: 1,
monthly_sales: 1
}
}
)
小结
场景 | 痛点 | 设计模式的方案及优点 |
---|---|---|
准确排名 排行榜 | 统计时间耗时,需要较长的计算时间 | 模型中直接增加统计字段 每次更新数据时候同时更新统计值 |