开头还是介绍一下群,如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, OceanBase, Sql Server等有问题,有需求都可以加群群内有各大数据库行业大咖,可以解决你的问题。加群请联系 liuaustin3 ,(共2700人左右 1 + 2 + 3 + 4 +5 + 6 + 7 + 8 +9 )(1 2 3 4 5 6群均已爆满,新人进7群400+,8群,9群)
这是MongoDB 宣传周的第二集,本集将讨论
2 在模式之间迁移如何更简单,让应用停机的时间更少
3 更好的支持板结构化的数据结构
说到这两个问题,我们首先要引入两个知识点,嵌入文档和引用文档两个概念。
1 什么叫嵌入文档
如果从字面的意思理解,是一个文档中嵌套了或包含了另一个文档,这是一种关联数据的方式,利用这样的方式我们就直接避免了JOIN,对传统数据库的表的JOIN。
这也是MongoDB 化解性能问题的第一个方案,因为JOIN的操作在传统数据库就是一个消耗性能的操作。
任何一个搞传统数据库的人都明白访问一个表,要比同时访问两个表要快的道理,这个道理正是MongoDB 化解传统
数据库性能不友好的第一方案。
{
"_id": ObjectId("..."),
"name": "Austin",
"address": {
"street": "XX路XX号",
"city": "天津",
"zipcode": "100000"
},
"contacts": [
{ "type": "phone", "number": "138xxxxxxxx" },
{ "type": "email", "address": "Liuhuayang@example.com" }
]
}
正如上面的这个例子,一个是人的个人信息,一个是他的地址信息,
但我们设想一个人现在如果他有俩手机怎么办,他有3个邮箱怎么办,如果他还有真假名怎么办。
传统数据库怎么办,在弄一个表,在弄一个列来记录这些信息??
而在MongoDB中很容易解决这些问题。
{
"_id": ObjectId("..."),
"name": "Austin",
"address": {
"street": "XX路XX号",
"city": "天津",
"zipcode": "100000"
},
"contacts1": [
{ "type": "phone", "number": "138xxxxxxxx" },
{ "type": "email", "address": "Liuhuayang@example.com" }
],
"contacts2": [
{ "type": "phone", "number": "139xxxxxxxx" },
{ "type": "email", "address": "Liuaustin@example.com" }
]
}
你看这就变得简单多了,实际上在讲MongoDB的时候,PG这个数据也有类似的设计,比如PG数据库中的Hstore。这些都是要化解一些传统数据库解决不好,笨拙的地方。
那么这里我们稍微总结一下,嵌套的使用场景是什么
在一个一对少的场景下,使用场景解决 1 减少获取数据的次数,由多次,改为一次获取。
2 与核心信息有关的信息,可以不用分类的存储在核心信息周围
3 简化数据的建模,不进行范式化的建设
4 数据高频进行访问的场景
5 数据量本身不宜过大(单条document)
缺点也显而易见
1 数据可能存在冗余
2 更新数据会更加的复杂
3 使用的场景有局限性,如一对多就不合适了,或者多对多的关系场景。
在出现缺点的时候,我们就应该使用第二种方案,
引用。嵌套和引用是MongoDB给我们解决大部分问题的方案。引用主要使用在必须要进行关联,但两个部分实在合不成一个"集合”的状态下。
比如:一个汽车生产商,它的配件信息和车辆信息的对接。一辆车上万个零件,而一个零件可以用在几种车上,这样的情况下,显然不适合用嵌套了。
那么我们怎么办, 1建立一汽车零件表 2每种汽车有一个零件的数组,或者将各个部分的零件门类分类,然后将零件表里面的零件编号以数组的方式存储在另一个表里面。
下面就是一个例子第一个是零件表,第二个是车辆表
{
"_id": ObjectId("..."), // 零件ID
"part_number": "P123456", // 零件编号(唯一)
"name": "发动机", // 零件名称
"description": "高性能发动机", // 零件描述
"manufacturer": "ABC公司", // 制造商
"price": 10000, // 价格
"compatible_vehicles": [ // 兼容的车辆ID数组(使用引用)
ObjectId("..."),
ObjectId("...")
],
// 其他零件相关属性
}
{
"_id": ObjectId("..."), // 车辆ID
"vin": "VIN1234567890", // 车辆识别码(唯一)
"model": "Model X", // 车型
"year": 2023, // 生产年份
"parts": [ // 车辆包含的零件ID数组(使用引用)
ObjectId("..."),
ObjectId("..."),
ObjectId("...")
// ... 大量零件ID
],
"parts_categories": { //按零件类别存储零件ID,更方便查询
"engine": [ObjectId("..."), ObjectId("...")],
"transmission": [ObjectId("..."), ObjectId("...")],
"body": [ObjectId("..."), ObjectId("...")],
// ...其他类别
},
// 其他车辆相关属性
}
这里查询起来很简单,如果要查询一辆车的零件,那么值需要分两步走, 第一步:获取第一个表的信息,以及各个零件的ObjectID
第二步:获取第二个表的所有OBJECT_ID对应行的需要提取的信息。
实际上这和阿里的一些MySQL设计的军规如出一辙,强制数据查询解耦。
这样的好处非常明显,如果这两个表都在UPDATE的情况下,产生锁和block的可能性很低,虽然在MongoDB中会产生锁,但在这样的设计下,查询对更新的锁干扰将微乎其微。
同时我们还可以注意到,如果一辆车的零件增加和减少,都只和第一张表有关,如果零件作废了,需要标记,只和第二张表有关。
同时我们还可以注意到,引用可以双向引用,比如我通过零件也可以查到,这个零件用到那些车上,零件也可以自己来一个车辆的数组。
通过有效的索引设计,查询信息将非常快。
写到这里一定有人问,这么好,有什么缺点吗?
聚合,无条件的聚合,无条件聚合且集合的数据量非常大......
返回到这期的主题
2 在模式之间迁移如何更简单,让应用停机的时间更少
3 更好的支持板结构化的数据结构
举例上面的例子中,如果是二位表格,一定会涉及到加减字段,对字段进行格式化的处理,应用需要注意相关的DDL操作,尤其大表,而这一切再MongoDB中根本不存在,因为无结构化的数据,你可以随心,在结构化的数据中,添加一些非结构化的特性,这才是MongoDB的精髓和超出传统数据库设计难题中的过墙梯。
那么MongoDB是花卷(嵌套),还是引用方式(面条),反正都好吃,加点肉馅就是肉龙和意面。
MongoDB 相关文章
MongoDB 大俗大雅,高端的知识讲“低俗” -- 1 什么叫多模
MongoDB 合作考试报销活动 贴附属,MongoDB基础知识速通
MongoDB 年底活动,免费考试名额 7个公众号获得
MongoDB 使用网上妙招,直接DOWN机---清理表碎片导致的灾祸 (送书活动结束)
数据库 《三体》“二向箔” 思维限制 !8个公众号联合抽奖送书 建立数据库设计新思维
MongoDB 是外星人,水瓶座,怎么和不按套路出牌的他沟通?
MongoDB 2023年度纽约 MongoDB 年度大会话题 -- MongoDB 数据模式与建模