Mongo

官方网址:https://www.mongodb.com
官方文档:https://docs.mongodb.com
Mongo简介
MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
版本
偶数版本为稳定版本,技术版本为开发版
NoSQL
Not Only SQL,意为不仅仅为SQL。现代计算机系统每天网络上都会产生庞大的数据量。这些数据很大部分由关系型数据库管理系统(RDBMS)处理。1970年 E.F.Codd's提出的关系模型的论文 "A relational model of data for large shared data banks",这使得数据建模和应用程序编程更加简单。
通过应用实践证明,关系模型是非常适合于客户服务器编程,远远超出预期的利益,今天它是结构化数据存储在网络和商务应用的主导技术。
NoSQL通常用于超大规模数据的存储。(例如谷歌或Facebook每天为他们的用户收集万亿比特的数据)。这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展。

RDBMs VS NoSQL
高度组织化、结构化的数据
非结构化和不可预知的数据
结构化查询语言(SQL)
没有声明性的查询语言
数据与关系都存储在表中
k-v存储,列存储,文档存储,图形存储
严格的一致性,ACID
最终一致性,非ACID属性
具有事务
大部分没有事务
高性能,高可用性和可伸缩性
NoSQL的优缺点
高扩展性,架构灵活,半结构化数据
没有标准化
分布式计算
有限的查询功能(到目前为止)
低成本
一致性较差
NoSQL数据库分类
xml数据库
Berkeley DB XML,BaseX
高效存储xml数据,并支持xml内部查询语法,比如XQuery,Xpath
key-value存储
Redis、MemcacheDB、Tokyo Cabine
可以通过key快速查询到对应的value。一般情况下value可以存储任何格式(redis做了拓展)。
对象存储
db4o、Versant
通过类似面向对象语言的语法操作数据库,通过对象的方式存取数据。
文档存储
MongoDB、CouchDB
文档存储一般用类似JSON的格式存储,存储的内容是文档型的。这样也就有机会对某些字段建立索引,实现关系数据库的某些功能
图存储
Neo4J、FlockDB
图形关系的最佳存储。使用传统关系数据库来解决的话性能低下,而且设计使用不方便
列存储
Hbase、Cassandra、Hypertable
按列存储数据的。最大的特点是方便存储结构化和半结构化数据,方便做数据压缩,对针对某一列或者某几列的查询有非常大的IO优势
适用场景
适用场景
网站实时数据
数据缓存
大尺寸、低价值的数据存储:如网盘
高伸缩性场景
文档JSON数据存储
不适用场景
高度事务性系统,比如银行以及会计系统
需要复杂的查询SQL
相关概念
MongoDB将数据存储为一个文档(document
),并以键值(k-v
)结构组成。MongoDB文档格式类似JSON,但是是以二进制形式存储,故称为BSON。

关于MongoDB中的属于概念:
database
database
数据库
table
collection
数据库表/集合
row
document
数据记录行/文档
column
field
数据字段/域
index
index
索引
table joins
表连接,MongoDB不支持
primary key
primary key
主键,MongoDB自动将_id字段设置为主键
安装MongoDB
docker安装
客户端工具
官方命令行工具
以下工具都位于 MongoDB的bin目录下,皆是可执行文件。
mongod
服务端程序,用于启动MongoDB
mongo
客户端程序,连接MongoDB,可执行js
脚本
mongofiles
GridFS
工具,内建的分布式文件系统,用于向 MongoDB 存储、读取文件,mongofiles
支持put、get、list等接口
mongoexport
/mongoimport
数据导入导出程序,支持CSV、JSON
mongodump
/mongorestore
数据备份程序/数据恢复程序,将导出BSON格式,用于定期备份
mongos
数据分片程序,支持数据的横向扩展
mongostat
监视程序,查看MongoDB 实时的增删改查操作的 pqs、以及内存使用、网络吞吐的信息
mongotop
实时查看 MongoDB 在哪些集合上花的读写时间最多,能快速找出实例里的热点集合
mongosniff
抓包工具,直接下载二进制包可能并不包含这个工具,需要下载源码编译出来,mongosniff
可以抓取某个 MongoDB 实例的所有请求及应答数据,对于 MongoDB driver 的开发者非常有帮助,也可以用于一些网络问题的定位
mongoperf
测试磁盘 IO 性能的工具
客户端Driver
Mongo支持10+种语言的客户端,点我看官方文档。所有官方客户端driver都支持MongoDB Connection String URI的方式去连接。
mongodb 的连接串 uri
连接串格式为:
mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]
mongodb://
协议user:password@
可选的用户名与密码host1
至少指定一个host,代表mongodb服务端的地址portx
端口号,可选,缺省值为27017
/database
如果制定了username:password@
,将会连接登录指定的数据库。如果未指定,则会打开test数据库?options
可选项,如果未指定database
,加入此参数时,需要在尾部加入/
,多个options中,需要使用&
或者;
连接。
options的可用选项:
replicaSet=name
验证replica set的名称。 Impliesconnect=replicaSet.
slaveOk=true|false
true:在connect=direct模式下,驱动会连接第一台机器,即使这台服务器不是主。在connect=replicaSet模式下,驱动会发送所有的写请求到主并且把读取操作分布在其他从服务器。false: 在 connect=direct模式下,驱动会自动找寻主服务器. 在connect=replicaSet 模式下,驱动仅仅连接主服务器,并且所有的读写命令都连接到主服务器。
safe=true|false
true: 在执行更新操作之后,驱动都会发送getLastError命令来确保更新成功。(还要参考 wtimeoutMS).false: 在每次更新之后,驱动不会发送getLastError来确保更新成功。
w=n
驱动添加 { w : n } 到getLastError命令. 应用于safe=true。
wtimeoutMS=ms
驱动添加 { wtimeout : ms } 到 getlasterror 命令. 应用于 safe=true.
fsync=true|false
true: 驱动添加 { fsync : true } 到 getlasterror 命令.应用于 safe=true.false: 驱动不会添加到getLastError命令中。
journal=true|false
如果设置为 true, 同步到 journal (在提交到数据库前写入到实体中). 应用于 safe=true
connectTimeoutMS=ms
可以打开连接的时间。
socketTimeoutMS=ms
发送和接受sockets的时间。
可视化连接工具
Navicat For MongoDB: https://www.navicat.com.cn/products/navicat-for-mongodb
Studio 3T:https://studio3t.com/
NoSQL Manager for MongoDB: https://www.mongodbmanager.com/
Robo 3T:https://robomongo.org/
mongod
启动服务端
mongod --dbpath=/d/db/mongodb/data
# dbpath用于指定数据库的磁盘存储路径

默认端口号27017,启动成功后,会在目标路径中生成一堆文件:
mogo shell
数据库操作
// 创建/进入数据库直接使用use
// 只有向数据库中创建collection,数据库才会被创建
use DATABASE_NAME
// 查看所有的数据库
show dbs
// 删除数据库(需要先 use 数据库)
// 如果数据库中没有集合,那么数据库也是删除的
db.dropDatabase()
用户操作
创建用户:
// mongo的用户与数据库关联,如果想要创建可管理所有数据库的用户,需要创建在 admin数据库中。
use admin
db.createUser(
{
user:"pws",
pwd:"pws",
roles:["userAdminAnyDatabase"]
}
)
db.createUser(
{
user:"pws",
pwd:"pws",
roles:["readWrite"]
}
)
修改用户:
db.updateUser("pws",
{
pwd: "pws",
roles: [ "root","userAdminAnyDatabase" ]
}
)
集合操作
// db.createCollection(name, options) 集合名称,可选参数(指定有关内存以及索引的选项)
db.createCollection("person")
// db.createCollection("person", {capped: true, size: 100000})
/*
可选参数:
- `capped` 布尔类型,true代表固定集合,此时必须指定他的固定大小`size`。当固定集合达到最大值时,会自动覆盖最早的文档。
- `size` 代表集合的最大大小,单位字节。如果是固定集合,则必须指定此属性,代表集合的固定大小。
- `max` 代表集合最大长度
- `autoIndexId`如果为true,会自动给`_id`字段创建索引。默认为false ,已废弃
*/
// 插入文档时,会先检查size字段,后检查max字段
// 插入文档会自动创建集合
db.testCollection.insert({"name" : "测试"})
// 没有testCollection,不会报错会创建一个collection
// 删除集合, collection是集合名称
db.collection.drop()
// 显示数据库中的所有集合
show collections
文档操作
插入
插入校验
检查文档基本结构是否正确,如果没有
_id
则自动增加一个id检查当文档是否小于16MB
mongodb校验有限,其他的校验通常在mongo客户端中进行
插入单条
db.foo.insertOne({bar : "baz"})
批量插入
db.foo.insertMany([
{bar: "baz1"},
{bar: "baz2"}
])
一次消息长度最多为48MB
如果插入总共大于48MB的数据,大部分驱动会将其拆分为多个批量插入请求
如果批量插入出现错误,那么错误文档之前的文档,都会被插入成功,后面的都会失败。如果想要忽略错误,需要增加
continueOnError
选项
查询
查询就是返回一个集合中符合条件的文档子集
find:查询所有文档
// 查询集合中的所有文档
db.foo.find()
find:条件查询
// 使用相等条件查询文档
// 查询 age==18 && bar==“张三”的数据
db.foo.find({
age: 18,
bar: "张三"
})
find:$lt
、lte
、$gt
、$gte
// 使用相等条件查询文档
// 查询 age==18 && bar==“张三”的数据
db.foo.find({
age: 18,
bar: "张三"
})
// $lt 小于
// $lte 小于等于
// $gt 大于
// $gte 大于等于
// 查询 age > 2 && age < 18 的数据
db.foo.find({age :{
$gt: 2,
$lt: 18
}})
// 查询age >= 2 && age <= 18 的数据
db.foo.find({age :{
$gte: 2,
$lte: 18
}})
// 日期范围匹配
// 查询2020年1月1
startDate = new Date(2020, 1, 1)
db.foo.find({create_at : {
$gte: startDate
}})
find:$in
// 查询年龄不是18或者19的文档
db.foo.find({
age: {
$not :{
$in: [18, 19]
}
}
})
find:$or
、$and
// 或者
// 查询 age == 18 || age == 19 的数据
db.foo.find({
$or: [ // 条件数组
{age: 18}, // 条件1
{age: 19}, // 条件2
]
})
// 并且
// 查询 age == 18 && bar == "张三" 的数据
db.foo.find({
$and: [ // 条件数组
{age: 18}, // 条件1
{bar: "张三"}, // 条件2
]
})
find:$not
$not
是元语句,可以用在任何其他语句之上
find:过滤返回键
节省传输的数据量,也能减少客户端解码文档的内存消耗
_id
字段始终返回
// 白名单
db.foo.find({
age: 18,
bar: "张三"
}, {
bar: 1 // 只返回bar字段
})
// 黑名单
db.foo.find({
age: 18,
bar: "张三"
}, {
age: 0 // age字段不返回
})
find:返回单条
db.foo.find({
age: 18,
bar: "张三"
})
查询文档大小
Object.bsonsize(文档)
删除
删除是永久性的,不可撤销,也不可能恢复
删除单条符合条件的文档
db.foo.deleteOne({
bar: "baz"
})
删除多条符合条件的文档
db.foo.deleteMany({
bar: "baz"
})
删除集合中的所有文档
db.foo.deleteMany()
不如使用
db.foo.drop()
更快更方便
更新
如果有两个客户端同时发送更新请求,先到达服务器的一方先执行,接着执行另一个
并发操作下是原子的,不会破坏文档
update
需要传入两个参数,第一个是查询文档,第二个是修改器文档更新修改器是原子性的,用于指定复杂的更新操作,比如修改、增加、删除键,操作数组、数字、或者内嵌文档
文档替换
使用新文档完全替换匹配的文档,replaceOne
:
db.user.drop()
db.user.insertOne({
name: "joe",
friends: 32,
enimies: 2,
})
var joe = db.user.findOne({name: "joe"})
joe.relationships = {
friends: joe.friends,
enimies: joe.enimies,
}
delete joe.friends
delete joe.enimies
joe.username = joe.name
delete joe.name
delete joe._id
db.user.replaceOne({name: "joe"}, joe)
db.user.findOne({username: "joe"})
/*
// 上述文档被替换为:
{
"_id": {"$oid": "6167eb1a64474624ff16ccff"},
"relationships": {
"friends": 32,
"enimies": 2
},
"username": "joe"
}*/
更新单条
更新多条
$set
和unset
$set
用于修改字段的值甚至是类型,如果字段不存在,则创建该字段;而$unset
用于删除某个字段。
db.user.drop()
db.user.insertOne({
username: "张三",
age: 19,
hobby: "🏀"
})
db.user.updateOne({username: "张三"}, {$set: {hobby: ["⚽️", "🏀", "🏓"]}, $unset: {age: 1}})
db.user.find({username: "张三"})
所得结果:
{
"_id": {"$oid": "6167f4ba93e9ab74824731d2"},
"hobby": ["⚽️", "🏀", "🏓"],
"username": "张三"
}
$inc
$inc
修改器用于增加键的值,如果键不存在就会创建。它可以用于整型、长整型、双精度浮点型的值,并且值必须为数字,不可是数字字符串,如果不是合法的值,将会造成操作失败。
db.analytics.drop()
db.analytics.insertOne({
url: "baidu.com",
pageviews: 0
})
// 增加百度的访问量
db.analytics.updateOne({
url: "baidu.com"
}, {
$inc: {pageviews: 1}
})
数组修改器
$push
末尾增加单个元素:
db.blog.drop()
db.blog.posts.insertOne({
title: "博客标题",
content: "我是文章内容"
})
// $push 添加一个元素
db.blog.posts.updateOne({title: "博客标题"}, {
$push: {
comments: {
name: "joe",
email: "joe.example.com",
content: "nice post"
}
}
})
db.blog.posts.findOne({title: "博客标题"})
结果:
[
{
"_id": {"$oid": "616939af2f37325a7eb4c623"},
"comments": [
{
"name": "joe",
"email": "joe.example.com",
"content": "nice post"
}
],
"content": "我是文章内容",
"title": "博客标题"
}
]
$each
+ $push
一次在末尾添加多个值:
db.blog.posts.drop()
db.blog.posts.insertOne({
title: "博客标题",
content: "我是文章内容"
})
// $each + $push 添加多个元素
// 向该博客添加两条评论
db.blog.posts.updateOne({title: "博客标题"}, {
$push: {
comments: {
$each: [
{
name: "joe",
email: "joe.example.com",
content: "nice post"
},
{
name: "lily",
email: "lily.example.com",
content: "nice post2"
},
]
}
}
})
db.blog.posts.findOne({title: "博客标题"})
结果:
[
{
"_id": {"$oid": "61693cb72f37325a7eb4c62d"},
"comments": [
{
"name": "joe",
"email": "joe.example.com",
"content": "nice post"
},
{
"name": "lily",
"email": "lily.example.com",
"content": "nice post2"
}
],
"content": "我是文章内容",
"title": "博客标题"
}
]
$slice
+ $push
$slice
的作用类似数组切片,指定一个数值,如果是正整数,则对数组的[0, n)
进行切片,如果值为负整数,则对数组从后往前进行切片,也就是(length-1-n, length-1]
索引
如何选择需要建立索引的字段?
如何强制使用索引?如何评估索引的效率?
创建索引和删除索引
什么是索引?为什么需要索引?
索引类似书的索引,记录某个数据的位置,不使用索引的查询称为全表扫描,全表扫描在搜寻某个字段时,会依次遍历表中的所有记录并查询该条记录的字段。所以当单张表数据很多时,这个操作将会非常耗时,所以需要使用索引解决这类问题。
索引可以根据给定的字段组织数据,让MongoDB能够非常快地找到目标文档。
db.user.drop()
for (let i = 0; i < 100000; i++) {
db.user.insertOne({
userid: 1,
username: "name" + i
})
}
db.user.find({userid: 99999}).limit(1).explain()
// 创建索引
db.users.ensureIndex({"username": 1})
// 如果集合数量很大或者机器性能较差,索引可能创建的很慢,可以使用如下方式查询索引的创建进度
db.currentOp()
最后更新于
这有帮助吗?