MongoDB是为快速开发互联网Web应用而设计的数据库系统。 MongoDB的设计目标是极简、灵活、作为Web应用栈的一部分 MongoDB的数据模型是面向文档的,简单理解MongoDB这个数据库中存的各种各样的JSON。(BSON)
优点
Free-schema无模式文档,适应非结构化数据存储;
内置GridFS,支持大容量的存储;
内置Sharding,分片简单
弱一致性(最终一致),更能保证用户的访问速度;
查询性能优越,对于千万级别的文档对象,差不多10个G,对有索引的ID的查询不会比MySQL慢,而对非索引字段的查询,则是完胜MySQL;
聚合框架,它支持典型几种聚合操作 , 比如,Aggregate pipelien, Map-Reduce等;支持自动故障恢复缺点
MongoDB目前只支持单文档事务,MongoDB暂时不适合需要复杂事务的场景。 灵活的文档模型JSON格式存储最接近真实对象模型,对开发者友好,方便快速开发迭代,可用复制集满足数据高可靠、高可用的需求,运维较为简单、故障自动切换可扩展分片集群海量数据存储。
传统的关系型数据库在解决三高问题上的力不从心。 何为三高?
High performance - 对数据库高并发读写的需求。
Huge Storage - 对海量数据的高效率存储和访问的需求。
High Scalability && High Availability- 对数据库的高可扩展性和高可用性的需求。
MongoDB可以完美解决三高问题。
游戏场景
使用MongoDB存储游戏用户信息、装备、积分等,直接以内嵌文档的形式存储,方便查询、更新。
物流场景
使用MongoDB存储订单信息、订单状态、物流信息,订单状态在运送过程中飞速迭代、以MongoDB内嵌数组的形式来存储,一次查询就能将订单所有的变更查出来
社交场景
使用MongoDB存储用户信息,朋友圈信息,通过地理位置索引实现附近的人、定位功能。
物联网场景
使用MongoDB存储设备信息、设备汇报的日志信息、并对这些信息进行多维度分析。
视频直播
使用MongoDB存储用户信息、点赞互动信息
数据量大 读写操作频繁 数据价值较低,对事务要求不高
MongoDB与MySQL关键字对比
| MySQL | MongoDB | 解释说明 |
|---|---|---|
| database | database | 数据库 |
| table | collection | 表/集合 |
| row | document | 行/文档 |
| column | field | 自带/域 |
| index | index | 索引 |
| join | 嵌入文档 | 表关联/MongoDB不支持join,MongoDB通过嵌入式文档来替代多表连接 |
| primary key | primary key | 主键/MongoDB自动将 id字段设置为主键 |
数据库(database)
数据库是一个仓库,在仓库中可以存放集合
集合(collection)
集合类似数组,在集合中可以存放文档,相当于 MySQL 的 table。
文档(document)
文档是数据库中最小单位,我们存储和操作的都是文档,相当于 MySQL 的 row
field: 数据域,相当于 MySQL 的 column。
index: 索引。
primary key: 主键。
MongoDB 预留了几个特殊的 database
admin: admin 数据库主要是保存 root 用户和角色。例如,system.users 表存储用户,system.roles 表存储角色。一般不建议用户直接操作这个数据库。将一个用户添加到这个数据库,且使它拥有 admin 库上的名为 dbAdminAnyDatabase 的角色权限,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如关闭服务器。
local: local 数据库是不会被复制到其他分片的,因此可以用来存储本地单台服务器的任意 collection。一般不建议用户直接使用 local 库存储任何数据,也不建议进行 CRUD 操作,因为数据无法被正常备份与恢复。
config: 当 MongoDB 使用分片设置时,config 数据库可用来保存分片的相关信息。
一个 MongoDB 实例的数据结构如下图:

MongoDB 集合存在于数据库中,没有固定的结构,可以往集合插入不同格式和类型的数据。集合不需要事先创建。当第一个文档插入,或者第一个索引创建时,集合就会被创建。集合名必须以下划线或者字母符号开始,并且不能包含 $,不能为空字符串(比如 “”),不能包含空字符,且不能以 system. 为前缀。
capped collection 是固定大小的集合,支持高吞吐的插入操作和查询操作。它的工作方式与循环缓冲区类似,当一个集合填满了被分配的空间,则通过覆盖最早的文档来为新的文档腾出空间。和标准的 collection 不同,capped collection 需要显式创建,指定大小,单位是字节。capped collection 可以按照文档的插入顺序保存到集合中,而且这些文档在磁盘上存放位置也是按照插入顺序来保存的,所以更新 capped collection 中的文档,不可以超过之前文档的大小,以便确保所有文档在磁盘上的位置一直保持不变。
视图基于已有的集合进行创建,是只读的,不实际存储硬盘,通过视图进行写操作会报错。视图使用其上游集合的索引。由于索引是基于集合的,所以你不能基于视图创建、删除或重建索引,也不能获取视图的索引列表。如果视图依赖的集合是分片的, 那么视图也视为分片的。视图是实时计算并读取的。
MongoDB 支持丰富的索引方式。如果没有索引,读操作就必须扫描集合中的每个文档并筛选符合查询条件的记录。索引能够在很大程度上提高查询速度
单字段索引:有三种方式,(1)在单个字段上创建索引;(2)在嵌入式字段上创建索引;(3)在内嵌文档上创建索引。
复合索引:支持在多个字段上匹配的查询。对任何复合索引施加 32 个字段的限制。对于复合索引,MongoDB 可以使用索引来支持对索引前缀的查询。
多键索引:为了索引包含数组值的字段,MongoDB 为数组中的每个元素创建一个索引键。这些多键索引支持对数组字段的高效查询。
文本索引:支持对字符串内容的文本搜索查询。文本索引可以包含任何值为字符串或字符串元素数组的字段。一个集合最多可以有一个文本索引。
通配符索引:支持针对未知或任意字段的查询。例如:db.collection.createIndex( {“a.$**” : 1 } ) 可支持诸如 db.collection.find({ “a.b” : 1 })、db.collection.find({ “a.c” : { $lt : 2 } }) 等查询,提高查询效率。不能使用通配符索引来分片集合。不能为通配符创建复合索引。
通配符文本索引:通配符文本索引不同于通配符索引。通配符索引不支持使用KaTeX parse error: Expected '}', got 'EOF' at end of input: …reateIndex( { "**": “text” } )。
2dsphere 索引:支持球体上的地理空间查询:包含、相交和邻近度查询。
hashed 索引:支持使用哈希的分片键进行分片。基于哈希的分片使用字段的散列索引作为分片键,以便跨分片集群对数据进行分区。MongoDB 支持任何单个字段的哈希索引,但不支持创建具有多个哈希字段的复合索引,也不能在索引上指定唯一哈希索引。
ttl 索引:一种特殊的单字段索引,支持在一定的时间或特定的期限后自动从集合中删除文档。TTL索引不能保证过期数据在过期时立即删除。默认每60秒运行一次删除过期文档的后台进程。capped collection 不支持 ttl 索引。
唯一索引:确保索引字段不会存储重复值。如果集合已经存在了违反索引的唯一约束的文档,则后台创建唯一索引会失败。
部分索引:只索引集合中满足指定筛选器表达式的文档。例如:db.collection.createIndex({ a:1 },{ partialFilterExpression: { b: { $lt: 100 } } }) 表示只对集合中 b 字段小于 100 的文进行索引,大于等于 100 的文档不会被索引。这可以有效提高存储效率。
稀疏索引:只包含有索引字段的文档的条目,即使索引字段包含空值。索引会跳过任何缺少索引字段的文档。非稀疏索引包含集合中的所有文档,为那些不包含索引字段的文档存储空值。
ObjectId 可以快速生成并排序,长度为 12 个字节,包括:
一个 4 字节的时间戳,表示 unix 时间戳 5 字节随机值 3 字节递增计数器,初始化为随机值 在 MongoDB 中,存储在集合中的每个文档都需要一个唯一的 _id 字段作为主键。如果插入的文档省略了 _id 字段,则自动为文档生成一个 _id。
可以为 mongod 实例启用数据库分析。数据库分析器既可以在实例上启用,也可以在单个数据库层面上启用。它收集在实例上执行的 CRUD 操作、游标、命令、配置等详细信息,并将它收集的所有数据写到 system.profile 集合。这是一个capped collection,默认情况下,system.profile 容量大小为 4M。开启实时数据库分析往往伴随着副作用,请谨慎使用。 使用 db.currentOp() 操作。它返回一个文档,其中包含有关数据库实例正在进行的操作的信息。 使用 db.serverStatus() 命令。它返回一个文档,提供数据库状态的概述,通过它可以收集有关该实例的统计信息。 使用 explain 来评估查询性能,例如 cursor.explain() 或 db.collection.explain() 方法可以用来返回关于查询执行的信息。 借用一些商业工具,比如 MongoDB Ops Manager、Percona 等。
官方网站有关于这个问题的说明(使用大量的集合)。默认情况下,mongodb的每个数据库的命名空间保存在一个16 mb的营收文件中,平均每个命名占用约628字节,也即整个数据库的命名空间的上限约为24000 .
每一个集合,索引都将占用一个命名空间,所以,如果每个集合有一个索引(比如默认的_id索引),那么最多可以创建12000个集合。如果索引数更多,则可创建的集合数就更少了,同时,如果集合数太多,一些操作也会变慢。
不过,如果真的需要建立更多的集合的话,mongodb也是支持的,只需要在启动时加上”——nssize“参数,这样对应数据库的命名空间文件就可以变得更大以便保存更多的命名。这个命名空间文件(。ns文件)最大可以为2 g,也就是说最大可以支持约340多万个命名,如果每个集合有一个索引的话,最多可创建约170多万个集合。
还需要注意,——nssize只设置新创建的新文件的大小,如果想改变已经存在的数据库的命名空间,在使用这个参数启动后,还需要运行db.repairDatabase()命令来调整尺寸。
MongoDB的版本偶数版本为稳定版,基数版本为开发板 MongoDB对于32位系统支持不佳,所以3.2版本以后没有再对32位系统的支持
Read:允许用户读取指定数据库
readWrite:允许用户读写指定数据库
dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile
userAdmin:允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户
clusterAdmin:只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限。
readAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读权限
readWriteAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读写权限
userAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限
dbAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限。
root:只在admin数据库中可用。超级账号,超级权限
使用Robo Studio 3T连接mongoDB 进入Studio 3T官网下载Studio 3T:https://studio3t.com/download/
下载:https://download.studio3t.com/studio-3t/windows/2022.9.0/studio-3t-x64.zip 参考:Robo 3T 使用教程
idea使用mongoDB plugin插件 参考:idea中安装mongodb插件 IntelliJ IDEA 配置MongoDB插件、连接MongoDB
使用服务端webadminmongo创建 服务器mongo可视化管理工具安装
mongodb://admin:123456@10.0.0.100:27017参数说明:
criteria:查询条件
objNew:update对象和一些更新操作符
upsert:如果不存在update的记录,是否插入objNew这个新的文档,true为插入,默认为false,不插入。
multi:默认是false,只更新找到的第一条记录。如果为true,把按条件查询出来的记录全部更新.
上面的update语句中:
{}:是过滤条件。(更新哪些文档);
{’$rename’: {‘raw_field’: 'new_field}}:是更新操作;
false:若根据该过滤条件无法找到匹配的文档时,不插入该文档。
true:更新多条(但是我在用这个的时候不太好使,还是用的updateMany来更新的多条)。
db.url.update({}, {$unset:{'content':''}},false, true)当我们向集合插入一个或多个文档时,如果没有指定_id,则mongodb会自动生成,该属性用来作为文档的唯一标识
# 插入一条数据
db.collectionName.insertOne( {name:'liu'}
通过数组批量插入
指定自定义id
db.collectionName.insertOne( {_id:"xxxx",name:'liu'} )查询数据
db.collectionName.find() 或db.collectionName.find({})
查询集合所有的文档,即所有的数据。
查询到的是整个数组对象。在最外层是有一个对象包裹起来的。
db.collectionName.find({_id:222}db.collectionName.findOne() 返回的是查询到的对象数组中的第一个对象注意:
db.students.find({_id:222}).name //错误
db.students.findOne({_id:222}).name //正确db.collectionName.count()或db.collectionName.length() 统计文档个数# 查询所有的name
db.user.distinct("name")mongoDB可以使用的类型如下表所示:
太吃内存,快是有原因的,因为MongoDB把数据都放内存里了;
不支持事务操作;
占用空间过大;
不支持联表查询;
只有最终一致性,言外之意,就是可能造成数据的不一致,如果想要保持强一致性,必须在一个服务器处理所有的读写操作,坑;
复杂聚合操作通过mapreduce创建,速度慢
Mongodb全局锁机制也是个坑;
预分配模式会带来的磁盘瓶颈;
删除记录时不会释放空间,相当于逻辑删除,这个真的坑;
MongoDB到现在为止,好像还没有太好用的客户端工具;docker run -d \
--name mongodb \
--restart always \
--privileged \
-p 27017:27017 \
-v /usr/local/mongodb/data:/data/db \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=123456 \
mongo mongod --auth
-d: 后台运行容器;
–name: 指定容器名;
-p: 指定服务运行的端口;
-v: 映射目录或文件;
–privileged 拥有真正的root权限
–restart=always Docker服务重启容器也启动
-e MONGO_INITDB_ROOT_USERNAME=admin 指定用户名
-e MONGO_INITDB_ROOT_PASSWORD=123456 指定密码
mongod --auth :容器默认启动命令是mongod,我们认证需要修改启动命为mongod --auth开启认证# 以admin身份进入mongoDB
docker exec -it (docker ps 镜像id) mongo admin
# 创建一个admin账号
db.createUser({ user: 'root', pwd: 'root', roles: [ { role: "userAdminAnyDatabase", db: "admin" } ] });
# 身份认证
db.auth("root","root");
# 新建数据库
use iot
# 创建 用户、密码和数据库:
db.createUser({ user: 'iot_admin', pwd: 'iot_admin', roles: [ { role: "dbAdmin", db: "iot" },{ role: "readWrite", db: "iot" } ] });
exit
[root@localhost mongodb]# docker exec -it mongodb bash
root@ce90018683a8:/# mongo --versiondocker run -itd --name mongoadmin --network mynet -p 8082:8082 -e HOST=0.0.0.0 -e PORT=8082 mrvautin/adminmongodb.collection.update(criteria,objNew,upsert,multi)
# demo
db.device.update({}, {$set: {projectId:"1584383990662078466"}}, {multi: 1})
# 1 、添加一个字段. table 代表表名 , 添加字段 content,字符串类型。
db.table.update({}, {$set: {content:""}}, {multi: 1})db.getCollection("device").update({}, {$rename : {"code":"deviceCode", "name":"deviceName"}}, false, true)
xx版本之后使用update和updateOne只能更新满足条件的一条文档。
例子,将原来的字段名raw_field修改成新的字段名new_field。
db.Test.update({}, {'$rename': {'raw_field': 'new_field}}, false, true)
# 或者
db.Test.updateOne({}, {'$rename': {'raw_field': 'new_field}}, false)# 查看所有的数据库
show dbs 或show databases
# 切换到指定的数据库
use xxx
# 查看当前操作的数据库
db
# 查看当前数据库中所有的集合
show collectionsdb.collectionName.insert( {name:'liu5'} )
db.collectionName.insert( [ {name:'liu5'} , {name:'liu6'} ] )
db.user insert({
"_id": "11",
"name":"zhangsan",
"age":18,
"hobbies":["music","read"],
"addr":{
"country":"China",
"city":"BJ"
}
})## 插入多条数据
db.collectionName.insertMany( [ {name:'liu5'} , {name:'liu6'} ] )
db.collectionName.insert( [ {name:'liu5'} , {name:'liu6'} ] )
需要用数组包起来
万能API:db.collectionName.insert()
#添加两万条数据
for(var i=0;i<20000;i++){
db.users.insert({username:'liu'+i}) #需要执行20000次数据库的添加操作
}
db.users.find().count()//20000# 优化:
var arr=[];
for(var i=0;i<20000;i++){
arr.push({username:'liu'+i})
}
db.user.insert(arr) #只需执行1次数据库的添加操作,可以节约很多时间# 1.mongodb支持直接通过内嵌文档的属性值进行查询
# 什么是内嵌文档:hobby就属于内嵌文档
{
name:'liu',
hobby:{
movies:['movie1','movie2'],
cities:['zhuhai','chengdu']
}
}
db.users.find({hobby.movies:'movie1'}) //错误
db.users.find({"hobby.movies":'movie1'})//此时查询的属性名必须加上引号#比较操作符
$gt 大于
$gte 大于等于
$lt 小于
$lte 小于等于
$ne 不等于
$eq 等于的另一种写法
db.users.find({num:{$gt:200}}) #大于200
db.users.find({num:{$gt:200,$lt:300}}) #大于200小于300
$or 或者
db.users.find(
{
$or:[
{num:{$gt:300}},
{num:{$lt:200}}
]
}
) #大于300或小于200db.users.find().skip(页码-1 * 每页显示的条数).limit(每页显示的条数)
db.users.find().skip(pageIndex-1 * pageSize).limit(pageSize)
db.users.find().limit(10) #前10条数据
db.users.find().skip(50).limit(10) #跳过前50条数据,即查询的是第61-70条数据,即第6页的数据db.emp.find().sort({sal:1}) #1表示升序排列,-1表示降序排列
db.emp.find().sort({sal:1,empno:-1}) #先按照sal升序排列,如果遇到相同的sal,则按empno降序排列
#注意:skip,limit,sort可以以任意的顺序调用,最终的结果都是先调sort,再调skip,最后调limit
#5.设置查询结果的投影,即只过滤出自己想要的字段
db.emp.find({},{ename:1,_id:0}) #在匹配到的文档中只显示ename字段$and,$or,$not
# 1)、select * from user where id>=3 and id<=4
db.user.find({"_id":{"$gte":3, "$lte":4}})
# 2)、select * from user where id >= 3 and id <= 4 and age >= 4
db.user.find({
"_id":{"$gte":3, "$lte":4}},
"age":{"$gte":4}
)
# 另一种写法
db.user.find({
"$and":[
{"_id":{"$gte":3, "$lte":4}},
{"age":{"$gte":4}}
]
})
# 3)、select * from user where id >= 0 and id <= 1 or id >= 4 or name = 'tianqi'
db.user.find({
"$or":[
{"_id":{"$gte":0, "$lte":1}},
{"_id":{"$gte":4}},
{"name":"tainqi"}
]
})
# 另一种写法
db.user.find({
"$or":[
"$and":[
{"_id":{"$gte":0}},
{"_id":{"$lte":1}}
],
{"_id":{"$gte":4}},
{"name":"tainqi"}
]
})
# 4)、select * from user where id % 2 = 1
db.user.find({"_id":{"$mod":[2,1]}})
# 5)、上一条取反
db.user.find({"_id":{"$not":{"$mod":[2,1]}}})$in $nin
# 1)、select * from user where age in (1,2,3)
db.user.find("age":{"$in":[1,2,3]});
# 1)、select * from user where name not in ('zhangsan', 'lisi')
db.user.find("age":{"$nin":["zhangsan", "lisi"]});