MongoDB的模型设计方法论
虽然说mongoDB不像传统的关系型数据库,没有固定的schema,但是在项目中实际运用时,还是需要进行一定程度的模型设计,一般来说模型设计分为三步,即概念模型(描述系统要管理的对象),逻辑模型(列出所有对象的所有属性以及对象之间的关系)和物理模型(数据库表的物理结构),mongoDB的模型设计只需要到逻辑模型就可以了。
在进行mongoDB模型设计的时候可以按照三步来,首先是基础设计,将即逻辑模型落地成一个基本的文档模型;然后是第二步工况细化,根据应用中的实际使用来进行模型的优化,比如一个文档中的一个数组字段存的值是通用的,并且需要经常进行变更,这样一次变更就需要对多个文档进行变更(比如文档中保存多对多的分组信息,而分组名变化时该分组下的所有文档都需要更新),这时候就可以把数组中存储的信息改为引用以提高更新效率;第三步就是模式套用,即套用一些已有的经典设计模式以提高模型查询效率。
经典模式举例
1.一个大文档,有很多字段,每个字段都需要索引,导致索引很多写入效率缓慢。此时可以用列转行的模式。比如一个商品要记录每个地区的价格,开始的结构可能是:
{price_sc:123,price_hb:222,price_gz:342}
列转行后的模型变为:
{price_info:[{area:"sc",price:123},{area:"hb",price:222},{area:"gz",price:342}]}
2.灵活模型由于可以每个应用版本自由得增减文档字段,为了更好进行管理就可以增加一个版本字段,以识别每个文档所处的版本
3.对于需要频繁写入但又对数据准确性要求不高的数据可以使用近似计算来提高效率,比如如果需要进行某个按钮点击次数的统计但又并不需要非常精确的数据,就可以使用if(rand(10)=0) count+=10这样的方式进行近似计算
4.如果有需要频繁进行聚合查询的数据,而由于数据量大聚合操作很耗时,就可以进行预聚合的模式,即保存一个聚合字段,每次更新数据的时候也更新聚合字段,这样在查询时就不需要再进行聚合操作了。当然这样也会增加写入数据的消耗,所以适用聚合查询很多且确实是主要性能瓶颈的情况。
MongoDB写事务
mongo的写事务是通过配置writeConcern参数来实现的,writeConcern有几个可以配置的值:0,1-集群最大节点数或者majority,如果配置为0意思是客户端只发起写操作并不关心写操作是否成功,这种配置下数据的安全性最低,不推荐配置成该值,如果配置为1-集群最大节点数(n)的话,就是至少保证有n个节点写入成功才返回客户端成功,意思是如果配置成集群节点数的话,一次写入会在数据同步到集群所有节点上之后才算成功,这样固然数据是最安全的,但是会大大影响写入的效率,所以一般也不建议这样使用。writeConcern最常见的配置就是majority,意为大多数节点写入成功即返回成功,比如集群中有5个节点,那么有3个节点写入成功即会返回客户端成功写入,这种配置兼顾了安全性和写入性能的保证。
MongoDB读事务
MongoDB的读事务主要涉及到两个参数,第一个是readPreference,另一个是readConcern
1.readPreference
readPreference的配置主要作用是配置读操作时更加倾向读取的节点。可能取值有primary,primaryPreferred,secondary,secondaryPreferred,nearest。primary为只读取主节点;primaryPreferred为优先读取主节点,主节点不可用时读取子节点;secondary为只读取子节点;secondaryPreferred为优先读取子节点,子节点不可用时读取主节点;nearest为读取最近的节点,主要用于数据跨区域存储。
2.readConcern
readConcern的主要作用就是用于读取时判断节点上哪些数据是可读的,类似于关系型数据库中的隔离级别,可选值有available,local,majoyrity,linearizable和snapshot。available和local在复制集中的作用大致上都是一样的,即都是读属于当前分片的数据;majoyrity意思是读已经被大多数分片保存到的数据,这个类似于关系型数据库隔离级别中的读已提交,在读写分离的场景中可能会遇到往主节点中完成了写入数据之后在子节点中无法马上找到该数据,这个时候可以通过配置writeConcern和readConcern都为majoyrity来解决这个问题;linearizable和majoyrity的不同是它会保证操作的线性顺序,及写操作之后的读一定能读到写操作写入的数据,比如有这样的情况,一个主节点在集群中与其他节点失联,而其他节点此时已经重新选出了一个主节点,但失联的主节点并没有意识到出现了问题依然在接受请求,则此时在新的主节点中写入的数据在失联的主节点中是查询不到的,而如果配置了linearizable,读操作会在保证读的节点与其他节点的连接性。snapshot则是读取最近的一次数据库快照,类似于可重复读。