MongoDB丢数据问题的分析
坊间有很多传说MongoDB会丢数据。特别是最近有一个InfoQ翻译的Sven的一篇水文(为什么叫做水文?因为里面并没有他自己的原创,只是搜罗了一些网上的博客,炒了些冷饭吃),其中又提到了丢数据的事情。大家知道作为一个数据库来说,数据的持久性基本上是数据库的最低要求了。如果MongoDB真的有那么糟糕的数据安全问题,它早就在技术选择众多的今天被无情地淘汰掉了。那么真相到底如何呢?
实事求是地来说,MongoDB确实在其发展的过程中,有一些数据持久化的问题没有处理好,特别是一些默认值的选定上。大部分用户会拿来就用,直到遇到问题之后才发现他们应该在开始的时候做一些必要的配置。但是,所有这些已经被发现的问题也好,默认设置也好,已经在 MongoDB 2.6以后得到了妥善的解决。我可以负责任地告诉你,你看到的数据安全问题,基本上都是2.4或者之前版本的问题或者是用户配置的问题。接下来我们来仔细分析一下MongoDB的数据安全机制,通过这个分析来更好地理解为什么有丢数据问题的说法,以及如何来正确的配置MongoDB来保证数据的安全。
MongoDB的数据安全包括以下几个概念:
- 恢复日志(Journal)
- 写关注(Write Concern)
恢复日志
在MySQL, PostgreSQL,Oracle等关系型数据库里都有一个Write Ahead Log(Redo Log)的机制用来解决因为系统掉电或者崩溃时导致内存数据丢失问题。MongoDB 的journal就是实现这个目的的一种WAL日志。在MongoDB 2.0之前,Journal没有被支持或者不是一个默认开的选项。所以当你进行写入操作时。在没有Journal的情况下,MongoDB是这样保存数据的:
简单来说,数据在写入内存之后即刻返回给应用程序。而数据刷盘动作则在后台由操作系统来进行。MongoDB会每隔60秒强制把数据刷到磁盘上。那么大家可以想象得到,如果这个时候发生了系统崩溃或者掉电,那么未刷盘的数据就会彻底丢失了。如果大家看到的博客是2011年左右的,那基本上是碰到了这种情况。
自从2.0开始,MongoDB已经把Journal日志设为默认开启。
在上图的情况下,MongoDB会先把数据更新写入到Journal Buffer里面然后再更新内存数据,然后再返回给应用端。Journal会以100ms的间隔批量刷到盘上。这样的情况下,即使出现断电数据尚未保存到文件,由于有Journal文件的存在,MongoDB会自动根据Journal里面的操作历史记录来对数据文件重新进行追加。
有细心的同学可能注意到,Journal文件是100ms 刷盘一次。那么要是系统掉电正好发生在上一次刷journal的50ms之后呢?这个时候,我们就可以来看一下MongoDB持久化的下一个概念了:写关注
写关注(Write Concern)
写关注(或翻译为写安全机制)是MongoDB特有的一个功能。它可以让你灵活地指定你写操作的持久化设定。这是一个在性能和可靠性之间的一个权衡。 写关注有以下几个级别:
{w: 0} Unacknowledged
Unacknowledged指的是对每一个写入操作,MongoDB并不会返回一个是否成功的状态值。这个级别是写入性能最好但也是最不安全的级别。比如说,你试图插入一个违反了唯一性的文档(重复的身份证号),那么MongoDB会拒绝写入并报错。但是由于驱动端并没有在乎你的报错,应用程序还满心欢喜以为一切都没问题,下回再来查询那条数据的时候就会出现数据缺失的情况。
有不少时候MongoDB用来保存一些监控和程序日志数据,这个时候如果你有1、2条数据丢失,是不会对应用程序有什么影响的。基于这些MongoDB早些时候不成熟考量,MongoDB在2.2之前的默认设置就是 {w:0}。这是个让MongoDB 悔恨无比的选择,因为这个是很多人觉得MongoDB数据不安全的根本原因。
在MongoDB 2.4,这个设置已经被改为下面的 {w:1}
{w: 1} Acknowledged
Acknowledged 的意思就是对每一个写入MongoDB都会确认操作的完成状态,不管是成功还是失败。当然这个确认只是基于主节点的内存写入。但就是这个级别,可以侦测到重复主键, 网络错误,系统故障或者是无效数据等错误。
自2.4版本起,MongoDB的默认写安全设置就是 {w:1} Acknowledged。在这种情况下,出现因为系统故障掉电原因而导致的数据丢失只会是我们早些提到的日志没有及时刷盘的情况。如果你不能接受因为系统崩溃而引起的可能的100ms的数据损失,那么你可以选用下一个级别: {j:1} Journaled
{j:1} Journaled
使用这种方式意味着每一次的写操作会在MongoDB实实在在的把journal落盘以后才会返回。当然这并不意味着每一个写操作就等于一个IO。MongoDB并不会对每一个操作都立即刷盘,而是会等最多30ms,把30ms内的写操作集中到一起,采用顺序追加的方式写入到盘里。在这30ms内客户端线程会处于等待状态。这样对于单个操作的总体响应时间将有所延长,但对于高并发的场景,综合下来平均吞吐能力和响应时间不会有太大的影响。特别是你能给journal部署一个对顺序写有优化的IO带宽足够的专门的存储系统的话,这个对性能的影响可以降到最低。
那么使用 {j:1} 是不是就100% 安全了呢?如果是单机版本,这个基本上就是可以确保的了(除非硬盘损坏)。可是在复制集的场景下,我们还需要来考虑一种更高的级别: {w: “majority”}
{w: “majority”} 写到多数节点
MongoDB 的默认部署是至少3个节点的复制集(Replicaset)。使用复制集的好处很多,最关键的就是提高系统的高可用性。另外一个好处就是提供数据的持久性。在复制集下哪怕你的整个主机连内存带硬盘坏掉,你的数据还是健康的存在在第二台或者第N台从节点上。但是复制集作为一种分布式的架构也对我们数据一致性提出了新的挑战。以上述的{w: 1} 写安全配置为例,我们来分析一种比较复杂的场景。
- 01:00:00 网络故障,主从之间网络断开
- 01:00:01 应用写入一个文档: {ts: “01:00:01″} 注意这个文档无法复制到B和C。此时主节点尚未完全确认网络已故障,所以按照{w:1}规则继续接受并确认写入。
- 01:00:02 主节点A意识到自己无法和从节点B,C 联络上,主动降级为从节点,停止接受写操作
- 01:00:05 B,C 选举结果成功,B升级为主节点。B开始接受写操作。{ts: “01:00:06″}
- 01:00:08 网络恢复,A重新加入集群。这个时候A的oplog 和B的oplog已经有不一致了。A会主动把B上面不存在的写操作回滚掉(rollback),并写入一个回滚文件。
在这个时候应用如果再去查询 {ts: “01:00:01″}这个文档,MongoDB 将会说文档不存在!
怎么办怎么办? {w: “majority”} 就是我们的答案。 “majority” 指的是“大多数节点”。使用这个写安全级别,MongoDB只有在数据已经被复制到多数个节点的情况下才会向客户端返回确认。
我们来看一下在使用 {w: “majaority”} 之后,刚才的情况就变成了:
- 01:00:00 网络故障,主从之间网络断开
- 01:00:01 应用要求写入一个文档: {ts: “01:00:01″} 文档会首先成功写入主节点。但是由于网络断开这个文档无法复制到B和C。因为无法满足{w:”majority”}要求,从应用的角度这个文档并没有写入成功。
- 01:00:02 主节点A意识到自己无法和从节点B,C 联络上,主动降级为从节点,停止接受写操作
- 01:00:05 B,C 选举结果成功,B升级为主节点。B开始接受写操作。{ts: “01:00:06″}
- 01:00:08 网络恢复,A重新加入集群。这个时候A会产生回滚,把{ts: “01:00:01″}这个文档删除。 此时集群的数据状态为一致和正确的。
至此,如果使用 {w: “majority”, j:1 }, 那么MongoDB可以满足所有级别数据持久性的要求。值得注意的是在2013年5月Kyle Kingsly 发表了一篇博客 Call Me Maybe: http://aphyr.com/posts/284-call-me-maybe-mongodb 在这片文章里Kyle 汇报了一些关于 {w: “majority”} 的bug, 这些bug已经在2.6里被解决了。当然像Sven那样的哗众取宠之流,估计并没有去研究3.0里面是否真的有问题,而是随便google了一下几年前的东西来做文章。
总结
一般来说,MongoDB建议在集群中使用 {w: “majority”} 设置。在一个集群是健壮的部署的情况下(如:足够网络带宽,机器没有满负荷),这个可以满足绝大部分数据安全的要求,因为MongoDB的复制在正常情况下是毫秒级别的,往往在Journal刷盘之前已经复制到从节点了。如果你追求完美,那么可以再进一步使用{j:1} 。两者相结合,
传说中MongoDB 丢数据的事情,确实已经成为传说了。
后记:在我写这篇文章之时,社区又有人汇报数据丢失的问题。说的是每一万条记录就会丢一两条记录。对于这种情况,我的第一反应就是:查查你的代码吧,很多时候往往问题出在程序上。果不其然,经过仔细检查,原来是代码的问题。
http://www.mongoing.com/archives/1723
MongoDB丢数据问题的分析的更多相关文章
- 数据抓取分析(python + mongodb)
分享点干货!!! Python数据抓取分析 编程模块:requests,lxml,pymongo,time,BeautifulSoup 首先获取所有产品的分类网址: def step(): try: ...
- python数据抓取分析(python + mongodb)
分享点干货!!! Python数据抓取分析 编程模块:requests,lxml,pymongo,time,BeautifulSoup 首先获取所有产品的分类网址: def step(): try: ...
- MongoDB数据库索引构建情况分析
前面的话 本文将详细介绍MongoDB数据库索引构建情况分析 概述 创建索引可以加快索引相关的查询,但是会增加磁盘空间的消耗,降低写入性能.这时,就需要评判当前索引的构建情况是否合理.有4种方法可以使 ...
- 海量大数据大屏分析展示一步到位:DataWorks数据服务对接DataV最佳实践
1. 概述 数据服务(https://ds-cn-shanghai.data.aliyun.com) 是DataWorks产品家族的一员,提供了快速将数据表生成API的能力,通过可视化的向导,一分钟 ...
- 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践
1. 概述 数据服务(https://ds-cn-shanghai.data.aliyun.com) 是DataWorks产品家族的一员,提供了快速将数据表生成API的能力,通过可视化的向导,一分钟“ ...
- 三年之久的 etcd3 数据不一致 bug 分析
问题背景 诡异的 K8S 滚动更新异常 笔者某天收到同事反馈,测试环境中 K8S 集群进行滚动更新发布时未生效.通过 kube-apiserver 查看发现,对应的 Deployment 版本已经是最 ...
- flume到底会丢数据吗?其可靠性如何?——轻松搞懂Flume事务机制
先给出答案: 需要结合具体使用的source.channel和sink来分析,具体结果可看本文最后一节. Flume事务 一提到事务,我们首先就想到的是MySQL中的事务,事务就是将一批操作做成原 ...
- 刨根问底: Kafka 到底会不会丢数据?
大家好,我是 华仔, 又跟大家见面了. 上一篇作为专题系列的第二篇,从演进的角度带你深度剖析了关于 Kafka 请求处理全流程以及超高并发的网络架构设计的实现细节,今天开启第三篇,我们来聊聊 Kafk ...
- 性能测试报告的指标选择、数据选择和分析的参考【以Apache AB test为例】
前几天尝试用loadrunner初试了一下性能测试,对于如何选择数据.生成数据后如何分析很是迷惑,刚刚翻看一篇网友的博客,很有条理,特此记录一下,以供参考 转自: http://liriguang.i ...
随机推荐
- DOS批处理不支持将UNC 路径作为当前目录的巧妙解决方案
DOS批处理不支持将UNC 路径作为当前目录的巧妙解决方案在有些时候,需要在批处理中操作UNC目录,但批处理并不能直接对UNC目录进行操作,怎么办? 废话少说,直接上代码,打开网上邻居→整个网络→Mi ...
- 5 分钟上手 ECharts
获取 ECharts 你可以通过以下几种方式获取 ECharts. 从官网下载界面选择你需要的版本下载,根据开发者功能和体积上的需求,我们提供了不同打包的下载,如果你在体积上没有要求,可以直接下载完整 ...
- Ubuntu 16.04服务器安装及软件配置
1.配置静态地址 vim /etc/network/interfaces auto enp1s0 iface enp1s0 inet static address 192.168.1.131 netm ...
- &与&&的区别
&是“逻辑与”(“按位与”“位运算符”),一定要判断完所有的条件才能确定到底返回true还是false. &&是“短路与”(“逻辑运算符”),当从左至右判断时,一旦出现有一个条 ...
- 樱花漫地集于我心,蝶舞纷飞祈愿相随---总结 浮动:flux 浮动:flux
知识点 数据类型,运算符与表达式: 程序结构:顺序,选择,循环 问题 各种结构,单词意思 心得体会 抄程序是最笨的方法但我认为它是进步最快的方法,抄程序是积累经验的时候,而做项目才是真正把所学为所用的 ...
- CodeForces #369 C. Coloring Trees DP
题目链接:C. Coloring Trees 题意:给出n棵树的颜色,有些树被染了,有些没有.现在让你把没被染色的树染色.使得beauty = k.问,最少使用的颜料是多少. K:连续的颜色为一组 ...
- 倒计时js代码
<div class="time"> <span id="t_d">00天</span> <span id=" ...
- Ubuntu 下载 & 编译 Android5.1 源码
ustc & tsinghua android srchttps://lug.ustc.edu.cn/wiki/mirrors/help/aosphttps://mirrors.tuna.ts ...
- Five More Hacker Tools Every CISO Should Understand
As we mentioned in the first article, Top Five Hacker Tools Every CISO Should Understand, the role o ...
- java中byte, int的转换
最近在做些与编解码相关的事情,又遇到了byte和int的转换,看着那些关于反码.补码的说明依旧头疼,还是记下些实用的方法吧.int -> byte可以直接使用强制类型转换: byte b = ( ...