|
MongoDB是一个基于分布式 文件存储的NoSQL数据库,适合存储JSON风格文件的形式。
-
三元素:数据库、集合和文档。
- 文档:对应着关系数据库中的行,就是一个对象,由键值对构成,是json的扩展Bson形式,示例
{'name':'guojing','gender':'男'}
- 集合:类似于关系数据库中的表,储存多个文档,结构不固定,示例
{'name':'guojing','gender':'男'}
{'name':'huangrong','age':18}
{'book':'shuihuzhuan','heros':'108'}
一、环境安装与运行
-
安装
sudo apt-get install mongodb
配置文件为 /etc/mongodb.conf,默认端口号:27017
-
启动服务器
sudo service mongodb start #相当于执行命令 sudo mongod --config /etc/mongodb.conf
-
关闭服务器
sudo service mongodb stop
-
重启服务器
sudo service mongodb restart #修改了配置文件/etc/mongodb.conf后,需要重启mongodb服务器让配置生效
-
启动客户端
二、数据库操作
三、集合操作
-
创建
db.createCollection(name[,options])
#name集合名称
#options可选,是一个用于指定集合配置的文档,其中capped参数默认不设置上限(false),若设置上限(true)则需要指定参数size,单位字节
#不设置集合大小
db.createCollection("stu")
#设置集合大小
db.createCollection("sub", { capped : true, size : 10 } )
-
查看当前数据库集合
-
删除集合
四、数据操作
-
插入
db.集合名.insert(文档) 或 db.集合名.insert([文档...]) #如果不指定_id字段,则分配一个唯一的ObjectId;如果指定_id字段,且_id已经存在时,不做任何操作;如果一次性插入多条数据,以数组的方式传入文档
# 不指定_id
db.stu.insert({name:'gj',gender:1})
#指定_id
s1={_id:'20160101',name:'hr'}
s1.gender=0
db.stu.insert(s1)
-
删除--(注意justOne参数)
db.集合名.remove(条件,{justone:<boolean>}) #参数justOne默认false,删除多条
# 只删除匹配到的一条数据
db.stu.remove({gender:0},{justOne:true})
#删除所有
db.stu.remove({})
-
修改--(注意$set以及multi参数)
db.集合名.update({条件},$操作符,{multi: <boolean>}]) #参数multi只和$操作符一起使用,默认false,只修改一条数据,true表示修改多条数据
# 不使用操作符$set,修改整条文档
db.stu.update({name:'hr'},{name:'mnc'})
#使用操作符$set指定属性修改
db.stu.update({name:'hr'},{$set:{name:'hys'}})
#multi参数和$set一起使用,修改多条文档
db.stu.update({},{$set:{gender:0}},{multi:true})
- 修改集合字段名
db.目标集合.updateMany( {查询条件}, {$rename : { 原名称:新名称 } } )
-
保存
db.集合名.save(document) #在手动插入_id字段时,如果_id已经存在,做全文档更新操作,其余均表示插入数据。
db.stu.save({_id:'20160102','name':'yk',gender:1})
db.stu.save({_id:'20160102','name':'wyk'}) #对上述文档做修改
-
查询
-
基本查询
db.集合名.find({条件文档}) #查询所有
db.集合名.findOne({条件文档}) #只查询第一条
db.集合名.find({条件文档}).pretty() #结果格式化输出
-
比较运算符
- 等于:默认,没有运算符
- 小于: $lt
- 小于等于: $lte
- 大于:$gt
- 大于等于:$gte
- 不等于:$ne
#查询年龄大于等于18的学生
db.stu.find({"age":{$gte:18}})
-
逻辑运算符
- 逻辑与:默认
- 逻辑或:$or
# 查询年龄大于或等于18,并且性别为1的学生
db.stu.find({"age":{$gte:18},"gender":1})
#查询年龄大于18,或性别为0的学生
db.stu.find({$or:[{"age":{$gt:18}},{"gender":1}]})
#查询年龄小于18或者大于20,性别为1的学生
db.stu.find({$or:[{"age":{$lt:18}},{"age":{$gt:20}}],"gender":1})
-
范围运算符
- 在某个范围$in,后面接数组
- 不在某个范围$nin,后面接数组
#查询年龄18、20以及22岁的学生
db.stu.find({"age":{$in:[18,20,22]}})
#查询年龄不等于18或20的学生
db.stu.find({"age":{$nin:[18,20]}})
-
正则表达式
- 使用/表达式/或者$regex
#查询姓黄的学生
db.stu.find({"name":/^黄/})
db.stu.find({"name":{$regex:"^黄"}})
-
自定义查询
- 使用$where:function(){return 满足条件的数据表达式}------运算符使用js语法,比如逻辑与(&&),逻辑或(||)
#查询年龄18-22的学生
db.stu.find({$where:function(){return this.age>18 && this.age<22}})
#查询年龄小于18或者大于22的学生
db.stu.find({$where:function(){return this.age<18 || this.age>22}})
-
投影
- 只显示部分字段
db.集合名.find({条件},{字段名:1,...}) #1表示该字段显示,0不显示;_id列默认显示,不显示需要明确设置为0
#查询姓名和年龄(显示_id)
db.stu.find({},{name:1,gender:1})
#查询姓名和年龄(不显示_id)
db.stu.find({},{_id:0,name:1,gender:1})
-
skip
- 跳过指定数量的文档
db.集合名.find({条件}).skip(number) #number默认为0
#查询从第3条开始的学生信息
db.stu.find().skip(2)
-
limit
- 读取指定数量的文档
db.集合名.find({条件}).limit(number) #不写number参数,默认读取所有文档
#读取3条学生信息
db.stu.find().limit(3)
-
skip和limit合用
- 不区分先后顺序
#查询第5-9条学生信息
db.stu.find().skip(4).limit(5) #相当于跳过4条数据,选5条
-
sort
- 对结果集进行排序
db.集合名称.find({条件}).sort({字段:1,...}) #1表示升序,-1表示降序
#根据性别降序,再根据年龄升序
db.stu.find().sort({"gender":-1,"age":1})
-
count
- 对结果集中文档数目进行统计,返回整数
db.集合名.find({条件}).count()
或者
db.集合名.count({条件})
#统计年龄大于20的男生人数
db.stu.count({"age":{$gt:20},"gender":1})
-
distinct
- 对数据去重,返回的是该字段值去重后组成的列表,比如对‘name’字段去重,则列表中的元素为去重后的name值。
db.集合名.distinct("字段名",{条件})
五、聚合(aggregate)
-
聚合主要用于计算数据,类似sql中的sum()、avg()
db.集合名.aggregate([{管道:{表达式}}...])
-
管道
-
文档处理完毕后,通过管道进行下一次处理
-
常用管道:
- $group:将集合中的文档分组,可用于统计结果
- $match:过滤数据,只输出符合条件的文档
- $project:修改输入文档的结构,如重命名、增加、删除字段、创建计算结果
- $sort:将输入文档排序后输出
- $limit:限制聚合管道返回的文档数
- $skip:跳过指定数量的文档,并返回余下的文档
- $unwind:将数组类型的字段进行拆分
-
表达式
-
处理输入文档并输出
-
语法:表达式:‘$字段名’
-
常用表达式:
- $sum:计算总和,$sum:'$字段'表示求和,注意$sum:1表示计数,
- $avg:计算平均值
- $min:获取最小值
- $max:获取最大值
- $push:在结果文档中插入值到一个数组中(以列表的方式显示字段值)
- $first:根据资源文档的排序获取第一个文档数据
- $last:根据资源文档的排序获取最后一个文档数据
-
$group
- 文档分组,用于统计结果
- _id表示分组的依据,使用某个字段的格式为'$字段'
# 统计男、女生人数
db.stu.aggregate([{$group:{"_id":'gender',"couter":{$sum:1}}}])
#结果文档中显示_id和counter的值
- _id按照null分组,会将集合中所有文档分为一组
# 求学生总人数和平均年龄
db.stu.aggregate([{$group:{_id:null,counter:{$sum:1},average_age:{$avg:"$age"}}}])
- 使用$$ROOT可以将文档内容加入到结果集的数组中
#统计男、女生信息
db.stu.aggregate([{$group:{_id:"$gender",objects:{$push:"$$ROOT"}}}])
-
$match
- 过滤数据,输出符合条件文档
#查询年龄大于20的学生
db.stu.aggregate([{$match:{age:{$gt:20}}}])
#查询年龄大于20的男、女生人数
db.stu.aggregate([{$match:{age:{$gt:20}}},{$group:{_id:"$gender",counter:{$sum:1}}}])
-
$project
- 修改输出文档的结构
#查找学生姓名、年龄
db.stu.aggregate([{$project:{"_id":0,"name":1,"age":1}}])
#查询男生、女生人数,但仅输出人数
db.stu.aggregate([{$group:{_id:'$gender',counter:{$sum:1}}},{$project:{_id:0,counter:1}}])
-
$sort
- 将输入文档排序后输出
#查询学生信息,按年龄升序
db.stu.aggregate([{$sort:{age:1}}])
#查询男生、女生人数,按人数降序
db.stu.aggregate([{$group:{_id:'$gender',counter:{$sum:1}},{$sort:{counter:-1}}])
-
$limit
- 限制聚合管道返回的文档数
#查询2条学生信息
db.stu.aggregate([{$limit:2}])
-
$skip
- 跳过指定数量的文档,并返回余下的文档,$skip和$limi合用时,注意先写skip,再写limit
#查询从第3条开始的学生信息
db.stu.aggregate([{$skip:2}])
#统计男生、女生人数,按人数升序,取第二条数据
db.stu.aggregate([{$group:{_id:"$gender",counter:{$sum:1}}},{$sort:{"counter":1}},{$skip:1},{$limit:1}])
-
$unwind
- 将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值
db.集合名称.aggregate([{$unwind:'$字段名称'}])
六、索引
七、数据库安全
为了更安全的访问mongodb,需要访问者提供用户名和密码,于是需要在mongodb中创建用户。mongodb数据库采用了角色-用户-数据库的安全管理方式。
-
常用系统角色如下:
- root:只在admin数据库中可用,超级账号,超级权限
- Read:允许用户读取指定数据库
- readWrite:允许用户读写指定数据库
-
创建超级管理用户:
use admin #首先切换到admin数据库
db.createUser({
user:'用户名',
pwd:'密码',
roles:[{role:'root',db:'admin'}]
})
-
启用安全验证
-
修改配置文件( /etc/mongodb.conf)
#noauth = true
auth = true #开启安全验证
-
重启服务
sudo service mongodb restart
-
终端连接
# sudo mongo -u '用户名' -p '密码' --authenticationDatabase '数据库名'
sudo mongo -u 'admin' -p '密码' --authenticationDatabase 'admin'
-
普通用户管理
-
首先使用超级管理员登陆,然后再进行用户管理操作
-
创建普通用户
db.createUser({
user:'用户名',
pwd:'密码',
roles:[{role:'readWrite',db:'数据库名'}...] #数组里可以有多个角色文档,比如用户在不同的数据库里都有读写权限
})
-
查看当前数据库的用户
-
修改用户:可以修改pwd、roles属性
db.updateUser('用户名',{pwd:'新密码',roles:[{'新角色'}...]})
-
删除用户
use admin #切换到admin数据库
db.system.users.remove(条件)
-
终端连接
sudo mongo -u '用户名' -p '密码' --authenticationDatabase '数据库名'
八、复制
复制提供了数据的冗余备份,并在多个服务器上存储数据的副本,允许从硬件故障和服务中断中恢复数据,能够实现无宕机维护(自动故障转移与自动恢复)。
复制至少需要2个节点,其中1个为主节点,其它均为从节点。任何节点均可以成为主节点。
主节点负责所有写入操作,从节点定期轮询主节点获取这些操作并执行这些操作,从而保证从节点的数据与主节点一致。
九、备份与恢复
-
备份
-
语法
sudo mongodump -h 服务器地址 -d 需要备份的数据库 -o 备份数据存放目录
mkdir ~/Desktop/test1_bak #创建存放备份数据的目录
sudo mongodump -h 192.168.196.128:27017 -d test1 -o ~/Desktop/test1_bak
-
恢复
-
语法
sudo mongorestore -h 服务器地址 -d 恢复后数据库名 --dir 备份数据所在位置
sudo mongorestore -h 192.168.196.128:27017 -d test2 --dir ~/Desktop/test1_bak/test1
十、python交互
-
安装pymongo包
sudo pip3 install pymongo
-
引入包
-
建立连接并创建客户端
有安全认证:client=MongoClient("mongodb://用户名:密码@host:27017/数据库")
无安全认证:client=MongoClient("mongodb://localhost: 27017")
-
获得数据库(以test数据库为例)
db = client.数据库名
例如:db = client.test
-
获得集合stu
-
数据操作
-
查询
- find_one 查找单个文档
stu1 = stu.find_one({条件}) #返回一条文档,字典类型
- find 查找多个文档
cursor = stu.find({条件}) #返回迭代器对象cursor
#方式1:用for循环迭代取值
for s in cursor:
print(s) #字典类型
#方式2:用next取值
s1 = next(cursor)
s2 = next(cursor)
...
-
插入
- insert_one 插入一条文档
- insert_many 插入多条文档
stu.insert_many([文档1,文档2...])
-
更新
-
删除
- delete_one 删除单条文档
- delete_many 删除多条文档
十一、解决游标连接超时问题
游标连接单次最大超时时间为10分钟,单次从mongo服务端获取的数据为101条或者1~16M,如果在10分钟内,未处理完获取的所有数据,则会报异常
1、设置 no_cursor_timeout=True,即游标连接永不超时,需要手动关闭游标(可以利用with上下文管理器)
2、减少单次获取的数据量,比如 batch_size=10,即单次获取10条数据
十二、连接池
十三、事务(MongoDB 4.0后增加的)
- 手动开启
with client.start_session() as session:
session.start_transaction() # 开始事务
try:
# 事务操作(例如多个读写)
collection1.update_one({...}, {...}, session=session)
collection2.insert_one({...}, session=session)
session.commit_transaction() # 提交事务
except Exception as e:
session.abort_transaction() # 回滚事务
print("事务失败:", str(e))
- 自动提交
with session.start_transaction():
collection.update_one({...}, {...}, session=session)
# 无需手动 commit/abort
- 事务必须在同一个会话(
session)中执行,且所有操作需传递 session 参数。
- 连接池默认支持事务,无需特殊配置,但需确保 MongoDB 是复制集(Replica Set)或分片集群(Sharded Cluster)---------需要特殊配置,否则会直接报错
来源:https://www.cnblogs.com/eliwang/p/14587595.html |