1、Mongodb介绍
1.1、什么是Mongodb
MongoDB是一个文档数据库(以 JSON 为数据模型),由C++语言编写,旨在为WEB应用提供可扩展的 高性能数据存储解决方案。
文档来自于“JSON Document”,并非我们一般理解的 PDF,WORD 文档。
MongoDB是非关系数据库当中功能最丰富,最像 关系数据库的。它支持的数据结构非常松散,数据格式是BSON,一种类似JSON的二进制形式的存储格 式,简称Binary JSON ,和JSON一样支持内嵌的文档对象和数组对象,因此可以存储比较复杂的数据类 型。MongoDB最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几 乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。原则上 Oracle 和 MySQL 能做的事情,MongoDB 都能做(包括 ACID 事务)。
MongoDB在数据库总排名第5,仅次于Oracle、MySQL等RDBMS,在NoSQL数据库排名首位。从诞生 以来,其项目应用广度、社区活跃指数持续上升。
MongoDB概念与关系型数据库(RDBMS)非常类似:
SQL概念 | MongoDB概念 |
数据库(database) | 数据库(database) |
表(table) | 集合(collection) |
行(row) | 文档(document) |
列(column) | 字段(field) |
索引(index) | 索引(index) |
主键(primary key) | _id(字段) |
视图(view) | 视图(view) |
表连接(table joins) | 聚合操作($lookup) |
- 数据库(database):最外层的概念,可以理解为逻辑上的名称空间,一个数据库包含多个不同名 称的集合。
- 集合(collection):相当于SQL中的表,一个集合可以存放多个不同的文档。
- 文档(document):一个文档相当于数据表中的一行,由多个不同的字段组成。
- 字段(field):文档中的一个属性,等同于列(column)。
- 索引(index):独立的检索式数据结构,与SQL概念一致。
- id:每个文档中都拥有一个唯一的id字段,相当于SQL中的主键(primary key)。
- 视图(view):可以看作一种虚拟的(非真实存在的)集合,与SQL中的视图类似。从MongoDB 3.4版本开始提供了视图功能,其通过聚合管道技术实现。
- 聚合操作($lookup):MongoDB用于实现“类似”表连接(tablejoin)的聚合操作符。
尽管这些概念大多与SQL标准定义类似,但MongoDB与传统RDBMS仍然存在不少差异,包括:
- 半结构化,在一个集合中,文档所拥有的字段并不需要是相同的,而且也不需要对所用的字段进行 声明。因此,MongoDB具有很明显的半结构化特点。除了松散的表结构,文档还可以支持多级的 嵌套、数组等灵活的数据类型,非常契合面向对象的编程模型。
- 弱关系,MongoDB没有外键的约束,也没有非常强大的表连接能力。类似的功能需要使用聚合管 道技术来弥补。
1.2、MongoDB的优势
MongoDB基于灵活的JSON文档模型,非常适合敏捷式的快速开发。与此同时,其与生俱来的高可用、 高水平扩展能力使得它在处理海量、高并发的数据应用时颇具优势。
- 横向扩展力,轻松支持TB-PB数量级
- JSON 结构和对象模型接近,开发代码量低
- JSON的动态模型意味着更容易响应新的业务需求
- 复制集提供99.999%高可用
- 分片架构支持海量数据和无缝扩容
2、MongoDB快速开始
2.1、linux安装MongoDB
下载MongoDB Community Server
下载地址: https://www.mongodb.com/try/download/community
重命名
mv mongodb-linux-x86_64-rhel70-4.4.15 mongodb
添加环境变量
修改/etc/profile
,添加环境变量,方便执行MongoDB命令
export MONGODB_HOME=/qijingjing/resource/mongodb PATH=$PATH:$MONGODB_HOME/bin
使环境变量立即生效
source /etc/profile
2.2、启动MongoDBServer
# 创建一些存放数据、日志、配置的文件夹 mkdir -p data log conf # 创建一个日志文件 touch log/mongodb.log # 进行启动 bin/mongod --port=27017 --dbpath=data --logpath=log/mongodb.log --fork
名词解释
- –dbpath :指定数据文件存放目录
- –logpath :指定日志文件,注意是指定文件不是目录
- –logappend :使用追加的方式记录日志
- –port:指定端口,默认为27017
- –bind_ip:默认只监听localhost网卡
- –fork: 后台启动
- –auth: 开启认证模式
利用配置文件启动服务
编辑mongodb/conf/mongo.conf
文件,内容如下
systemLog: destination: file path: /qijingjing/resource/mongodb/log/mongod.log # 日志文件的全路径 logAppend: true storage: dbPath: /qijingjing/resource/mongodb/data # 存放数据文件夹的的全路径 engine: wiredTiger #存储引擎 journal: #是否启用journal日志 enabled: true net: bindIp: 0.0.0.0 #开启可以使外网访问 port: 27017 # 端口号 processManagement: fork: true
注意一定要yaml格式
启动mongod(没有关闭要先关闭mongod,然后再启动)
mongod -f conf/mongo.conf
关闭MongoDB服务
方式一:
mongod --port=27017 --dbpath=data --shutdown
方式二:
执行mongo
命令进入shell界面
use admin; db.shutdownServer();
2.3、MongoDB shell的使用
mongo是MongoDB的交互式JavaScript Shell界面,它为系统管理员提供了强大的界面,并为开发人员 提供了直接测试数据库查询和操作的方法。
mongo --port=27017
–port:指定端口,默认为27017
–host: 连接的主机地址,默认为127.0.0.1
JavaScript支持
mongo shell是基于JavaScript语法的,MongoDB使用了SpiderMonkey作为其内部的JavaScript解释器 引擎,这是由Mozilla官方提供的JavaScript内核解释器,该解释器也被同样用于大名鼎鼎的Firefox浏览 器产品之中。SpiderMonkey对ECMA Script标准兼容性非常好,可以支持ECMA Script 6。可以通过下 面的命令检查JavaScript解释器的版本:
mongo shell 常用命令
命令 | 说明 |
show dbs|show databases | 显示数据库列表 |
use 数据库名 | 切换数据库,如果不存在创建数据库 |
db.dropDatabase() | 删除数据库 |
show collections|show tables | 显示当前数据库的集合列表 |
db.集合名.stats() | 查看集合详情 |
db.集合名.drop() | 删除集合 |
show users | 显示当前数据库的用户列表 |
show roles | 显示当前数据库的角色列表 |
show profile | 显示最近发生的操作 |
load(“xxx.js”) | 执行一个JavaScript脚本文件 |
exit | quit()退出当前shell |
help | 查看mongodb支持哪些命令 |
db.help() | 查询当前数据库支持的方法 |
db.集合名.help() | 显示集合的帮助信息 |
db.version() | 查看数据库版本 |
2.3.1、数据库操作
#查看所有库 show dbs # 切换到指定数据库,不存在则创建 use test # 删除当前数据库 db.dropDatabase()
2.3.2、集合操作
#查看集合 show collections #创建集合 db.createCollection("emp") #删除集合 db.emp.drop()
创建集合语法
db.createCollection(name,options)
options参数
字段 | 类型 | 描述 |
capped | 布尔 | (可选)如果为true,则会创建固定集合,当达到最大值时,它会自动覆盖最早的文档 |
size | 数值 | (可选)为固定集合指定一个最大值(以字节计)。如果capped为true,也需要指定该字段 |
max | 数值 | (可选)指定固定集合中包含文档的最大数量 |
注意:当集合不存在时,向集合中插入文档也会创建集合
2.4、安全认证
2.4.1、创建管理员账号
# 设置管理员用户名密码需要切换到admin库 use admin #创建管理员 db.createUser({user:"root",pwd:"root",roles:["root"]}) # 查看所有用户信息 show users #删除用户 db.dropUser("root")
2.4.2、常用权限
权限名 | 描述 |
read | 允许用户读取指定数据库 |
readWrite | 允许用户读写指定数据库 |
dbAdmin | 允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统 计或访问system.profile |
dbOwner | 允许用户在指定数据库中执行任意操作,增、删、改、查等 |
userAdmin | 允许用户向system.users集合写入,可以在指定数据库里创建、删 除和管理用户 |
clusterAdmin | 只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管 理权限 |
readAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的读权限 |
readWriteAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的读写权限 |
userAdminAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的userAdmin权限 |
dbAdminAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限 |
root | 只在admin数据库中可用。超级账号,超级权限 |
用户认证,返回1表示认证成功
> use admin switched to db admin > db.auth("root","root") 1
2.4.3、创建应用数据库用户
use appdb db.createUser({user:"123456",pwd:"123456",roles:["dbOwner"]})
默认情况下,MongoDB不会启用鉴权,以鉴权模式启动MongoDB
mongod -f conf/mongo.conf --auth
启用鉴权之后,连接MongoDB的相关操作都需要提供身份认证。
mongo -u 123456 -p 123456 --authenticationDatabase=appdb
3、MongoDB文档操作
3.1、插入文档
3.2版本之后新增了db.collection.insertOne()
和db.collection.insertMany()
.
新增单个文档
- insertOne: 支持
writeConcern
db.collection.insertOne( <document>, { writeConcern: <document> } )
writeConcern 决定一个写操作落到多少个节点上才算成功。writeConcern 的取值包括: 0:发起写操作,不关心是否成功;
1~集群最大数据节点数:写操作需要被复制到指定节点数才算成功;
majority:写操作需要被复制到大多数节点上才算成功
- insert: 若插入的数据主键已经存在,则会抛 DuplicateKeyException 异常,提示主键重复,不保存当前数据
- save: 如果_id主键存在则更新数据,如果不存在就插入数据。
> db.emp.insert({name:"张三",age:12}) WriteResult({ "nInserted" : 1 }) > db.emp.save({name:"李四",age:3}) WriteResult({ "nInserted" : 1 }) > db.emp.insertOne({sex:1,age:2}) { "acknowledged" : true, "insertedId" : ObjectId("62c410ed65d7649befbb697a") } > db.emp.find() { "_id" : ObjectId("62c410ac65d7649befbb6978"), "name" : "张三", "age" : 12 } { "_id" : ObjectId("62c410b465d7649befbb6979"), "name" : "李四", "age" : 3 } { "_id" : ObjectId("62c410ed65d7649befbb697a"), "sex" : 1, "age" : 2 } >
批量新增文档
insertMany
: 向指定集合中插入多条文档数据
db.collection.insertMany( [ <document 1> , <document 2>, ... ], { writeConcern: <document>, ordered: <boolean> } )
writeConcern:写入策略,默认为 1,即要求确认写操作,0 是不要求。
ordered:指定是否按顺序写入,默认 true,按顺序写入。
- insert和save也可以实现批量插入
> db.emp.insert([{x:5,y:7},{x:3,y:9}]) BulkWriteResult({ "writeErrors" : [ ], "writeConcernErrors" : [ ], "nInserted" : 2, "nUpserted" : 0, "nMatched" : 0, "nModified" : 0, "nRemoved" : 0, "upserted" : [ ] }) > db.emp.save([{x:50,y:70},{x:30,y:90}]) BulkWriteResult({ "writeErrors" : [ ], "writeConcernErrors" : [ ], "nInserted" : 2, "nUpserted" : 0, "nMatched" : 0, "nModified" : 0, "nRemoved" : 0, "upserted" : [ ] }) > db.emp.insertMany([{x:550,y:750},{x:350,y:950}]) { "acknowledged" : true, "insertedIds" : [ ObjectId("62c4143565d7649befbb6980"), ObjectId("62c4143565d7649befbb6981") ] } >
测试: 批量插入50条随机数据
编辑脚本book.js
var tags = ["nosql","mongodb","document","developer","popular"]; var types = ["technology","sociality","travel","novel","literature"]; var books=[]; for(var i=0;i<50;i++){ var typeIdx = Math.floor(Math.random()*types.length); var tagIdx = Math.floor(Math.random()*tags.length); var favCount = Math.floor(Math.random()*100); var book = { title: "book-"+i, type: types[typeIdx], tag: tags[tagIdx], favCount: favCount, author: "xxx"+i }; books.push(book) } db.books.insertMany(books);
进入mongo shell ,执行
load("book.js")
读取:
db.books.find()
有分页,输入it进入下一页
3.2、查询文档
find查询集合中的若干文档。语法格式如下:
db.collection.find(query, projection)
- query: 可选,使用查询操作符指定查询条件
- projection:可选,使用投影操作符指定返回的键。查询时时返回文档中所有键值, 只需省略该参 数即可(默认省略)。投影时,id为1的时候,其他字段必须是1;id是0的时候,其他字段可以是 0;如果没有_id字段约束,多个其他字段必须同为0或同为1。
如果查询返回的条目数量较多,mongo shell则会自动实现分批显示。默认情况下每次只显示20条,可 以输入it命令读取下一批。
findOne查询集合中的第一个文档。语法格式如下:
db.collection.findOne(query, projection)
条件查询
#查询带有nosql标签的book文档: db.books.find({tag:"nosql"}) #按照id查询单个book文档: db.books.find({_id:ObjectId("61caa09ee0782536660494d9")}) #查询分类为“travel”、收藏数超过60个的book文档: db.books.find({type:"travel",favCount:{$gt:60}})
查询条件对照表
SQL | MQL |
a =1 | {a: 1} |
a <> 1 | {a: {$ne: 1}} |
a > 1 | {a: {$gt: 1}} |
a >= 1 | {a: {$gte: 1}} |
a < 1 | {a: {$lt: 1}} |
a <=1 | {a: {$lte: 1}} |
查询逻辑对照表
SQL | MQL |
a = 1 AND b = 1 | {a: 1, b: 1}或{$and: [{a: 1}, {b: 1}]} |
a = 1 OR b = 1 | {$or: [{a: 1}, {b: 1}]} |
a IS NULL | {a: {$exists: false}} |
a IN (1, 2, 3) | {a: {$in: [1, 2, 3]}} |
查询逻辑运算符
- $lt: 存在并小于
- $lte: 存在并小于等于
- $gt: 存在并大于
- $gte: 存在并大于等于
- $ne: 不存在或存在但不等于
- $in: 存在并在指定数组中
- $nin: 不存在或不在指定数组中
- $or: 匹配两个或多个条件中的一个
- $and: 匹配全部条件
排序分页
- 指定排序
在MongoDB中使用sort()
方法对数据进行排序
#指定按收藏数(favCount)降序返回 db.books.find({type:"travel"}).sort({favCount:-1})
- 分页查询
skip
用于指定跳过记录数,limit
则用于限定返回结果数量
db.books.find().skip(8).limit(4)
正则表达式匹配查询
MongoDB使用$regex
操作符来设置匹配字符串的正则表达式
//使用正则表达式查找type包含 so 字符串的book db.books.find({type:{$regex:"so"}}) //或者 db.books.find({type:/so/})
3.3、更新文档
可以用update命令对指定的数据进行更新,命令的格式如下:
db.collection.update(query,update,options)
- query:描述更新的查询条件
- update:描述更新的动作及新的内容;
- update:描述更新的动作及新的内容;
- upsert: 可选,如果不存在update的记录,是否插入新的记录。默认false,不插入
- multi: 可选,是否按条件查询出的多条记录全部更新。 默认false,只更新找到的第一条记录
- writeConcern :可选,决定一个写操作落到多少个节点上才算成功。
更新操作符
操作符 | 格式 | 描述 |
$set | {$set:{field:value}} | 指定一个键并更新值,若键不存在则创 建 |
$unset | {$unset : {field : 1 }} | 删除一个键 |
$inc | {$inc : {field : value } } | 对数值类型进行增减 |
$rename | {$rename : {old_field_name : new_field_name } } | 修改字段名称 |
$push | { $push : {field : value } } | 将数值追加到数组中,若数组不存在则 会进行初始化 |
$pushAll | {$pushAll : {field : value_array }} | 追加多个值到一个数组字段内 |
$pull | {$pull : {field : _value } } | 从数组中删除指定的元素 |
$addToSet | {$addToSet : {field : value } } | 添加元素到数组中,具有排重功能 |
$pop | {$pop : {field : 1 }} | 删除数组的第一个或最后一个元素 |
更新单个文档
某个book文档被收藏了,则需要将该文档的favCount字段自增
db.books.update({_id:ObjectId("61caa09ee0782536660494d9")},{$inc:{favCount:1}})
更新多个文档
默认情况下,update命令只在更新第一个文档之后返回,如果需要更新多个文档,则可以使用multi选项。
将分类为’novel’的文档增加发布时间(publishedDate)
db.books.update({type:"novel"},{$set:{publishedDate:new Date()}},{"multi":true})
multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件 查出来多条记录全部更新
update命令的选项配置较多,为了简化使用还可以使用一些快捷命令:
- updateOne: 更新单个文档
- updateMany: 更新多个文档
- replaceOne: 替换单个文档
使用upsert命令
upsert是一种特殊的更新,其表现为如果目标文档不存在,则执行插入命令
db.books.update( {title:"my book"}, {$set:{tags:["nosql","mongodb"],type:"none",author:"lili"}}, {upsert:true} )
nMatched、nModified都为0,表示没有文档被匹配及更新,nUpserted=1提示执行了upsert动作
实现replace语义
update命令中的更新描述(update)通常由操作符描述,如果更新描述中不包含任何操作符,那么 MongoDB会实现文档的replace语义
db.books.update( {title:"my book"}, {justTitle:"my first book"} )
如果没有使用操作符的话,这个替换,是对文档的一个替换
findAndModify命令
findAndModify兼容了查询和修改指定文档的功能,findAndModify只能更新单个文档
//将某个book文档的收藏数(favCount)加1 db.books.findAndModify({ query:{_id:ObjectId("62c41650a6a10cd974bac894")}, update:{$inc:{favCount:1}} })
该操作会返回符合查询条件的文档数据,并完成对文档的修改。
默认情况下,findAndModify会返回修改前的旧数据,如果希望返回修改后的数据,则可以指定new选项
db.books.findAndModify({ query:{_id:ObjectId("62c41650a6a10cd974bac894")}, update:{$inc:{favCount:1}}, new: true })
与findAndModify语义相近的命令如下:
- findOneAndUpdate: 更新单个文档并返回更新前(或更新后)的文档
- findOneAndReplace: 替换单个文档并返回替换前(或替换后的文档)
3.4、删除文档
- remove 命令需要配合查询条件使用;
- 匹配查询条件的文档会被删除;
- 指定一个空文档条件会删除所有文档;
示例:
db.user.remove({age:28})// 删除age 等于28的记录 db.user.remove({age:{$lt:25}}) // 删除age 小于25的记录 db.user.remove( { } ) // 删除所有记录 db.user.remove() //报错
remove命令会删除匹配条件的全部文档,如果希望明确限定只删除一个文档,则需要指定justOne参 数,命令格式如下
db.collection.remove(query,justOne)
例如: 删除满足type:novel条件的首条记录
db.books.remove({type:"novel"},true)
使用delete删除文档
官方推荐使用 deleteOne()
和 deleteMany()
方法删除文档,语法格式如下
db.books.deleteMany ({}) //删除集合下全部文档 db.books.deleteMany ({ type:"novel" }) //删除 type等于 novel 的全部文档 db.books.deleteOne ({ type:"novel" }) //删除 type等于novel 的一个文档
注意: remove、deleteMany等命令需要对查询范围内的文档逐个删除,如果希望删除整个集合,则使 用drop命令会更加高效
返回被删除文档
remove、deleteOne等命令在删除文档后只会返回确认性的信息,如果希望获得被删除的文档,则可以 使用findOneAndDelete命令
db.books.findOneAndDelete({type:"novel"})
除了在结果中返回删除文档,findOneAndDelete命令还允许定义“删除的顺序”,即按照指定顺序删除找 到的第一个文档
db.books.findOneAndDelete({type:"novel"},{sort:{favCount:1}})
remove、deleteOne等命令只能按默认顺序删除,利用这个特性,findOneAndDelete可以实现队列的 先进先出。