MongoDB,我的道
Posted MongoDB中文用户组
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MongoDB,我的道相关的知识,希望对你有一定的参考价值。
本文是罗聪在“我和MongoDB的故事”征文比赛的获奖文章,下面我们一起来欣赏下。
01
一切的开始
我们的用户在2014年提出了一个新需求,存储亿级数量的半结构化数据,并且要求支持高并发查询和更新。对于一个在年底才进入这个团队的菜鸟,我很惊讶技术负责人最终会选择使用MongoDB存储海量的数据,所以对这个团队充满了好奇感,是什么理由选择MongoDB?能否Hold新技术?不怕丢数据?
02
应用和拓展
好的技术只有在不断实践和总结中才能找到打开它那一扇魔法门的钥匙。
NoSQL的第一站
-
版本: MongoDB 3.2 -
集群模式: 副本集 -
读写压力比: 1:1 -
磁盘类型: SATA -
数据量: 2T,3亿条。 单集合最大1.5T,近1.4亿条,平均大小12K。 -
数据类型: 核心数据
这个集群目前仍运行中,最近一次较大的运维工作发生在2019年5月,从v3.2连续升级到3.6。 是为了使用Change Streams特性,为随后需要的跨地域的副本集和实时同步(到其他数据源)做基础。 不过3.6除了Change Streams极具吸引力外,还有很多新的feature让老用户看的心动,如可重试写入、增加Causal Consistency的可调一致性、Compass社区免费版等,所以让我们坚定升级到3.6。
MongoDB的官网文档非常强大,用一句话介绍就是只要你肯花时间去阅读和理解它,再勤加练习,肯定能成为一名合格的MongoDB DBA。 在这个章节的最后,我也附加了一个副本集版本升级实操,分享如何利用官方docs在不停服下滚动升级实例版本。
全面适应容器化
MongoDB部署之前,我们也思考了既然要发挥docker的弹性能力,避免过多的人工指令,于是设计了一些实例管理脚本,包括Dockerfile和下面的一段巧妙的集群初始化代码。 将这个代码封装在init.js文件中,然后和上层的shell脚本关联起来就能够轻松实现MongoDB副本集的所有初始化工作。
cfg = {
_id: 'rs',
members: [
{
_id: 0,
host: 'c1.luoc.com:27027'
},
{
_id: 1,
host: 'c1.luoc.com:27027'
},
{
_id: 2,
host: 'c1.luoc.com:27027'
}
]
};
rs.initiate(cfg);
while(db.isMaster().ismaster == false) { }
db = db.getSiblingDB('admin');
db.createUser({user:"admin", pwd:"0000", roles:["root"]});
db.createUser({user:"users", pwd:"0000", roles:["userAdminAnyDatabase"]});
db = db.getSiblingDB('test');
db.createUser({user:"test", pwd:"0000", roles:["dbOwner"]});
db.test.insert({key:"hello", value:"world"});
mongo --quiet init.js
应对海量元数据
-
使用GlusterFS或Ceph这种分布式文件存储系统? -
使用MongoDB GFS? -
自行设计方案?
写路径
-
应用传输文件。 -
所有文件直接上传到HDFS。 -
在上传之前,我们设计了存储优化服务将该文件元信息(Meta)存到MongoDB。 -
优化服务后台控制线程,定期对Meta进行聚合统计,如果未做compact的文件大小(不计数量)累积超过HDFS Block(128MB)的阈值(默认80%),启动新线程对所有文件进行compact并写入到SequenceFile,每个文件在SequenceFile的offset和size更新到对应Meta中。 -
如果没有超过阈值,就放弃本次compact,等待下个检查点。 -
如果单个文件大于该阈值,就跳过compact。
读路径
-
应用接口发起文件读请求。 -
请求首先通过优化服务路由到MongoDB并获取该文件Meta。 -
优化服务使用Meta定位在HDFS的Sequencefile。 -
最后打开HDFS Sequencefile从offset位置读取指定size的字节构建成文件返回。
MongoDB v3.2 > 3.6
-
副本集集群。 -
滚动升级。 -
升级路径 3.2 > 3.4 > 3.6。
准备
-
mongodb-linux-x86_64-rhel62-3.4.20.tgz
-
mongodb-linux-x86_64-rhel62-3.6.12.tgz
关键步骤
-
mongostat
命令行评估集群节点压力,在读写压力小的时间段做升级更合适。 -
mongo
命令行登录到admin数据库并执行以下命令。# 滚动日志
db.runCommand({ logRotate: 1 })
# 在Primary上锁定写操作和flush内存数据到磁盘
db.fsyncLock();
-
直接使用 cp -R
拷贝数据文件进行备份。 -
mongo
命令行设置优先Primary。cfg = rs.conf()
cfg.members[0].priority = 1
cfg.members[1].priority = 0.5
cfg.members[2].priority = 0.5
rs.reconfig(cfg)
-
Secondary执行以下命令(每次操作一个Secondary或者保证有 (n/2) + 1
个节点在线)。# 关闭数据库
db.shutdownServer();
# 替换bin目录的可执行文件
cp /opt/mongodb-linux-x86_64-rhel62-3.4.20/bin/* bin/
# 重启mongod实例(startup.sh是自定义脚本)
./startup.sh
-
Secondary正常升级后,再在Primary上执行以下命令。 # 强制重新选举,Primary降级为Secondary
rs.stepDown();
# 关闭数据库
db.shutdownServer();
cp /opt/mongodb-linux-x86_64-rhel62-3.4.20/bin/* bin/
# 重启mongod实例
./startup.sh
-
开启版本兼容性特性。
db.adminCommand( { setFeatureCompatibilityVersion: "3.4" } )
db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )
```
-
按照以上第 5
、6
、7
步骤再从v3.4升级到3.6即完成。
Troubleshoot
-
升级后数据如何预热到内存中? (1)mongo提供 touch
命令可以将磁盘上的数据文件预热到内存。但是仅适用于MMAPv1
存储引擎,不支持WiredTiger
。(2)不支持 WiredTiger
,那怎么预热? -
两次升级过程中配置文件需要修改吗? -
3.2 > 3.4
过程中需要将配置文件中废弃的参数移除才能启动mongod实例,如:nohttpinterface
-
3.4 > 3.6
过程中需要在启动指令中加入--bind_ip
参数。
-
db.stepDown()
命令有哪些注意事项? -
降级有效时间默认只有60s,执行后如果没有在60s内关闭数据库,集群会使用 priority
规则重新发起选主。 -
可以将自定义秒数传入方法来延长时间,如: db.stepDown(600)
-
如何确认数据库升级完成? -
db.version()
确认实例版本。 -
rs.status()
确认集群节点状态正常。 -
db.fsyncLock()
作用是什么? 不需要解锁吗? -
阻塞Primary上的写请求,防止在物理备份期间发生数据不一致。 -
解锁请使用 db.fsyncUnlock()
。 -
为什么不采用 mongodump
方式来备份数据? -
mongodump
不适合超大数据库或_id
没有采用默认ObjectId
的超大数据集合。 -
会不会发生 db.shutdownServer()
执行后无响应? -
可能会(这次就是)。 -
如果执行后等待时间超过1h请使用 kill -2 <pid>
再尝试关闭mongod实例,如果仍然无效,请评估风险后自行使用kill -9
。 -
kill -9 <pid>
的风险是什么? -
前提是mongod实例开启了 journal
,否则可能造成数据丢失。 -
kill -9
不应该在生产环境任何一种数据库中使用。 -
版本升级有哪些权威资料可以参考? -
https://docs.mongodb.com/v3.4/release-notes/3.4-upgrade-replica-set -
https://docs.mongodb.com/v3.6/release-notes/3.6-upgrade-replica-set
_id
没有采用默认
ObjectId
的超大数据集合。
03
花絮
3个月后,在基本掌握MongoDB的原理后,我多次模拟事故变量,终于复现了之前发生的一切。
-
A 登陆了GUI,连接配置中的Read Preference使用默认的Primay,即连接到Primary节点。 -
B 也登陆了GUI,但是连接配置和A有区别,Replica Set members列表仅填写了一个副本节点,且读选项选择了Secondary Preferred,即连接到Secondary节点。 -
A 使用GUI Shell执行了 db.coll.remove({x})
语法,但是x值在上下文不能保证非null,即remove(null),这是遍历删除集合中的所有数据! -
B 在随后的排查过程中,验证GUI Shell的remove方法时,虽然执行了setSlaveOk(),但是将"not master"的提示错误解读为不可操作,漏掉了这个主要细节。 -
A 的请求因为只会发送给Primary节点,所以不会遇到B的报错。
04
未来
-
MongoDB做核心数据的存储服务。 -
使用Change Streams将数据变化实时同步到Hadoop做数据开发、治理和OLAP。 -
最后形成的资产数据可以回流MongoDB,如果用户需要SQL,也可以通过Hadoop中的Phoenix进行SQL即席查询和操作型分析。 -
数据接口服务根据用户的需求和自定义配置连接MongoDB的数据集并生成RESTful API,提供给应用层。 -
数据集成过程中如果有非结构化、二进制大对象等,就可以根据数据规模和数据特征来选择MongoDB GFS、HDFS和Hbase 2.0 MOB灵活实现对象存储服务。 -
流式数据可以通过Kafka和Connector连接器分发到计算引擎,如果流式传输大对象,MongoDB可以作为海量数据切片的元数据最佳存储库。
长城软件大数据实验室,1Data产品负责人。
感谢MongoDB官方,锦木信息和Tapdata对活动的大力支持!
进入MongoDB技术交流群/投稿/合作 请添加社区助理小芒果微信 (ID:mongoingcom),添加请备注mongo
您还可以点击
“阅读原文”
预约在线研讨会
主题: MongoDB 4.2 新特性及其在阿里云的应用研讨会
主讲人:中国区高级咨询顾问Danny Zhang&阿里云专家徐雷
时间:2月27日下午2点
报名方式: 点击“阅读原文”进入报名
以上是关于MongoDB,我的道的主要内容,如果未能解决你的问题,请参考以下文章
ios - Heroku 和 MongoDb 上的自定义解析服务器错误 3080:JSON 文本没有以数组或对象开头,并且允许未设置片段的选项