MongoDB 初见指南
技术若只如初见,那么还会踩坑么?
在系统引入 MongoDB 也有几年了,一开始是因为 MySQL 中有单表记录增长太快(每天几千万条吧)容易拖慢 MySQL 的主从复制。而这类数据增长迅速的流水表,对数据一致性也没那么高要求,而且业务上也不需要关联查询它,就考虑分出去。为什么是 MongoDB?刚巧赶上公司 DBA 团队引入了这个数据库,有人帮助运维,对业务团队就成了一个自然的选择。不过对于任何技术产品你如果要把它用在生产环境上,最好确定对它的架构和运作机理有个全面的理解。
形态
MongoDB 是一种 NoSQL 数据库,它在数据存储的形态上和 MySQL 这类关系数据库有本质区别。MongoDB 存储的基本对象是 Document,所以我们把它称为一种文档数据库,而文档的集合则组成了 Collection。与 SQL 的概念类比,Collection 对应于 Table 而 Document 对应于 Row。Document 使用一种 BSON(Binary JSON)结构来表达,JSON 大家都熟悉,像下面这样。
Document 在内部是如何存储的?每个 Document 被保存在一个 Record 中。Record 相当于 MongoDB 内部分配的一块空间,除了保存 Document 的内容可能还会预留一些填充的额外空间。对于写入后的 Document 如果还会更新,可能导致 Document 长度增加,就可以利用上额外的填充空间来。若业务对于写入后的 Document 不会再更新或删除(像监控日志、流水记录等),可以指定无填充的 Record 分配策略,更节省空间。
了解了 Document 形态的基础上,我们再说点针对 Document 的访问操作。新的 WiredTiger 存储引擎提供了 Document 级别的并发操作,所以并发性能有所改善。另外 MongoDB 仅对单一 Document 提供事务的 ACID 保障,如果一个操作涉及多个 Document 则不能保证事务特性。不同的业务数据对事务一致性的要求不同,所以应用开发者需要知道将数据放在不同的 Document 中写入时在一致性方面可能的影响。详细的操作 API 直接看官方文档,不赘述了。
安全
这里的安全指的数据安全,安全就是说数据被安全的保存好了,不会丢失。关于 MongoDB 数据安全在早期的版本(1.x)引发了很多争论。(可以看参考[2])
安全和效率其实是相互制约的,越安全则效率越低,越高效则越不安全。MongoDB 的设计场景考虑的是应对大量的数据写入和查询,而数据的重要性相对没那么高。所以 MongoDB 的默认设置在安全和效率之间,更偏向效率。
我们先看下一个 Document 被写入到 MongoDB 后它内部的处理方式。MongoDB 的 API 提供了不同安全级别的写入选项来让使用方根据其数据性质灵活的选择。
Write To Buffer Without ACK
这个模式下 MongoDB 是不确认写请求的,Client 端调用驱动写入后若没有网络错误就认为成功,实际到底写入成功没有是不确定的。即使网络没有问题,数据到达 MongoDB 后它先保存在内存 Buffer 中,再异步写入 Journaling 日志,这中间有 100ms(默认值) 的落盘(写入磁盘)时间窗口。一般数据库的设计都是先写 Journaling 的流水日志,随后异步再写真正的数据文件到磁盘,这个随后可能就比较长了,MongoDB 是 60 秒或者 Journaling 日志达到 2G。
Write To Buffer With ACK
这个比上一种模式稍微好一点,MongoDB 收到写入请求,先写入内存 Buffer 后回发 Ack 确认。Client 端能确保 MongoDB 收到了写入数据,但依然有短暂的 Journaling 日志落盘时差导致潜在的数据丢失可能。
Write To Journaling With ACK
这个模式确保至少写入 Journaling 日志后才回发 Ack 确认,Client 端能确保数据至少写入磁盘了,安全性较高。
Write To Replica Buffer With ACK
这个模式是针对多副本集的,为了提升数据安全性,除了及时写入磁盘也可以通过写多个副本来提升。在这个模式下,数据至少写入 2 个副本的内存 Buffer 中才回发 Ack 确认。虽然都在内存 Buffer 中,但两个实例在落盘短暂的 100ms 时差中同时故障的概率很低,所以安全性有所提升。
明白了不同的写入模式选项,我们才能更好的真对数据的性质选择合适的安全级别。后面效率一节我们再分析不同写入模式下的效率差异。
容量
在考虑 MongoDB 整体的存储容量前,先考虑作为基本单元的 Document 的容量。Document 这种 JSON 形态天生会带来数据存储冗余,主要是 field 属性每个 Document 都会保存一遍。目前 3.2 版本的 MongoDB 已经将新的 WiredTiger 作为默认存储引擎,它提供了压缩功能,有两种压缩形式:
- Snappy 默认压缩算法,在压缩率和 CPU 开销之间取得平衡。
- Zlib 更高的压缩率,但也带来更高的 CPU 开销。
而每个 Document 依然有最大容量限制,不能无限增长下去,这个限制目前是 16MB。那么我要存大于 16MB 的文件怎么办,MongoDB 提供了 GridFS 来存储超过 16MB 大小的文件。如下图所示,一个大文件被拆分成小的 File Chunk,每个 Chunk 大小 255KB,并存放在一个 Document 中。GridFS 使用了 2 个 Collection 来分别存放文件 Chunk 和文件元数据。
单机的容量总是受限于磁盘大小,而 MongoDB 解决方案依然是分片化。是用更多的机器来提供更大的容量,分片集群采用代理模式(《Redis 集群的合纵与连横》一文中写过这类模式),如下图。
而每个分片上的数据又以 Chunk 的形式组织(类似于 Redis Cluster 的 Slot 概念),以便于集群内部的数据迁移和再平衡。比较容易混淆的是这里的 Chunk 不是前面 GridFS 里提到的 Chunk,它们的关系大概如下图(吐槽下,干嘛要用同名的术语来表达完全不同的概念)。
支持水平扩展和数据再平衡功能的 MongoDB Cluster 基本上数据容量就不再是个问题了。
效率
前面「安全」一节列举了不同的写入模式,我们看下在这些不同模式下写入的效率如何。由于官方没有提供基准性能测试数据,下面的数据来自参考文献[5]一个从 2009 年开始使用 MongoDB 的专业技术公司博客分享的写入基准测试数据。我这里根据数据结果做一些分析总结,下面是测试结果数据的表格和图形展示。
- w=0, Write To Buffer Without ACK
- w=1, Write To Buffer With ACK
- j=1, Write To Journaling With ACK
- w=2, Write To Replica Buffer With ACK
测试类型多了一项将 Journaling 日志放在 SSD 和机械硬盘上的差异,这让我们可以直观的感受 SSD 和机械硬盘在顺序写情况下的性能差异。对于机械硬盘最大的性能制约是在磁头移动,所以 MongoDB 官方文档也建议将 Journaling 日志和数据文件放在不同的磁盘上。保证顺序写 Journaling 日志的磁头不会被随机写数据文件影响,而数据文件的写入是通过内存 buffer 缓冲的一个异步过程,对交互性能延迟的影响不大。
根据测试结果数据看,有无 Ack 之间响应延时相差一倍,基本就是多了一个网络传输的延时等待时间。开启 Journaling 保证及时落盘,不论是 SSD 还是机械硬盘,这个延时都上升了 2 个数量级,翻了百倍,而 SSD 的顺序写比机械硬盘平均快 3 倍。而写双副本的平均延时比我预期高了不少,应该说延时的波动很大,不像写磁盘延时最小、最大和平均的值非常接近。理论上写双副本不落盘的情况延时只应该比单一情况多一倍的网络开销外加部份程序开销,而实际测试数据显示远远高于预期而且延时波动范围大了很多。这种模式下 MongoDB 延时表现波动范围太大,不够稳定,具体到底是实现上的缺陷还是测试不够准确,就不得而知。而且当时测试的版本是 2.4.1 不知道最新的 3.2 版本如何,如果采用这类写模式,可模拟自己生产环境实测得出结论。
至于读取性能是没法做基准测试了,不同的文档模型,选择不同的查询条件,性能都可能不同。虽然 MongoDB 是 Schemaless 的,但不意味着不需要对文档的 Schema 进行设计,不同的 Schema 设计对性能的影响还是很大的。
总结
面临一个新的技术产品或系统,「形态」是针对这个产品或系统最独特部分的描述,属于核心模型。而「安全」、「容量」、「效率」三个核心维度全面反应了一个技术产品或系统的不同设计和实现考虑,可类于比机械设计中的「三视图」。对于初次面对一个新的技术产品或系统,这是一个适合的切入点来帮助做初步的技术决策,然后跟着进一步的实践测试来验证思考和理解,这样才能更好的理解和用好现有技术,做一个合格的技术拿来主义者。
参考
[1] MongoDB Doc. MongoDB Manual
[2] MongoDB White Paper. MongoDB Architecture Guide
[3] 陈皓. 千万别用MongoDB?真的吗?. 2011.11
[4] David Mytton. Does everyone hate MongoDB?. 2012.09
[5] David Mytton. MongoDB Benchmarks. 2012.08
[6] David Mytton. MongoDB Schema Design Pitfalls. 2013.02
MongoDB 初见指南的更多相关文章
- 大数据存储:MongoDB实战指南——常见问题解答
锁粒度与并发性能怎么样? 数据库的读写并发性能与锁的粒度息息相关,不管是读操作还是写操作开始运行时,都会请求相应的锁资源,如果请求不到,操作就会被阻塞.读操作请求的是读锁,能够与其它读操作共享,但是当 ...
- mongoDB权威指南学习笔记
//mongoDB第1-3章节添加,修改,修改器的笔记: //备注:和MySQL查询一样,时刻想着优化查询数据的时间和性能 //db.help() //数据库帮助信息 //db.blog.help() ...
- 《MongoDB 权威指南》 学习总结
书目 http://baike.baidu.com/link?url=DVJuvrT4ssbM8V1pKcoRx5H1CwRRCynC6_Ix6YWIrr414opislflpPuY9hRDO4g ...
- mongoDB 入门指南、示例
一.准备工作 1. 下载mongoDB 下载地址:http://www.mongodb.org/downloads 选择合适你的版本 相关文档:http://www.mongodb.org/displ ...
- MongoDB实战指南(七):MongoDB复制集之复制集工作机制
http://www.cnblogs.com/longshiyVip/p/5097336.html 概述了复制集,整体上对复制集有了个概念,但是复制集最重要的功能之——自动故障转移是怎么实现的呢?数据 ...
- MongoDB实战指南(六):MongoDB复制集之复制集概述
1. 复制集概述 数据库总是会遇到各种失败的场景,如网络连接断开.断电等,尽管journaling日志功能也提供了数据恢复的功能,但journaling通常是针对单个节点来说的,只能保证单节点数据的一 ...
- MongoDB实战指南(五):MongoDB中的聚集分析
聚集操作是对数据进行分析的有效手段.MongoDB主要提供了三种对数据进行分析计算的方式:管道模式聚集分析,MapReduce聚集分析,简单函数和命令的聚集分析. 1. 管道模式进行聚集 这里所说的管 ...
- MongoDB实战指南(四):MongoDB的Journaling日志功能
mongoDB的Journaling日志功能与常见的log日志是不一样的,mongoDB也有log日志,它只是简单记录了数据库在服务器上的启动信息.慢查询记录.数据库异常信息.客户端与数据库服务器连接 ...
- MongoDB实战指南(三):MongoDB的锁机制
与关系数据库一样,MongoDB也是通过锁机制来保证数据的完整性和一致性,MongoDB利用读写锁来支持并发操作,读锁可以共享写锁具有排他性.当一个读锁存在时,其他读操作也可以用这个读锁:但当一个写锁 ...
随机推荐
- 网络请求 __ NSURLSession
首先配置into.plist文件 1. 添加 App Transport Security Settings , Type栏自动变为Dictionary 2. 点击左边箭头,使之向下,点击右边加号,添 ...
- PHP中的header()函数作用
PHP 中 header()函数的作用是给客户端发送头信息. 什么是头信息?这里只作简单解释,详细的自己看http协议.在 HTTP协议中,服务器端的回答(response)内容包括两部分:头信息(h ...
- Knockout.js随手记(6)
实时反映对象属性的变化 在前一篇博客中我们使用了如下代码去新增user对象,即push方法: $("#btnAddUser").click(function () { vm.use ...
- poj上的dp专题
更新中... http://poj.org/problem?id=1037 dp[i][j][0]表示序列长度为i,以j开始并且前两位下降的合法序列数目; dp[i][j][1]表示序列长度为i, 以 ...
- HBase基本shell命令
HBase基本shell命令 以下shell命令都是经过测试,正常展示,若有不足,还望指点! 1.创建表 create ‘表名称’,‘列族名称1’,‘列族名称1’create 'test_M_01', ...
- 【ajax 提交表单】多种方式的注意事项
在业务中,可能因为表单内容过于庞大,字段过于繁杂,如果人为去拼接的话 ,需要耗费大量的时间和精力,与此同时,代码看上去也是冗余不堪. 所以,提交表单的时候如果能整个表单数据整体提交,那是非常开心的事情 ...
- 【Hawk】高级教程——post参数采集万方医学网论文
目标——万方医学网论文列表 http://med.wanfangdata.com.cn/Author/General/A000000001 和普通网页不一样的地方在于点击下一页的时候,URL没有发生变 ...
- 让mysql不能为空的字段为空时也能插入
第一步: 在mysql安装目录中找到my.ini将: #sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTIT ...
- js模拟抛出球运动
js练手之模拟水平抛球运动 -匀加速运动 -匀减速运动 模拟运动有些基本的思路,当前所在点的坐标,元素的长宽是多少,向右/向下运动x/y增加,向上/向左运动x/y减少,运动的路程是多少,用什么方程进行 ...
- 用PS如何把图片调出时尚杂志色
摘自:http://www.3lian.com/edu/2013/07-22/83061.html 01:打开图片,执行调整图层-色彩平衡;调整图层的标记-红色方框内图标. 02:色彩平衡-设置-点选 ...