MongoDB 初体验
1 基础概念
- database - 数据库
- collection - 集合,类似于mysql中的表
- document - 每行记录数据
- field - 字段,类似于mysql中的列名
mongodb提供一个mongo客户端,类似于mysql提供的客户端命令。
cd /path/to/mongo
mongo
mongo 127.0.0.1:27017
mongo客户端的命令有自动提示功能,使用tab键
# 查询所有的库,默认自带三个库不要动
show dbs
show databases
# 查看当前数据库的所有集合
show collections
show tables
# mongo客户端提供一个正确关闭mongodb服务器的方法,使用kill杀掉进程可能会丢失数据
use admin
db.shutdownServer()
# 无需显式创建数据库和集合,需要时会自动创建
use soloman
db.user.insert({name: 'soloman.vip', age: 6, is_admin: 1})
# 查看数据
db.user.find().pretty()
db.user.find({is_admin: 1}).pretty()
db.user.find({is_admin: 1}).sort({age: -1}).pretty()
# limit限制条数查询
db.user.find().limit(2)
# 使用skip跳过记录,结合limit实现分页
db.user.find().skip(2).limit(2)
# 删除数据
db.user.remove({is_admin: 1})
# 删除所有数据
db.user.remove({})
# 删除集合
db.user.drop()
# 删除数据库
db.dropDatabase()
# 更新数据
db.user.update({name: 'soloman'}, {$set: {age: 17}})
# 数字比较查询
db.user.find({age: {$gt: 90}}).pretty()
db.user.find({age: {$gte: 88}}).pretty()
db.user.find({age: {$lt: 17}}).pretty()
db.user.find({age: {$lte: 17}}).pretty()
db.user.find({age: {$eq: 17}}).pretty()
db.user.find({age: {$ne: 17}}).pretty()
# 多种条件的组合
db.user.find({$and: [{name: 'soloman'}, {age: {$lt: 18}}]}).pretty()
db.user.find({$or: [{name: 'soloman'}, {age: {$gt: 18}}]}).pretty()
# 使用正则表达式匹配
db.user.find({name: {$regex: '^[nN]+'}}).pretty()
db.user.find({name: {$regex: '(soloman)'}}).pretty()
2 慢查询与索引
MongoDB慢查询默认是超过100ms,会记录慢日志mongodb.log
db.getProfilingStatus()
# 准备50万测试数据
use soloman
for(i=1; i<=500000; i++){
db.myuser.insert({name: 'mytest'+i, age: i})
}
# 使用explain可以查看是否全表扫描
db.myuser.find({age:9999}).explain(true)
# 获取当前索引,默认有_id的索引,所以用_id查是比较快
db.myuser.getIndexes()
# 增加age的升序索引
db.myuser.ensureIndex({age:1})
# 删除索引
db.myuser.dropIndex({age:1})
# 使用正则的话,索引无效果
db.myuser.ensureIndex({name:1})
db.myuser.find({name: 'mytest99999'}).explain(true)
db.myuser.find({name: /99999/}).explain(true)
# 创建唯一索引,唯一索引对应的值不能重复
db.myuser.ensureIndex({name:1}, {unique:true})
db.myuser.insert({name:'nb', age:9})
# 因为是唯一索引,所以重复数据会报错
db.myuser.insert({name:'nb', age:9})
3 MongoDB监控
mongostat是mongodb自带的状态检测工具,在命令行下使用,它会间隔固定时间获取mongodb的当前运行状态并输出。
mongotop也是mongodb下的一个内置工具,用来跟踪一个MongoDB的实例,查看哪些读取和写入花费了大量时间。 mongotop提供每个集合水平的统计数据,默认情况下每秒返回一次统计值。
MongoDB 4.4版本开始默认只保留部分命令(mongo、mongos、mongod、install_compass),如有使用其余命令的需求,则需要安装“MongoDB Database Tools”。
# mongostat实时监控
mongostat -h 127.0.0.1:27017
# 每10秒返回一次数据
mongotop 10
# serverStatus可用来获取mongodb的状态信息
db.serverStatus()
# 查看网络流量信息
db.serverStatus().network
# 统计增、删、改、查的次数
db.serverStatus().opcounters
# 查看连接信息
db.serverStatus().connections
5 副本集与故障自动恢复
mongodb单台服务器数据会有丢失的风险,单台服务器无法做高可用性。mongodb副本集能够预防数据丢失,多台mongodb数据一致,并能够在有问题的时候自动切换。

mongodb的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。主节点记录在其上的所有操作到oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。
# 主服务器配置好集群名字replSetName
config = {
_id:"replSetName",
members:[
{_id:0,host:"192.168.0.128:27017"},
{_id:1,host:"192.168.0.129:27017"},
{_id:2,host:"192.168.0.130:27017"}
]
}
use admin
# 副本集初始化,需要一定时间
rs.initiate(config)
# 查看副本集状态,一个PRIMARY,其它SECONDARY。PRIMARY是主,只有PRIMARY能写入
rs.status()
#SECONDARY需要声明是slave才能查看数据,从库无法插入数据
rs.slaveOk()
# 查看slave的延时情况
rs.printSlaveReplicationInfo()
副本集当primary挂了,会挑选其中的一台secondary升为主,挑选其中一台secondary升级为primary的条件是剩下的集群台数>=2,如果集群只剩下一个实例的话,会有异常。
primary的选举依赖于各个secondary实例的优先权重,默认权重都是1。设置各个实例的优先权重,挑选自己想要的实例为主,只有primary可以更改权重配置。
# 获取副本集的配置,默认权重都是1
conf = rs.config()
# 索引号从0开始,每次递增1,类似数组
conf.members[0].priority = 10
conf.members[1].priority = 5
conf.members[2].priority = 2
# 更新mongodb副本集的配置,优先权重最高的提升为primary,关闭后再启动后也为主
rs.reconfig(conf)
mongodb副本集的扩展非常好,往副本集里添加实例和移除实例都非常方便,往mongodb副本集添加实例数据能够自动同步,无需人工干预。副本集经过添加、删除后顺序会乱,设置权重需要注意
# 往现有mongodb副本集中添加实例,注意副本集名称要保持一致replSetName
use admin
# 数据是自动同步,rs.add的优先权重默认为1
rs.add('192.168.0.131:27017')
# 从mongodb副本集中移除实例,不可移除primary
use admin
rs.remove('192.168.0.130:27017')
5 数据备份与恢复
单台服务器一定需要备份,以防天灾人祸。mongodb有提供mongodump工具用来备份数据,也提供mongorestore工具用来恢复数据。单台服务器直接使用mongodump进行备份,副本集需要连接到primary上备份。
# 备份所有库
mongodump -h 127.0.0.1:27017 -o /data/mongodbbackup/
# 恢复数据
mongorestore -h 127.0.0.1:27018 /data/mongodbbackup/
6 MongoDB分片集群
mongodb副本集可以解决数据备份、读性能的问题,但由于mongodb副本集每份数据都一模一样的,无法解决数据量过大问题。使用分片技术能够把数据分成多份存储,假如soloman.myuser里面有1亿条数据,分片能够实现5千万左右存储在data1,5千万左右存储在data2。data1、data2需要使用副本集的形式,以预防数据丢失。
mongodb分片集群三种角色
- router角色。mongodb的路由,提供入口,使得分片集群对外透明,router不存储数据。
- configsvr角色。mongodb的配置角色,存储元数据信息。分片集群后端有多份存储,读取数据该去哪个存储上读取,依赖于配置角色,配置角色建议使用副本集搭建。
- shardsvr角色。mongodb的存储角色,存储真正的数据,建议使用副本集搭建。
当用户通过router角色插入数据时,需要从configsvr知道这份数据插入到哪个节点,然后执行插入动作插入数据到sharedsvr。当用户通过router角色获取数据时,需要从configsvr知道这份数据是存储在哪个节点,然后再去sharedsvr获取数据。

配置角色configsvr
使用28017,28018,28019三个端口来搭建,修改各自端口的mongodb.conf
# 28017端口mongodb.conf示例,28018,28019同理配置
systemLog:
destination: file
logAppend: true
path: /data/mongodb/28017/mongodb.log
storage:
dbPath: /data/mongodb/28017/
journal:
enabled: true
processManagement:
fork: true
net:
port: 28017
bindIp: 127.0.0.1
replication:
replSetName: solomanconf
sharding:
clusterRole: configsvr
mongodb配置角色服务集群的启动跟单例的启动方式一致,都是使用mongod
mongod -f /data/mongodb/28017/mongodb.conf
mongod -f /data/mongodb/28018/mongodb.conf
mongod -f /data/mongodb/28019/mongodb.conf
全部启动后进行分片集群的配置角色副本集搭建
config = {
_id:"solomanconf",
configsvr: true,
members:[
{_id:0,host:"127.0.0.1:28017"},
{_id:1,host:"127.0.0.1:28018"},
{_id:2,host:"127.0.0.1:28019"}
]
}
rs.initiate(config)
验证是否搭建成功
mongo 127.0.0.1:28017
rs.status()
路由角色router
使用27017,27018,27019三个端口来搭建,修改各自端口的mongodb.conf。mongodb中的router角色只负责提供一个入口,不存储任何的数据。配置多个router,任何一个都能正常的获取数据。
# 27017端口mongodb.conf示例,27018,27019同理配置
systemLog:
destination: file
logAppend: true
path: /data/mongodb/27017/mongodb.log
processManagement:
fork: true
net:
port: 27017
bindIp: 127.0.0.1
sharding:
# 指定configsvr的地址,使用副本集id+ip端口的方式指定
configDB: solomanconf/127.0.0.1:28017,127.0.0.1:28018,127.0.0.1:28019
router的启动需要使用mongos
mongos -f /data/mongodb/27017/mongodb.conf
mongos -f /data/mongodb/27018/mongodb.conf
mongos -f /data/mongodb/27019/mongodb.conf
数据角色sharedsvr
分片集群的数据角色里面存储着真正的数据,所以数据角色一定得使用副本集,而且会配置多个数据角色(即多个副本集)
使用29017,29018,29019,29020四个端口来搭建,两个端口一个副本集(生产环境肯定是要三个端口以上)
# 数据服务副本集solomandata1,29017端口mongodb.conf示例,29018同理配置
systemLog:
destination: file
logAppend: true
path: /data/mongodb/29017/mongodb.log
storage:
dbPath: /data/mongodb/29017/
journal:
enabled: true
processManagement:
fork: true
net:
port: 29017
bindIp: 127.0.0.1
replication:
replSetName: solomandata1
sharding:
clusterRole: shardsvr
# 数据服务副本集solomandata2,29019端口mongodb.conf示例,29020同理配置
systemLog:
destination: file
logAppend: true
path: /data/mongodb/29019/mongodb.log
storage:
dbPath: /data/mongodb/29019/
journal:
enabled: true
processManagement:
fork: true
net:
port: 29019
bindIp: 127.0.0.1
replication:
replSetName: solomandata2
sharding:
clusterRole: shardsvr
启动各个端口的数据服务
mongod -f /data/mongodb/29017/mongodb.conf
mongod -f /data/mongodb/29018/mongodb.conf
mongod -f /data/mongodb/29019/mongodb.conf
mongod -f /data/mongodb/29020/mongodb.conf
29017、29018数据角色副本集solomandata1。29019、29020数据角色副本集solomandata2
# 数据角色副本集solomandata1
config = {
_id:"solomandata1",
members:[
{_id:0,host:"127.0.0.1:29017"},
{_id:1,host:"127.0.0.1:29018"}
]
}
rs.initiate(config)
# 数据角色副本集solomandata2
config = {
_id:"solomandata2",
members:[
{_id:0,host:"127.0.0.1:29019"},
{_id:1,host:"127.0.0.1:29020"}
]
}
rs.initiate(config)
注意:以上配置均使用不同端口来区分不同服务,实际生产环境中,数据服务的每个端口需要单独的一台服务器,路由和配置角色的可复用同一台服务器,所以至少需要6台以上的服务器
使用分片集群
在路由角色中给分片集群添加数据角色,数据角色为副本集的方式
# 连接路由角色服务器
mongo 127.0.0.1:27017
# 添加数据角色
sh.addShard("solomandata1/127.0.0.1:29017,127.0.0.1:29018")
sh.addShard("solomandata2/127.0.0.1:29019,127.0.0.1:29020")
sh.status()
至此,默认添加数据还是没有分片存储。针对某个数据库的某个表使用hash分片存储,分片存储就会同一个colloection分配两个数据角色(操作都是在路由角色里面)
use admin
db.runCommand({enablesharding:"soloman"});
db.runCommand({shardcollection: "soloman.myuser",key: {_id: "hashed"}})
插入50万条数据验证,会发现两个数据角色每个大概存有25万条数据
use soloman
for(i=1; i<=50000;i++){
db.myuser.insert( {name:'mytest'+i, age:i} )
}
7 Python使用MongoDB
使用python操作mongodb,需要使用pip安装pymongo模块
pip3 install pymongo
单实例连接示例
import pymongo
client = pymongo.MongoClient('127.0.0.1', 27017)
db_soloman = client.soloman
collection_myuser = db_soloman.myuser
for item in collection_myuser.find():
print(item)
副本集连接示例
import pymongo
# 两从一主副本集
client = pymongo.MongoClient(
['127.0.0.1:27017', '127.0.0.1:27018', '127.0.0.1:27019']
)
db_soloman = client.soloman
collection_myuser = db_soloman.myuser
new_user = {'age':20, 'name': 'nb'}
collection_myuser.insert(new_user)
for item in collection_myuser.find():
print(item)
运维时也能使用Python去监控mongodb状态
import pymongo
client = pymongo.MongoClient('127.0.0.1',27017)
db = client.admin
server_status = db.command('serverStatus')
print(server_status)
print('连接信息:', server_status['connections'])
print('流量信息':, server_status['network'])
print('增删改查信息':, server_status['opcounters'])