介绍
MongoDB是一个文档数据库(以 JSON 为数据模型)
,由C++语言编写,旨在为WEB应用提供可扩展
的高性能数据存储解决方案。
MongoDB是一个介于关系数据库和非关系数据库
之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,数据格式是BSON
,一种类似JSON的二进制形式的存储格式,简称Binary JSON
,和JSON一样支持内嵌的文档对象和数组对象,因此可以存储比较复杂的数据类型。
Mongo最大的特点是它支持的查询语言非常强大,还支持对数据建立索引。原则上 Oracle 和 MySQL 能做的事情,MongoDB 都能做(包括 ACID 事务)。
MongoDB是一个开源OLTP数据库
,它灵活的文档模型(JSON)非常适合敏捷式开发、高可用和水平扩展的大数据应用。
OLTP:on-line Transaction Processing,联机(在线)事务处理
OLAP:on-line Analytical Processing,联机(在线)分析处理
MongoDB vs 关系型数据库
概念对比
- 数据库(database):可以理解为逻辑上的名称空间,一个数据库包含多个集合。
- 集合(collection)--> 表(table),一个集合可以存放多个不同的文档。
- 文档(document)--> 行(row),一个文档可以存多个字段。
- 字段(field)--> 列(column):文档中的一个属性。
- 索引(index):独立的检索式数据结构,与SQL概念一致。
_id
:每个文档中都拥有一个唯一的_id字段,相当于SQL中的主键primary key
。- 视图(view):可以看作一种虚拟的(非真实存在的)集合,与SQL中的视图类似。从
MongoDB 3.4
版本开始提供了视图功能,通过聚合管道技术实现。 - 聚合操作($lookup):MongoDB用于实现“类似”表连接(tablejoin)的聚合操作符。
差异
半结构化:在一个集合中,文档的字段不需要完全相同,并且不需要提前声明;文档还支持多级嵌套
、数组
等数据结构
弱关系:没有外键约束
应用场景
没有某个业务场景必须要使用MongoDB才能解决,但使用MongoDB通常能让你以更低的成本解决问题。
只要有一项需求满足就可以考虑使用MongoDB,匹配越多,选择MongoDB越合适。
实操
安装
版本:4.4.X
#下载MongoDB
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.4.9.tgz
tar -zxvf mongodb-linux-x86_64-rhel70-4.4.9.tgz
#进入mongodb目录,启动mongodb服务
mkdir data log
bin/mongod --port=27017 --dbpath=data --logpath=log/mongodb.log --bind_ip=0.0.0.0 --fork
利用配置文件启动服务,编辑/mongodb/conf/mongo.conf文件,内容如下:
systemLog:
destination: file
path: /mongodb/log/mongod.log # log path
logAppend: true
storage:
dbPath: /mongodb/data # data directory
engine: wiredTiger #存储引擎
journal: #是否启用journal日志
enabled: true
net:
bindIp: 0.0.0.0
port: 27017 # port
processManagement:
fork: true
启动
mongod -f /mongodb/conf/mongo.conf
关闭服务
bin/mongod --port=27017 --dbpath=data --shutdown
Mongo shell
基于JavaScript语法,为系统管理员提供了强大的界面,并为开发人员提供了直接测试数据库查询和操作的方法。
bin/mongo --port=27017
bin/mongo localhost:27017
常用命令
命令 | 说明 |
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() | 查看数据库版本 |
创建集合
db.createCollection("emp")
db.createCollection(name, options)
字段 | 类型 | 描述 |
capped | 布尔 | (可选)如果为true,则创建固定集合 。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。 |
size | 数值 | (可选)为固定集合指定一个最大值(以字节计)。如果 capped 为 true,也需要指定该字段。 |
max | 数值 | (可选)指定固定集合中包含文档的最大数量。 |
安全认证
# 设置管理员用户名密码需要切换到admin库
use admin
#创建管理员
db.createUser({user:"zhkai",pwd:"123456",roles:["root"]})
#显示所有用户
db.system.users.find()
db.dropUser("zhkai")
#删除当前数据库所有用户
db.dropAllUser()
# 认证
db.auth(账户名,密码)
常用权限
权限名 | 描述 |
read | 允许用户读取指定数据库 |
readWrite | 允许用户读写指定数据库 |
dbAdmin | 允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile |
dbOwner | 允许用户在指定数据库中执行任意操作,增、删、改、查等 |
userAdmin | 允许用户向system.users集合写入,可以在指定数据库里创建、删除和管理用户 |
clusterAdmin | 只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限 |
readAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的读权限 |
readWriteAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的读写权限 |
userAdminAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的userAdmin权限 |
dbAdminAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限 |
root | 只在admin数据库中可用。超级账号,超级权限 |
db.grantRolesToUser( "zhkai" , [
{ role: "clusterAdmin", db: "admin" } ,
{ role: "userAdminAnyDatabase", db: "admin"},
{ role: "readWriteAnyDatabase", db: "admin"}
])
启用鉴权之后,连接MongoDB的相关操作需要提供身份认证
# 鉴权模式启动
bin/mongod --port=27017 --dbpath=data --logpath=log/mongodb.log --bind_ip=0.0.0.0 --fork --auth
# 连接
mongo 192.168.65.174:27017 -u zhakai -p 123456 --authenticationDatabase=admin
文档
增
增加单个文档
insertOne: 支持writeConcern,决定一个写操作落到多少个节点上才算成功
db.products.insertOne(
{ "item": "envelopes", "qty": 100, type: "Self-Sealing" },
{ writeConcern: { w : "majority", wtimeout : 100 } }
);
insert: 若插入的数据主键已经存在,则会抛 DuplicateKeyException 异常,提示主键重复,不保存当前数据。
save: 如果 _id 主键存在则更新数据,如果不存在就插入数据
writeConcern取值
0
,发起写操作,不关心是否成功;1
, 写操作需要被写入备份节点才算成功majority
,写操作需要被复制到大多数节点上才算成功。
增加多个文档
db.collection.insertMany(
{ [ <document 1> , <document 2>, ... ] },
{
writeConcern: <document>,
ordered: <boolean>
}
)
writeConcern, 写入策略,默认为 1,即要求确认写操作,0 是不要求
ordered,指定是否按顺序写入,默认 true,按顺序写入
脚本插入
编辑脚本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("books.js")
删除
remove
db.user.remove({age:28})// 删除age 等于28的记录
db.user.remove({age:{$lt:25}}) // 删除age 小于25的记录
db.user.remove( { } ) // 删除所有记录
db.user.remove() //报错
delete(官方推荐)
db.books.deleteMany ({}) //删除集合下全部文档
db.books.deleteMany ({ type:"novel" }) //删除 type等于 novel 的全部文档
db.books.deleteOne ({ type:"novel" }) //删除 type等于novel 的一个文档
返回被删除文档 remove、deleteOne等命令在删除文档后只会返回确认性的信息,如果希望获得被删除的文档,则可以使用findOneAndDelete命令
db.books.findOneAndDelete({type:"novel"})
db.books.findOneAndDelete({type:"novel"},{sort:{favCount:1}}) # 可以实现队列的先进先出。
更新
db.collection.update(
<query>, # 查询条件
<update>, # 更新内容
{
upsert: <boolean>, # 如果不存在更新的记录,是否插入
multi: <boolean>, # 是否更新所有符合条件的记录,默认false,只更新找到的第一条
writeConcern: <document> # 写操作到多少个节点才算成功
}
)
updateOne:更新单个文档。
updateMany:更新多个文档。
replaceOne:替换单个文档
操作符 | 格式 | 描述 |
$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 }} | 删除数组的第一个或最后一个元素 |
db.books.update(
{title:"my book"},
{$set:{tags:["nosql","mongodb"],type:"none",author:"fox"}},
{upsert:true}
)
Replace update中如果没有操作符,就是替换查询的文档
db.books.update(
{title:"my book"},
{justTitle:"my first book"}
)
findAndModify兼容了查询和修改指定文档的功能,findAndModify只能更新单个文档,默认情况下,findAndModify会返回修改前的“旧”数据。如果希望返回修改后的数据,则可以指定new选项
db.books.findAndModify({
query:{_id:ObjectId("61caa09ee0782536660494dd")},
update:{$inc:{favCount:1}},
new: true
})
查询
db.collection.find( <query filter>, <projection> ).pretty()
query, 指定查询条件,key:value对
projection, 指定返回字段,1,包含该字段;0,排除该字段
db.books.find({"tag": "mongodb"}, {"title":1})
db.books.findOne({"tag": "mongodb"}, {"title":1}) # 查第一条文档
如果查询返回的条目数量较多,mongo shell则会自动实现分批显示。默认情况下每次只显示20条,可以输入it命令读取下一批。
条件查询
查询条件对照表
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: 匹配全部条件
排序
#指定按收藏数(favCount)降序返回
db.books.find({type:"travel"}).sort({favCount:-1})
1,升序, -1,降序
分页
skip
用于指定跳过记录数,limit
则用于限定返回结果数量。可以在执行find命令的同时指定skip、limit参数,以此实现分页的功能
# 查第3页数据
db.books.find().skip(8).limit(4)
巧分页 数据量大的时候,应该避免使用skip/limit形式的分页。
替代方案:使用查询条件+唯一排序条件;
例如:
第一页:db.posts.find({}).sort({_id: 1}).limit(20);
第二页:db.posts.find({_id: {$gt: <第一页最后一个_id>}}).sort({_id: 1}).limit(20);
第三页:db.posts.find({_id: {$gt: <第二页最后一个_id>}}).sort({_id: 1}).limit(20);
处理分页问题 – 避免使用 count
尽可能不要计算总页数,特别是数据量大和查询条件不能完整命中索引时。
考虑以下场景:假设集合总共有 1000w 条数据,在没有索引的情况下考虑以下查询:
db.coll.find({x: 100}).limit(50); db.coll.count({x: 100});
正则表达式
//使用正则表达式查找type包含 so 字符串的book
db.books.find({type:{$regex:"so"}})
或者
db.books.find({type:/so/})
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第3天,点击查看活动详情