MongoDB–数据的组织形式和优化数据
一:范式化和反范式化
范式化:将数据分散到多个不同的集合,不同集合之间可以相互引用数据。如果要修改数据,只需修改保存这块数据的文档就行。但是MongoDB没有连接(join)工具,所以在不同集合之间执行连接查询需要进行多次查询。
反范式化:将每个文档所需的数据都嵌入在文档内部。每个文档都有自己的数据副本,而不是所有文档共同引用一个数据副本。但是如果数据发生变化,那么所有相关文档都需要进行更新。
范式化能够提高数据写入速度,反范式化能够提高数据读取速度。
一般来说有四种数据组织方式:
1.学生表,课程表,学生和课程之间的关系表;由于MongoDB不能join,所以如果要查,学生选修的课程,就需要三次查询;(范式化)
2.学生表(有一个字段包含了课程表的引用),课程表;只需要两次查询(半反范式化)
3.学生表中内嵌课程表,只需要一次查询,但是会占用更多的存储空间,而且数据同步困难(完全反范式化)
4.学生表中不仅内嵌课程表还还要课程表的引用,这样内嵌的文档可以随着需求修改
适合引用 | 适合内嵌 |
---|---|
子文档较大 | 子文档较小 |
数据经常改变 | 数据不会定期改变 |
中间阶段数据必须一致 | 最终数据一致 |
快速写入 | 快速读取 |
二:优化数据操作
定位到读写瓶颈。针对性优化,例如合理使用索引,优化写操作尽量减少使用索引,优化读操作要正确使用索引和将所需信息放在单个文档。
1. 优化文档增长
更新数据的时候明确数据增长的程度,是否会导致文件体积变大。可为文档预留足够的增长空间,避免文档移动。
例如有一个文档中有一个字段names,是一个数组,会增长数据,我们可以手动为文档留出足够的空间,在names字段后面再加一个fill字段,可以在第一次插入文档或者upsert时使用$setOnInsert创建这个字段
db.mong.update({},{$setOnInsert:{"fill":"...................................."}},{true},{true})
在更新文档字段names的时候,使用"$unset"移除fill字段,这样names就有了空白空间用于增长
db.mong.update({"_id":1},{"$push":{"names":{"$each":["zs","li","we"]}},"$unset":{"fill":1}})
2. 删除旧数据
最常见的就是使用TTL索引和固定集合
三:数据库和集合的设计
- 相近的文档应该放在相同的集合中;
- 由于MongoDB不允许跨集合聚合数据,所以需要聚合的数据要放在相同集合中;
- 可以让不同的数据库位于不同的磁盘分卷中,经常访问的可以放在SSD中;
- 无法将数据从一个数据库转移到另一个数据库中
四:一致性锁
当进行读操作的时候会加读锁,这个时候其他读操作可以也获得读锁。但是不能或者写锁。
当进行写操作的时候会加写锁,这个时候不能进行其他的读操作和写操作。
在3.0之后的版本,WiredTiger提供了文档(不是集合)级别的锁
MongoDB内部处理请求的机制:服务器为每一个数据库连接维护一个请求队列,客户端每次发来的请求都会添加到请求队列的末尾。每个请求队列对应的是一个连接,如果有多个连接同时读和写就会存在了一致性的问题。