Mongodb 于其他的数据库不同的地方在于灵活,而恰恰是因为灵活导致设计的重要性,不好的设计对于MONGODB 的性能伤害是十分大的,在设计中MONGODB 需要注意以下几点
1 避免JOIN :虽然MYSQL 在设计中也尽量避免JOIN,但Mongodb 不同,MOGNODB 本身如果要采用JOIN 是要通过聚合框架来进行的,而聚合操作本身在MONGODB 中的支持并不好,虽然可以通过特殊的方式将两个COLLECTION 进行模拟JOIN 操作,但也仅仅是能操作,而不是一个有效的可以优化的操作。
2 数据的冗余:MONGODB 的设计中冗余的数据存在在部分场景是被需要的,反过来带来的问题是,数据的更新,如果选择的数据需要被更新则在多个Document 中更新数据将是一个困难的地方,所以冗余数据的选择也是一个需要考虑的地方,尤其是不是要对需要更新的数据进行冗余。
3 事务的一致性:MONGODB 在4.2后对事务有着非常好的支持,基于分布式的方式,这样的支持带来的是性能上的损耗,所以事务到底要不要使用,怎么使用是一个需要考虑的地方。
4 对于硬件的依赖,MONGODB 与其他数据库相比对于内存的依赖更为重要,并且一个好的IOPS的磁盘系统也是一个必需品,所以要想MONGODB 表现好,必须提供更大的内存和更好的磁盘系统。
下面通过一个实例来看看MONGODB的设计
1 连接 或 嵌入
连接 和 嵌入,是处理多表数据的一个方式,前者是非MONGODB的数据库常用的方式,通过关联和连接的方式来进行处理,而后者是MOGNODB 通常的使用的方式,将多个表融合到一个COLLECTION 中并通过 嵌套的方式在DOCUMENT 中体现。
下面我们举例一个简单的客户的订单系统,在系统中包含以下内容
1 客户的信息:只要是订购产品的客户必须在系统中有相关的客户信息的驻留
2 产品信息:只要销售的产品,在系统中必须有相关的信息
3 订购信息:客户购买一次产品就需要建立客户的订单信息
4 订单与产品之间的关系:每一个订单,并不一定只购买一个产品,可能是多个产品。
这里我们以设计的角度来看看如何将这些信息存放到MONGODB中
1 链接大法
使用连接的思路和传统数据库设计类似,这里面可以设计成四个COLLECTIONS
1 客户信息集合
2 产品信息集合
3 订单信息集合
4 订单与产品关联集合
1 客户信息集合
代码语言:javascript复制{"id”: ObjectXXXXX,
"customer_id: 客户ID",
“c_phone: 客户电话”,
“c_name: 客户姓名”,
“c_address: 客户地址”,
“c_comments:注释”,
“c_disabled: 客户失效标志位”}
2 产品信息
代码语言:javascript复制{“id”:ObjectXXXX,
”P_no":产品编号,
“P_name”: 产品名字,
“P_type”: 产品类型,
“P_unit”: 产品单位}
3 订单信息
代码语言:javascript复制 {“id”: ObjectXXXX,
"O_id":订单编号,
“O_date: 订单日期”,
“O_disabled”:订单退货或作废}
4 订单和产品关系表
代码语言:javascript复制 {“id”: ObjectXXXX,
"O_id": 订单编号,
“P_info“:1,
“p_info”: 2,
"p_info" 3,
O_id: 客户ID
}
以上是一个个人认为,非常,非常,非常,不好的MOGNODB 的应用程序的设计中的表设计。
问题在哪里:
1 如果一个客户在同一个时期下了多个产品,那么这个订单和产品的关系表,中的item 将无限扩大。
2 如果有大面积的退单,则会产生大量的无效的订单和产品的关系的documents
3 查询中需要至少通过三次查询才能获得完整的整体的客户订单和产品的信息
4 索引的问题(这单后面会说,不是这期)
OK 那么如何设计一个看上去还不错的MONGODB 的应用设计
1 客户信息集合
代码语言:javascript复制{"id”: ObjectXXXXX,
"customer_id: 客户ID",
“c_phone“”: [电话1:xxx,
电话2:xxx,
电话3:xxx
],
“c_name: 客户姓名”,
“c_address“: [地址1:xxxxxx,
地址2:xxxxxx ,
地址3:xxxxxx]
,
c_address_i: 有效地址位,
c_phone_i: 有效电话位
“c_comments:注释”,
“c_disabled: 客户失效标志位”}
2 产品信息
代码语言:javascript复制{“id”:ObjectXXXX,
”P_no":产品编号,
“P_name”: 产品名字,
“P_type”: 产品类型,
“P_unit”: 产品单位}
3 订单信息
代码语言:javascript复制 {“id”: ObjectXXXX,
"O_id":订单编号,
“O_date: 订单日期”,
“O_disabled”:订单退货或作废,
P_info:[1,2,3,4,5,6,6],
O_id: 客户ID}
上面的设计将订单和产品的关系表融合到了订单信息中,并且利用数组,将客户定的产品的信息放到了P_info 中,并且如果客户定了某个产品多个,可以重复产品的编号的信息方式将产品信息放到p_info 里面
这样做可以一次性的将客户的订货信息提取,但也存在问题
1 不建议修改订单信息中的数组信息,也就是p_info ,因为在MONGODB 中经常修改。
2 如果需要打印订单信息,则还需要进行关联查询,所以这个设计也有问题点
那么我们可以继续改,满足一些打印信息的要求
订单信息
代码语言:javascript复制 {“id”: ObjectXXXX,
"O_id":订单编号,
“O_date: 订单日期”,
“O_disabled”:订单退货或作废,
P_info:[
{P_id"xxx, P_name:xxx, P_unit:产品数量},
{P_id"xxx, P_name:xxx, P_unit:产品数量},
{P_id"xxx, P_name:xxx, P_unit:产品数量}
],
C_info:{C_IDxxxxx,C_name:xxxxx,C_address:xxxxx,C_phone:xxxxx}}
上面的信息的设计和之前的设计有了很大的不同,主要的是信息的完整性和细节都添加到订单信息collection 中了,好处是一次性就可以将信息提取,而不用再去 客户信息 和 产品信息 两个表中获取对应的信息,增加计算的时间,而不好的地方也有,就是这个表单行的DOCUMENT 将变得很大,而且添加索引的困难增加了。
从上面多种设计的 方式,可以感受到MONGODB 的设计中的灵活性,但反观不同的设计应该对应不同的业务场景,如果是高频的订单需要进行修改的场景,则最后一种是不适宜的。
这里围绕MONGODB的 链接还是嵌套还需要针对嵌套层数过多的问题的理解和数组中元素过多和修改以及添加的问题的理解。