mongoDB最佳实践

大概任何事物都有优缺点,尤其是在不同场景之下。所以,我们要做的是让这些工具在最佳的场景下被使用,发挥出它们的特长,尽量避免不擅长的地方,所谓扬长避短。

mongoDB作为一种NoSql数据库,有很多优点,也有很多缺点。那么,MongoDB在应该在什么场景下使用呢?怎样使用它才能发挥出它的最大威力?

规范化VS反规范化(Normalization versus Denormalization)

  • 规范化:即把数据根据不同属性拆分开来,以便权责清晰,不耦合,通过部分字段进行关联(Refrence)。这样相关联数据更新比较方便(只需要更改一个表中的某个文档),但可能需要多次查询才能得到所有数据,读效率低。适用于写频繁的场景。
  • 反规范化:把所有相关联数据作为子文档的形式(Embedding),放在一个大文档里面,这样可以一次查询就可以得到所有信息,但是要更新子文档数据的话,可能涉及多个文档的修改,更新复杂度高,适用于读频繁的场景。
适用场景

基本原则

Embedding is Better Refrence is Better
文档很小 文档很大
不经常变 经常变
最终一致即可 数据一致实时性要求高
文档数量很少 文档数量很大
经常需要二次查询子文档 子文档经常需要从查询结果中排除
追求更快的读 追求更快的写

如果是子文档形式,来确保子文档内容需要更新时,所有文档更新成功,这时候需要有cronjob来保证

当子文档变化不是那么频繁,但也可能会改变的情况下,可以考虑子文档只存储相关id和关键信息(需要检索的字段),详细子文档内容缓存到Redis,查询时,结合Redis缓存补充完整子文档。

MongoDB作为非关系型数据库,Reference方式的查询是不擅长的,因此会优先考虑Embedding方式,提高查询速度。但Embedding方式也有自身的问题,所以需要结合上述规则进行综合判断,有舍有得。结合Redis等缓存方式,也是个不错的选择。
当然,如果业务逻辑大多需要Reference方式,建议就不要使用MongoDB了,用关系型数据库好了。

针对Embedding方式一些好的建议
  • 针对“名人效应”,少部分文档的子文档特别大,比如某明星的关注者特别多,这种可以采取“续表”的方式,主表只存储少部分关注者id,其它的关注者存放在别的表。
  • 读写效率之间的权衡主要看量,重不重要倒是其次,大多数情况下,读的量会远大于写
  • 针对会变大的文档,变大的时候,文档会被移动,不然存储不下,移动耗时多,这个时候,如果可以预测文档变大的幅度,可以在插入文档的时候,加适量的空白数据,以避免文档变大的时候被移动。注意,空白数据需要放在文档最后。
{
	_id: ObjectId(),
	restaurant: "Le cirque",
	review: "Hamburgers were overpriced",
	userId: ObjectId(),
	tags: [],
	garbage: "----------------------------"
	// 这个就是空白数据,事先替tags占位
}
规划集合和数据库
  • 通常,具有相似schema的数据放在一个集合
  • 经常需要一起聚合的数据,即使不是那么相似,也最好放一个集合
  • 每个DB都有自己的数据存储文件,不同的DB可以存放在不同的磁盘
  • DB的区分通常按照数据的重要性以及数据的读写速度要求来划分,比如用户信息,非常重要,需要存放在RAID10的磁盘系统,而log数据不重要,就可以直接存放着RAID0系统
数据一致性

MongoDB服务器会给每个连接创建一个队列,这个连接的所有请求会依次加入队列,并依次处理。因此,单个连接,是可以保持数据一致性的。
但,如果开了多个连接,同时读写,那么无法保证数据一致性。比如两个线程都去读取某个文档,不存在则插入,就可能插入2条。

问题: 开一个连接,并发随机读取某个文档,总共读取10000次,所花时间比开多个连接更少,,这是为啥?

Schema变更

随着业务的发展,某些集合的schema是有可能发生变化的,甚至是有冲突的变化,这时候该怎么办呢?

  • 给schema加上版本号,查询的时候根据不同版本号进行不同的处理,这样不需要处理旧数据,也可以兼容旧数据。
  • 将旧数据全部按照新Schema修改一遍,如果旧数据量很大,修改数据会非常耗时,而且需要保证每条旧数据都被更新成功了。多条数据的更新并不是原子操作,一旦中途更新失败,那就会出现部分成功,部分没更新的情况。不推荐这样处理
什么情况下不用MongoDB
  • 需要进行大量的join操作
  • 系统工具不支持mongodb

你可能感兴趣的:(Node,mongodb)