MongoDB是一個基於分布式 文件存儲的NoSQL數據庫,適合存儲JSON風格文件的形式。
-
三元素:數據庫、集合和文檔。
- 文檔:對應着關系數據庫中的行,就是一個對象,由鍵值對構成,是json的擴展Bson形式,示例
{'name':'guojing','gender':'男'}
- 集合:類似於關系數據庫中的表,儲存多個文檔,結構不固定,示例
{'name':'guojing','gender':'男'} {'name':'huangrong','age':18} {'book':'shuihuzhuan','heros':'108'}
- 文檔:對應着關系數據庫中的行,就是一個對象,由鍵值對構成,是json的擴展Bson形式,示例
一、環境安裝與運行
-
安裝
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服務器讓配置生效
-
啟動客戶端
sudo mongo
二、數據庫操作
-
查看當前數據庫
db
-
查看所有數據庫
show dbs
-
切換(或創建)數據庫
use 數據庫名
切換數據庫,如果數據庫不存在,當插入數據或創建集合時,會自動創建這個數據庫
-
刪除數據庫
db.dropDatabase()
刪除當前指向的數據庫,如果不存在,則什么也不做
三、集合操作
-
創建
db.createCollection(name[,options]) #name集合名稱 #options可選,是一個用於指定集合配置的文檔,其中capped參數默認不設置上限(false),若設置上限(true)則需要指定參數size,單位字節
#不設置集合大小 db.createCollection("stu") #設置集合大小 db.createCollection("sub", { capped : true, size : 10 } )
-
查看當前數據庫集合
show collections
-
刪除集合
db.集合名.drop()
四、數據操作
-
插入
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.集合名.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:"^黃"}})
- 使用/表達式/或者$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}})
- 使用$where:function(){return 滿足條件的數據表達式}------運算符使用js語法,比如邏輯與(&&),邏輯或(||)
-
投影
- 只顯示部分字段
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("字段名",{條件})
- 對數據去重,返回的是該字段值去重后組成的列表,比如對‘name’字段去重,則列表中的元素為去重后的name值。
-
五、聚合(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}])
- 跳過指定數量的文檔,並返回余下的文檔,$skip和$limi合用時,注意先寫skip,再寫limit
-
$unwind
- 將文檔中的某一個數組類型字段拆分成多條,每條包含數組中的一個值
db.集合名稱.aggregate([{$unwind:'$字段名稱'}])
- 將文檔中的某一個數組類型字段拆分成多條,每條包含數組中的一個值
六、索引
-
提升查詢速度
-
創建大量數據
for(var i=0;i<1000000;i++){db.stu.insert({name:'test'+i,num:i})}
-
性能分析工具:explain("executionStats")
查詢語句.explain("executionStats")
'millis'后面顯示的是查詢時間,單位ms
-
建立索引
db.集合.ensureIndex({屬性:1或-1}) #1表示升序,-1表示降序,建立后的索引名稱為"屬性_1"或者"屬性_-1"
例如:db.stu.ensureIndex({name:1}),建立后的索引名稱為"name_1"
-
建立唯一索引
db.集合.ensureIndex({屬性:1},{"unique":true})
-
建立聯合索引
db.集合名.ensureIndex({屬性1:1,屬性2:1...})
-
查看索引
db.集合名.getIndexes()
-
刪除索引
db.集合名.dropIndex("索引名稱")
七、數據庫安全
為了更安全的訪問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:'數據庫名'}...] #數組里可以有多個角色文檔,比如用戶在不同的數據庫里都有讀寫權限 })
-
查看當前數據庫的用戶
show users
-
修改用戶:可以修改pwd、roles屬性
db.updateUser('用戶名',{pwd:'新密碼',roles:[{'新角色'}...]})
-
刪除用戶
use admin #切換到admin數據庫 db.system.users.remove(條件)
-
終端連接
sudo mongo -u '用戶名' -p '密碼' --authenticationDatabase '數據庫名'
-
八、復制
復制提供了數據的冗余備份,並在多個服務器上存儲數據的副本,允許從硬件故障和服務中斷中恢復數據,能夠實現無宕機維護(自動故障轉移與自動恢復)。
復制至少需要2個節點,其中1個為主節點,其它均為從節點。任何節點均可以成為主節點。
主節點負責所有寫入操作,從節點定期輪詢主節點獲取這些操作並執行這些操作,從而保證從節點的數據與主節點一致。
-
設置復制節點
-
創建數據庫文件存放目錄(自定義)
mkdir ~/Desktop/t1 mkdir ~/Desktop/t2
-
使用如下格式啟動mongod,如果在同一台主機上,注意port不能相同,replSet的名稱必須是一致的(名稱可以自定義)
sudo mongod --bind_ip 192.168.196.128 --port 27017 --dbpath ~/Desktop/t1 --replSet rs0 sudo mongod --bind_ip 192.168.196.128 --port 27018 --dbpath ~/Desktop/t2 --replSet rs0
-
連接主服務器(自定義一個)
sudo mongo --host 192.168.196.128 --port 27017
-
初始化
rs.initiate()
哪個服務器執行初始化,哪個服務器就作為主節點,rs是mongo服務器中專門用於復本集操作的內置對象
-
查看當前狀態
rs.status()
-
添加副本集
rs.add("192.168.127.128:8899")
-
連接從服務器
sudo mongo --host 192.168.196.128 --port 27018
-
從服務器設置
rs.slaveOk()
主服務器插入數據,從服務器就可以讀取數據了
-
刪除從節點
rs.remove('192.168.196.128:27018') #需要在主服務器操作
關閉主服務器后再重新啟動,會發現原來的從服務器變為了主服務器,新啟動的服務器(原來的主服務器)變為了從服務器,但是注意重新設置rs.slaveOk()
-
九、備份與恢復
-
備份
-
語法
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
-
引入包
import pymongo
-
建立連接並創建客戶端
有安全認證:client=MongoClient("mongodb://用戶名:密碼@host:27017/數據庫名稱") 無安全認證:client=MongoClient("mongodb://localhost: 27017")
-
獲得數據庫(以test數據庫為例)
db = client.數據庫名 例如:db = client.test
-
獲得集合stu
stu = db.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) ...
- find_one 查找單個文檔
-
插入
- insert_one 插入一條文檔
stu.insert_one(文檔)
- insert_many 插入多條文檔
stu.insert_many([文檔1,文檔2...])
- insert_one 插入一條文檔
-
更新
- update 更新匹配到的第一條整條文檔
stu.update({條件},文檔)
- update_one 與$set一起使用,指定屬性修改匹配到的第一條文檔
stu.update_one({條件},{$set:{文檔}})
- update_many 與$set一起使用,指定屬性修改多條文檔
stu.update_many({條件},{$set:{文檔}})
- update 更新匹配到的第一條整條文檔
-
刪除
- delete_one 刪除單條文檔
stu.delete_one({條件})
- delete_many 刪除多條文檔
stu.delete_many({條件})
- delete_one 刪除單條文檔
-