这是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的精髓和超出传统数据库设计难题中的过墙梯。
本文分享自 AustinDatabases 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!