目录
- 帮助文档
- MongoDB在企业级网站中的定位
- 单机MongoDB部署
- 集群(副本集)
- 集群(副本集+仲裁)
- 集群(分片)
- mongo运行原理
- mongo管理小工具推荐
帮助文档
MongoDB官方更新速度过快,语法不断更新,shell命令也不断更新,建议直接看官方版。
- 国内帮助文档:https://docs.mongoing.com/the-mongodb-manual-cn
- 官方文档(海外):https://www.mongodb.com/developer/
- docker官方提供的MongoDB安装文档:https://hub.docker.com/_/mongo(mongo更新过快,某些命令可能不是最新)
本文主要从docker角度出发安装MongoDB
MongoDB在企业级网站中的定位
如图MongoDB是在关系型与非关系数据库之间,目前企业级网站面临3V(海量Volume,多样variety,实时velocity),3高(高并发,高可扩,高性能),通过数据冗余,在一条数据中存储父子的表数据来解决不能联查的问题。
举个例子:
- 当每秒写入的数据超过了数据库所能写入的上限时,我们改怎么办?
将数据暂存在MongoDB中,再通过定时任务,写入数据库中。虽然MongoDB存在丢失数据风险的,但如果服务器稳定遍不会出现这种问题
- redis缓存不能满足统计分析等场景时,我们改怎么办?
将数据通过冗余的方式写入MongoDB中,通过MongoDB丰富的查询方式解决这个问题。
单机MongoDB部署
最基础的使用MongoDB,无法使用事务功能
//下载镜像
docker pull mongo
//创建容器并持久化数据
docker run --name 容器名称 -p 对外端口:27017 --restart=always -v /app/docker/mongo/db:/data/db -v /etc/localtime:/etc/localtime -d mongo --bind_ip_all
//进入docker 容器
docker exec -it 容器名称 bash
//进入docker 容器并登录MongoDB
docker exec -it 容器名称 mongosh
//登录进MongoDB后需要重置Mongo
rs.initiate()
//至此MongoDB可以正常使用了
集群(副本集)
进阶版使用MongoDB
注:原复制集因主节点挂掉,不能继续写入数据被淘汰,更新为副本集
一个主库;两个从库组成,主库宕机时,这两个从库都可以被选为主库。但数据通过主库写入时,副本库会将数据抓取到副本集中进行存储。主库负责写入数据,副本节点负责读取数据。我们写代码时不需要区分主节点与副本节点,可以连接所有ip,MongoDB会自己分辨
通过心跳检测,如果主节点挂掉,优先发现主节点挂掉的副本节点会成为主节点,但主节点再恢复后不能再成为主节点,只能成为副本节点。但这里会出现一个问题,副本节点并不一定是计算能力最强的节点。而且主节点只能有一个
docker pull mongo
mkdir -p /app/docker/mongo1/db #创建挂载的db目录
mkdir -p /app/docker/mongo2/db #创建挂载的db目录
mkdir -p /app/docker/mongo3/db #创建挂载的db目录
#第一台:
docker run --name mongo-server1 -p 30001:27017 --restart=always -v /app/docker/mongo1/db:/data/db -v /etc/localtime:/etc/localtime -d mongo --replSet "rs0" --bind_ip_all
#第二台:
docker run --name mongo-server2 -p 30002:27018 --restart=always -v /app/docker/mongo2/db:/data/db -v /etc/localtime:/etc/localtime -d mongo --replSet "rs0" --bind_ip_all
#第三台:
docker run --name mongo-server3 -p 30003:27019 --restart=always -v /app/docker/mongo3/db:/data/db -v /etc/localtime:/etc/localtime -d mongo --replSet "rs0" --bind_ip_all
#进入容器 进入主的容器
docker exec -it mongo-server1 bash
#连接客户端
docker exec -it 容器名称 mongosh
//这里localhost是因为我都是在同一个服务器上装的,都换成各自的ip就好
//初始化
rs.initiate()
//注册副本集
rs.add("localhost:30002")
#如果想要在从节点查询(默认是客户端直连是不可查询的)
如果想要在端口30002 从服务上执行查询,则连接上之后需要执行 db.getMongo().setSlaveOk();
集群(副本集+仲裁)
仲裁节点不存储数据,仲裁会帮助选举出主节点,但如果仲裁集群挂掉,主节点也挂掉了,那就完蛋了。因为副本集群只能读取不能写入。而且主节点只能有一个
docker pull mongo
mkdir -p /app/docker/mongo1/db #创建挂载的db目录
mkdir -p /app/docker/mongo2/db #创建挂载的db目录
mkdir -p /app/docker/mongo3/db #创建挂载的db目录
#第一台:
docker run --name mongo-server1 -p 30001:27017 --restart=always -v /app/docker/mongo1/db:/data/db -v /etc/localtime:/etc/localtime -d mongo --replSet "rs0" --bind_ip_all
#第二台:
docker run --name mongo-server2 -p 30002:27018 --restart=always -v /app/docker/mongo2/db:/data/db -v /etc/localtime:/etc/localtime -d mongo --replSet "rs0" --bind_ip_all
#第三台:
docker run --name mongo-server3 -p 30003:27019 --restart=always -v /app/docker/mongo3/db:/data/db -v /etc/localtime:/etc/localtime -d mongo --replSet "rs0" --bind_ip_all
#进入容器 进入主的容器
docker exec -it mongo-server1 bash
#连接客户端
docker exec -it 容器名称 mongosh
//这里localhost是因为我都是在同一个服务器上装的,都换成各自的ip就好
//初始化
rs.initiate()
//注册副本集
rs.add("localhost:30002")
//注册仲裁
rs.addArb("localhost:30003")
#如果想要在从节点查询(默认是客户端直连是不可查询的)
如果想要在端口30002 从服务上执行查询,则连接上之后需要执行 db.getMongo().setSlaveOk();
集群(分片)
架构师必备,搞不懂分片完全有情可原,相当的复杂。大家创建分片时切记画好图再创建,不然会弄乱。
分片的意思就是将一个集合中的数据,拆成不同的分片集群来存储数据。接下来忘记副本集的概念,分片和副本集架构关系并不大
- App Server 就是某一个分片集群,其中包含很多分片(Shard)和一个主节点 。实际生产环境中的一个分片服务可以由好几台服务组成reclica set,防止单点故障。(详见下图)
- Shard 为数据存储分片,用来将分割后的数据储存起来。可以是集群,实现高负载,高可用。每一片都可以是复制集(replica set)
- Router 路由进程 也叫mongos,用来外部访问。从整体看来,无论有多少个app server,访问时只连接Router即可,Router只需要一个。应用程序接入 mongos 再查询到具体分片
- Config Service 路由表,每一台都具有全部 chunk 的路由信息,用来存储分割的信息,比如XXX存到哪里去,XXX要从哪里读取。也可以搭建集群。
任何集群里面都可以有仲裁
当然问题也会存在,比如某个分片挂掉,就会影响该分片内数据的读取,但只需要重启就好。如果 App Server,Config Service 任何一个整体挂掉,就不可用了,但都可以集群分布在不同机器上,问题不大。
192.168.1.201:
~~~shell
docker run --name shardsvr00 -p 10031:27018 -d -v /home/mongodb/data/sh/shardsvr00:/data/db mongo --shardsvr --replSet "rs_shardsvr0" --bind_ip_all
docker run --name shardsvr10 -p 10041:27018 -d -v /home/mongodb/data/sh/shardsvr10:/data/db mongo --shardsvr --replSet "rs_shardsvr1" --bind_ip_all
~~~
192.168.1.202:
~~~shell
docker run --name shardsvr01 -p 10032:27018 -d -v /home/mongodb/data/sh/shardsvr00:/data/db mongo --shardsvr --replSet "rs_shardsvr0" --bind_ip_all
docker run --name shardsvr11 -p 10042:27018 -d -v /home/mongodb/data/sh/shardsvr11:/data/db mongo --shardsvr --replSet "rs_shardsvr1" --bind_ip_all
~~~
192.168.3.203:
~~~shell
docker run --name shardsvr02 -p 10033:27018 -d -v /home/mongodb/data/sh/shardsvr00:/data/db mongo --shardsvr --replSet "rs_shardsvr0" --bind_ip_all
docker run --name shardsvr12 -p 10043:27018 -d -v /home/mongodb/data/sh/shardsvr12:/data/db mongo --shardsvr --replSet "rs_shardsvr1" --bind_ip_all
~~~
### 2.5初始化副本集
192.168.1.201执行
docker exec -it shardsvr00 bash
mongo --host 192.168.1.201 --port 10031
~~~shell
rs.initiate(
{
_id: "rs_shardsvr0",
members: [
{ _id: 0, host : "192.168.1.201:10031" },
{ _id: 1, host : "192.168.1.202:10032" },
{ _id: 2, host : "192.168.1.203:10033" }
]
}
);
~~~
docker exec -it shardsvr10 bash
mongo --host 192.168.1.201 --port 10041
~~~shell
rs.initiate(
{
_id: "rs_shardsvr0",
members: [
{ _id: 0, host : "192.168.1.201:10041" },
{ _id: 1, host : "192.168.1.202:10042" },
{ _id: 2, host : "192.168.1.203:10043" }
]
}
);
~~~
### 2.6.创建mongos,连接mongos到分片集群
192.168.1.201:
docker run --name mongos0 -d -p 10011:27017 --entrypoint "mongos" mongo --configdb rs_configsvr/192.168.1.201:10021,192.168.1.202:10022,192.168.1.203:10023 --bind_ip_all
192.168.3.202:
docker run --name mongos1 -d -p 10012:27017 --entrypoint "mongos" mongo --configdb rs_configsvr/192.168.1.201:10021,192.168.1.202:10022,192.168.1.203:10023 --bind_ip_all
### 2.7添加分片到集群
192.168.3.201:
docker exec -it mongos0 bash
mongo --host 192.168.1.201 --port 10011
sh.addShard("rs_shardsvr0/192.168.1.201:10031,192.168.1.202:10032,192.168.1.203:10033")
sh.addShard("rs_shardsvr1/192.168.1.201:10041,192.168.1.202:10042,192.168.1.203:10043")
## 数据库 启用 分片
sh.enableSharding("test")
## 分片集合
对 test.order 的 _id 字段进行哈希分片:
sh.shardCollection("test.order", {"_id": "hashed" })
mongo运行原理
- 当数据data需要写入时,会先存入私有内存空间Journal内存中,随后通过定时任务journal log 每100ms刷一次存入journal log对于的硬盘中 。同时也会通过异步的方式将数据每60s一次,写入硬盘中,完成存储
- 当数据需要读取时,不会直接读取硬盘中的数据,而是读取journal log 中的数据,并返回。
- 当然journal log 功能是可以通过配置关闭的,定时存储时间也可以自定义设置(一般都不会动)。但是 这样的模式当MongoDB挂掉时,丢失的数据不仅仅是100ms内的数据,而是取决于数据存入journal log 内存后,定时任务有没有办法在100ms内将数据写入硬盘。如果100ms内能解决,那么只会丢最近100ms内的数据。如果不能解决,那么丢失的就会因为延迟的问题,丢失超过100ms的数据。
mongo管理小工具推荐
docker run -it --rm \
--name mongo-express \
-p 8081:8081 \
-e ME_CONFIG_OPTIONS_EDITORTHEME="ambiance" \
-e ME_CONFIG_MONGODB_SERVER="192.168.1.201" \
-e ME_CONFIG_MONGODB_PORT="10011" \
-e ME_CONFIG_BASICAUTH_USERNAME="admin" \
-e ME_CONFIG_BASICAUTH_PASSWORD="admin" \
mongo-express