大数据存储:MongoDB实战指南——常见问题解答

锁粒度与并发性能怎么样?

数据库的读写并发性能与锁的粒度息息相关,不管是读操作还是写操作开始运行时,都会请求相应的锁资源,如果请求不到,操作就会被阻塞。读操作请求的是读锁,能够与其它读操作共享,但是当写操作请求数据库时,它所申请的是写锁,具有排它性。

MongoDB在2.2之前的版本,锁的粒度是非常粗的,它会锁住整个mongod实例。这意味着当一个数据库上的写锁被请求后,对mongod实例上管理的其它数据库的操作都会被阻塞。2.2版本降低了锁的粒度,引入了单个数据库范围的锁,也就是说读写操作的锁被限定在单个数据库上,当一个数据库被锁住后,其它数据库上的操作可以继续被执行。尽管相对于全局实例范围锁,数据库范围锁性能有所提高,但是对于同一个数据库大量的并发读写还是会有性能瓶颈出现,本书介绍的2.6版本仍然是数据库范围锁,所以并发性能问题仍然存在。

在即将发布的2.8版本中将会引入基于文档级别的锁,相当于关系数据库中的行级锁,锁的粒度更进一步变细。因此当一个写操作发生时,只有涉及到的文档会被锁住,如果写操作涉及到整个集合,那么将会产生一个集合锁来锁住整个集合,同理,如果写操作涉及多个数据库,仍然会有一个全局实例锁产生。

是否支持ACID事务?

经典ACID事务有四种特性即Atomicity,Consistency,Isolation和Durability。其中原子性保证了事务的操作要么全部成功,要么失败后进行回滚,使数据库回到原来的状态;一致性保证了事务在开始之前和结束以后,数据库中的数据完全符合所设置的各种约束和规则;隔离性保证了多个事务操作同一数据时,相互之间按照约定的隔离级别访问和修改相同的数据,不同的关系数据库会有不同的默认隔离级别;持久性保证了事务结束后,事务所涉及到的数据变化被持久的保存在数据库中,即使断电重启数据也会存在,并且是完整的。

MongoDB并不支持ACID事务特性,但是MongoDB支持在单个文档(记录)上的原子操作,设计数据模型时,通过文档嵌套的方式也能解决关系数据库中ACID事务特性所要求的大多数问题。例如,在关系数据库中多条相关联的记录存储,可以通过嵌套数组或文档的形式作为一条记录保存在MongoDB中,这样相当于实现了原子性。

内存映射文件如何工作?

内存映射文件是指调用操作系统的底层函数mmap( )将磁盘上的文件映射到一个操作系统虚拟地址空间中,只是地址与地址之间建立一个映射关系,实际数据还在物理磁盘上,不在内存中。内存映射文件是MongoDB存储引擎管理数据的核心方法,一旦完成映射,MongoDB对这部分文件数据的访问,就好像在内存中访问一样,通过一个地址就能直接访问了。

MongoDB利用内存映射文件的方式管理和操作所有的数据,如果数据没有被映射到内存中,则不能被访问。对已经完成内存映射的文件进行访问时,如果发现数据不在内存中,则会发生缺页错误,操作系统将通过映射好的地址关系找到在磁盘上的数据文件并将它加载到内存中。

服务器的内存多大合适?

MongoDB采用内存映射文件的机制来加快数据的读写速度,使用操作系统自带的虚拟内存管理器来管理内存,理论上MongoDB会占用服务器上所有的空闲内存,但实情况下的数据文件总是远大于物理内存的大小,况且可能会有新的进程运行在服务器上,这也需要占用内存,因此全部将数据文件映射到虚拟地址空间是不可能的。

MongoDB在实际运行过程中,会有一部分经常被客户端访问的数据和索引,称之为活跃“工作集”,如果能保证这部分“工作集”的数据常驻内存,系统性能将比较高效,否则,大量的磁盘I/O操作将会发生,降低系统性能。因此,服务器的内存大小最少大于“工作集”数据的大小比较合适。当服务器的空闲内存不足时,操作系统会根据内存管理算法将最近最少使用的数据从内存中移除,腾出空间给有需要的数据。

不支持join查询怎么办?

Join查询是关系数据库中一种经典的多表联合查询的方式,但MongoDB并不支持这种操作。如果你想在多个Collection中检索数据,那么你必须做多次的查询,如果觉得手动做的查询太多了,你可以重设计你的数据模型来减少整体查询的数量。

MongoDB中的文档可以是任何类型,我们可以轻易的对数据结构进行重构,这样就可以让它始终和应用程序保持一致,用一次查询就能满足需求。切记,避免用关系数据库的思维来设计MongoDB的表结构。

复制集提供了数据冗余功能为什么还要用Journaling?

Journaling是特别有用的当数据库遇到突然断电等异常情况时,尤其是对只有单个节点的数据中心,它能使数据库快速的恢复起来。Journaling类似关系数据库MySql中的事务日志功能,它与复制集的冗余功能不一样,后者更强调的是一种数据备份,而journaling偏向与数据库灾难恢复。关于Journaling的工作机制,请参考本书第5章。

什么时候该用GridFS?

GridFS本质上还是基于MongoDB的collection和document等核心技术的,只是它会将大于16MB的文件分割成许多小文件,然后将这些小文件存储在相应的collection中。如果需要存储的单个文件的大小超过16MB,就应该用MongoDB自带的GridFS系统,有的时候将大文件存储在MongoDB的GridFS中比直接存在操作系统的文件系统中要更加高效;如果需要存储的文件数超过了操作系统中一个目录下允许包含的文件总数,则可以用GridFS系统;当你想要将你的文件分布式部署在各个数据中心并提供冗余保护时,可以用GridFS系统。

此外,当你的所有文件大小都小于16MB时,不要用GridFS系统,因为将每个文件保存在一个document中往往会更高效。

分片集群如何分发查询请求的?

如何分发查询请求在一个分片集群上取决于集群的配置和查询语句本身。例如一个被分片的集合,有两个字段:user_id和user_name,其中user_id为分片的片键,当一个查询语句利用user_id作为过滤条件返回结果时,mongos路由进程将先利用配置服务器上的元信息解析出需要从哪个或哪几个片上获取数据,然后直接将查询请求定向到具体的片上,最后返回结果给客户端;当一个查询利用user_id作为过滤条件同时要求对查询结果进行排序时,mongos先将查询请求路由到具体的片上,并在各个片上完成排序,最后mongos将合并排序结果返回给客户端;当一个查询利用user_name作为过滤条件时,则查询请求将被定向到所有的片上,mongos合并各片上查询结果返回给客户端。

复制集从故障中自动恢复要多久?

复制集从故障中恢复并选择一个新的primary节点大约需1分钟的时间。通常其它成员节点将会花大约10到30秒的时间发送心跳包到发生故障的primary节点,判断primary节点发生了故障;接着触发一个选举,大约再花10到30秒的时间选举出新的primary节点,在选举的过程中,复制集不能响应写操作请求。如果配置了允许客户端从其它secondary节点读取,则在选举的过程中复制集能够响应客户端的读请求。

为什么磁盘分配的空间大于数据库中数据大小?

这主要有以下几方面的原因:

(1)MongoDB存储引擎采取预分配数据文件机制,这样能够减少文件系统的碎片。MongoDB将第一个数据文件命名为<数据库名>.0,第二个命名为<数据库名>.1,依次类推下去。第一个文件的大小为64MB,第二个为128MB,后面分配的文件大小都是前面一个的两倍,这样会导致最后分配的数据文件可能会有部分空间是没有保存数据库数据的,因此会浪费一点磁盘空间。

(2)如果mongod实例是复制集的成员,则会在数据目录下产生一个oplog.rs文件,文件中包含用来同步数据的操作日志,类似于MySql中的二进制日志,这个文件的大小约占5%的磁盘空间,它是可以被重复使用的。

(3)数据目录中还会包含journal文件,在写作的更改刷新到数据文件之前,用来保存写操作日志,实现数据库的恢复功能,类似于MySql中的redo日志。

(4)当删除一些集合或文档时,MongoDB存储引擎会重用这部分数据空间,但是不会把这部分空间返还给操作系统,除非执行repairDatabase命令。

所以mongod实例占用的磁盘空间大小总是大于数据库中数据文件的大小。

大数据存储:MongoDB实战指南

大数据存储:MongoDB实战指南——常见问题解答

上一篇:分布式大数据多维分析(OLAP)引擎Apache Kylin安装配置及使用示例【转】


下一篇:从 RAID 到 Hadoop Hdfs 『大数据存储的进化史』