侧边栏壁纸
博主头像
枕头下放双臭袜子博主等级

今我何功德,曾不事农桑

  • 累计撰写 166 篇文章
  • 累计创建 32 个标签
  • 累计收到 0 条评论

MongoDB学习笔记

枕头下放双臭袜子
2022-01-12 / 0 评论 / 0 点赞 / 283 阅读 / 9,862 字 / 正在检测是否收录...

0、NoSQL简

由于关系型数据库的范式约束、事务特性、磁盘IO等特点,如果服务器使用关系型数据库,当有大量数据产生时,传统的关系型数据库已经无法满足快速查询与插入数据的需求。NoSql的出现解决了这一危机。他通过降低数据的安全性,减少对事务的支持,减少对复杂查询的支持,来获取性能上的提升。但是在某些特定的场景下,NoSql仍然不是最佳的数据库选择,比如一些绝对要有事务与安全指标的场景。

NoSQLRDBMS关系型数据库管理系统
代表着不仅仅是SQL高度组织化结构化数据
没有声明性查询语言(每一个NoSQL都有自己对应的操作语句,最终学习的其实是编程语言对应的该SQL的API)结构化查询语言sql语句
没有预定义的模式数据和关系都存储在单独的表中
键值对存储、列存储、文档存储、图形数据库数据操纵语言、数据定义语言
最终一致性,而非ACID()属性严格一致性
非结构化和不可预知的数据基础事务
CAP定理(CAP最多只能保持两个,要么cp、要么ap)C一致性A可用性P分区容错性
高性能、高可用性和可伸缩性(比如redis性能不够了,搭主从性能立马翻倍,搭集群立马又会性能翻倍。而mysql单纯搭集群并不会使性能翻倍)

0.1 NoSQL数据库四大家族

1、键值对(key-value)存储

特点:键值数据库就像传统语言中使用的哈希表。通过key添加、查询或者删除数据
优点:查询速度快
缺点:数据无结构化
应用场景:内容缓存、用户信息如会话、配置信息、购物车等,主要用于处理大量数据的高访问负载
NoSQL代表:Redis、Memcached

2、文档(Document-Oriented)存储

特点:文档数据库将数据以文档的形式存储,类似json,是一系列数据项的集合。每个数据项都有一个名称与对应的值,值既可以是简单的数据类型,如字符串、数字和日期等;也可以是复杂的类型,如关联对象和有序列表
优点:数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义表结构
缺点:查询性能不高,缺乏统一的查询语法
应用场景:日志、Web应用的
NoSQL代表:MongoDB、CouchDB

3、列(Wide Column Store/Column-Family)存储

特点:列存储数据库将数据存储在列族(Column Family)中,将多个列聚合成一个列族,键仍然存在,但是它们的特点是指向了多个列。举个例子,如果有一个Persion类,我们通常会一起查询它们的姓名和年龄而不是薪资。这种情况下,姓名和年龄就会被放入一个列族中,而薪资则在另一个列族中
优点:列存储查找速度快,可扩展性强,更容易进行分布式扩展,适用于分布式的文件系统,应对分布式存储的海量数据
缺点:查询性能不高,缺乏统一的查询语法
应用场景:日志、分布式的文件系统(对象存储)、推荐画像、时空数据、消息/订单等
NoSQL代表:Hbase、Cassandra

4、图形(Graph-Oriented)存储

特点:图形数据库允许将数据以图的方式存储
优点:图形相关算法。比如最短路径寻址,N度关系查找等
缺点:很多时候需要对整个图做计算才能得出需要的信息,分布式的集群方案不好做,处理超级节点乏力,没有分片存储机制,国内社区不活跃
应用场景:社交网络、推荐系统等。专注于构建关系图谱。
NoSQL代表:Neo4j、Infinite Graph

0.2 NoSQL的优缺点:

1、优点

高扩展性
没有标准化
分布式计算
有限的查询功能(目前为止)
低成本

2、缺点

最终一致是不直观的程序
架构的灵活性,半结构化数据
没有复杂的关系

0.3 总结:

NoSQL数据库在以下几种情况下比较适用:

数据模型比较简单
需要灵活性更强的IT系统
对数据库性能要求较高
不需要高度的数据一致性
对于给定的Key,比较容易映射复杂值的环境

1、MongoDB基本概念

MongoDB与关系型数据库术语对比:

image

MongoDB数据类型:

image.png

MongoDB常用权限:
image.png

read、readWrite、useAdmin、dbAdmin四个权限只对mongodb中单个数据库生效,也就是说该权限仅作用于单个特定数据库。

当mongodb中的数据库越来越多的时候,为单个数据库设置权限就很麻烦了,这时候就要用到 readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase权限了。(多数据库的权限必须定义在admin数据库中以便全局生效)

2、MongoDB权限相关操作

创建管理用户:

MongoDB有一个用户管理机制,简单描述为管理用户组,这个组的用户是专门为管理普通用户而设定的,暂且称之为管理员。

管理员通常没有数据库的读写权限,只有操作用户的权限,我们只需要赋予管理员userAdminAnyDatabase角色即可。另外管理员账户必须在admin数据库下创建

由于用户被创建在哪个数据库下,就只能在哪个数据库登陆,所以把所有的用户都创建在admin数据库下,这样我们切换数据库时就不需要频繁的进行登录了。

先use admin切换至admin数据库进行登陆,登陆后在use切换其他数据库进行操作即可。第二次的use就不需要再次登陆了。MongoDB设定use第二个数据库时如果登陆用户权限比较高就可以直接操作第二个数据库,而不需要登陆。

# 切换数据库:
## 管理员需要在admin数据库下创建,所以需要先切换至admin数据库
use admin

# 查看用户:
## 通过 db.system.users.find() 函数查看admin数据库中的所有用户信息

# 创建用户:
## 在mongodb中可以使用db.createUser({用户信息})函数创建用户
db.createUser({
	user: '<name>',
	pwd: '<password>',
	roles:[
		{
			role: '<role>',
			db: '<database>'
		}
	      ]
});


# 创建普通用户:
## 需求:创建一个test数据库,给这个数据库添加一个用户,用户名为testuser,密码为123456。并授予该用户对test数据库的读写操作权限

###  管理员登陆数据库:
###  普通用户需要由管理员用户创建,所以先使用管理员用户登陆数据库
	use admin
	db.auth("用户名","密码")
        db.auth("uaad",uaad)

###  创建数据库:
###  mongodb没有特定创建数据库的语法,在使用use切换数据库时,如果对应的数据库不存在则直接创建并切换
        use test

	show dbs 只显示有数据的数据库,没有数据不显示

###  创建普通用户(role只能是针对单个数据库的具体权限):
	db.createUser({
		user: '<name>’,
		pwd: '<password>’,
		roles:[
				{
					role: ’<role>’,
					db: ’<database>’ 
				}
			  ]
	});

###  身份认证:
	db.auth("用户名","密码")
	
	结果1表示认证成功
	结果0表示认证失败
	登陆成功以后即可执行该用户所拥有的角色对应的权限的其他操作,执行insert操作和find操作验证读写权限是否可用
	db.user.insert({"":""})		
		user是mongodb的集合(相当于rsdb的表)
		如果user集合存在,则会直接插入数据,如果不存在则创建之后在插入
	db.user.find()

### 更新用户:

### 更新角色:
	如果我们需要对已存在的用户进行角色修改,可以使用db.updateUser()函数来更新用户角色。注意:执行该函数需要当前用户具有 userAdmin 或userAdminAnyDatabase或root角色
	
	db.updateUser("用户名",{"roles":[{"role":"角色名称",db:"数据库"},{"更新项2":"更新内容"}]})

	比如给刚才的 uaad 用户再添加 readWriteAnyDatabase 和 dbAdminAnyDatabase 权限。

	注意:执行updateUser更新角色时,已有的权限role必须也要带上,否则不带上的话相当于删除掉已有的老权限       

	db.updateUser("uaad",{"roles":[{"role":"readWriteAnyDatabase",db:"admin"},{"role":"userAdminAnyDatabase",db:"admin"},{"role":"dbAdminAnyDatabase",db:"admin"}]})
	show users


	更新用户密码:
	更新用户密码有以下两种方式,更新密码时需要切换到该用户所在的数据库。注意:需要使用具有 userAdmin 或 userAdminAnyDatabase 或 root 角色的用户执行:
	使用 db.updateUser("用户名",{"pwd":"新密码"}) 函数更新密码
	使用 db.changeUserPassword("用户名","新密码")	函数更新密码


	删除用户:
	通过 db.dropUser()函数可以删除指定用户,删除成功以后会返回true。删除用户时需要切换到该用户所在的数据库。注意:需要使用具有	userAdmin	或	userAdminAnyDatabase	或	root	角色的用户才可以删除其他用户

3、MongoDB Database操作

	# 创建数据库:
	在MongoDB中创建数据库的命令使用的是 use 命令。该命令有两层含义:
	a、切换指定数据库
	b、如果切换的数据库不存在,则创建该数据库

	# 查询数据库:
	执行db命令可以显示当前所在的数据库

	# 删除数据库:
	删除数据库需要切换到需要删除的数据库中,且登陆用户具有	dbAdmin 或	dbAdminAnyDatabase	或更高权限,执行	db.dropDatabase()
	use	database
	db.dropDatabase()

4、Collection操作:

MongoDB中的集合是一组文档的集,相当于关系型数据库中的表

# 创建集合:
MongoDB使用 db.createCollection() 函数来创建集合

db.createCollection(name, options)

name: 要创建的集合名称。
options:可选参数,指定有关内存大小及索引的选项。

可选选项如下所示:
image.png

# 创建一个默认选项的集合命令如下:
db.createCollection("c1")


# 提示:其实在MongoDB中创建文档时会自动创建集合,除非你对创建的集合有特殊的集合
## 方式一:
db.c2.insert({"a":1})	# 当第一个文档插入时,集合就会被创建并包含该文档
	
## 方式二:
db.c3	
# 创建一个空集合,里面没有数据时通过show tables或show collections是无法查看到的,需要向集合中插入一个文档才能看见

	
# 查看集合:
## 通过 show tables 或 show collections 查看所有集合

## show tables
## 通过db.COLLECTION_NAME.stats()查看集合详情

# 删除集合:
## 通过 db.COLLECTION_NAME.drop() 删除集合

5、Document操作

5.1 插入文档
db.COLLECTION_NAME.insert({"xxxxx":"xxxx"})  # 可以插入单条,可以插入多条
db.COLLECTION_NAME.insertOne({"xxxx":"xxx"}) # 只能插入单条
db.COLLECTION_NAME.save({"xxxx":"xxxx"})     # 可以插入单条或多条

## db.COLLECTION_NAME.insert({"xxxxx":"xxxx"})
### 插入文档时如果没有指定 _id 则默认生成随机值,类型为 ObjectId,_id不能重复,且在插入后不可变。如果通过自定义id,且被匹配到了,就更新原有的值,如果没有匹配到_id,就做插入操作。

		user1 = {
			"name":"zhangsan",
			"age":23,
			"hobbies":["music","read"],
			"addr":{
				"conuntry":"China",
				"city":"bj"
			}
		}
		db.Collection_NAME.insert(user1)

		
# insertOne():
## 在Mongodb 3.2 版本之后,提供了insertOne()函数用于插入单条文档

		user2 ={
			"_id": "2",
			"name": "lisi",
			"age": 20,
			"hobbies": ["music","read"],
			"addr": {
				"country": "China",
				"city": "BJ"
			}
		}
		db.user.insertOne(user2)

	
# 多条插入:
## 可以使用 insert/insertMany/save 插入多条文档,区别在于把单条插入时函数参数的对象类型{} 变成了数组类型 [{},{}]
	db.Collection_NAME.insert([{name:"a"},{name:"b"}])
	db.Collection_NAME.insertMany([{name:"a"},{name:"b"}])
	db.Collection_NAME.save([{name:"a"},{name:"b"}])
5.2 更新文档
# update 函数:
## 通过update系列函数或者save函数可以更新集合中的文档。
	
# update()函数用于更新已存在的文档。语法格式如下:
	
db.Collection_NAME.update(query,update,options)
	
- query: update的查询条件,类似SQL update语句中的where部分。
- update: update的查询条件,类似SQL update 语句中where部分
- upset、multi都属于option可选选项
-- upset: 可选,如果不存在update的文档,是否插入该文档。true为插入,默认是false,不插入。
-- multi: 可选,是否批量更新。true表示按条件查询出来的多条记录全部更新,false只更新找到的第一条记录,默认是false


# 修改单条
db.Collection_NAME.updateOne(query,update,options)
## updateOne修改单条数据时,如果筛选条件 query 匹配到了多条数据,仅会更新匹配到的第一条数据,例如 
        	user = {"name":"wangwu","age":20,"hobbies":["music","read"]}
		db.user.updateOne({"name":"lisi"},{"$set":user})  
		当updateOne这个语句在user集合中匹配到了多条叫做wangwu的人,它仅会更新从上往下的第一个wangwu 的值。
                并且更新语句如果指定了$set操作符,仅会更新特定的字段(rdb中的列),没有涉及到的字段不会删除或更新。如果没有指定$set操作符,没有指定的原有字段会被更新语句给删除掉。

# 使用update 更新一条文档时作用等于updateOne 语句

# 修改多条
db.Collection_NAME.update(query,update,false,true)
-- update()方法的后两个参数分别是 upset和multi。
-- upset表示如果不存在update的文档,是否插入该文档。true为插入,默认是false,不插入。
-- multi表示是否批量更新。true表示按条件查询出来的多条记录全部更新,false只更新找到的第一条记录,默认是false

### 注意,更新文档操作如果不使用操作符,默认更新整个文档。比如没有使用操作符且修改的值只指定了name和age,这种情况下除了_id以外的其他属性都将被删除。

更新操作符:
官网主页

语法格式如下:
db.Collection_NAME.update({query},{更新操作符:{update}})

  • query: update的查询条件,类似sql update语句中的where部分
  • update: update的对象和一些更新的操作符(如$set,$inc...)等,也可以理解为SQL update语句中的set部分。
操作符作用
$set用来指定一个键并更新键值,若键不存在并创建
$inc可以对文档的某个值为数字型(只能为满足要求的数字)的键进行增减的操作
$unset主要用来删除键
$push向文档的某个数组类型的键添加一个数组元素,不过滤重复的数据。添加时键存在,要求键值类型必须是数组;键不存在,则创建数组类型的键
$pop删除数据元素。1表示从数组的尾部删除,-1表示从数组的头部删除元素
$pull从数组中删除满足条件的元素
$pullAll从数组中删除满足条件的多个元素
$rename对键进行重新命名

注意:

当执行update操作并使用$set 操作符的时候,如果update()的update参数中包含文档原先不存在的字段,则该字段不会被插入到文档中

例:
	user = {"name":"lisi","age":20,"hobbies":["music","read"]}
	db.user.updateOne({"name":"lisi"},{"$set":user}) 
	例子中执行的update语句,如果mongodb数据库集合文档中没有age这个字段,该age=20不会被插入到数据库中

	# $inc操作符
	db.user.update({"name":"lisi"},{"$inc":{"age":5}})		# 匹配到的第一个lisi的age在原有基础上递增5,如果想要减5
5.3 删除文档
## 通过 remove() 函数来移除集合中的数据,格式如下

 db.Collection_NAME.remove(<query>, {justOne: <boolean>})
 - query: (可选)删除文档的条件
 - justOne: (可选)如果设为true,则只删除一个文档,设为False则删除所有匹配的数据
	
	remove() 等价于
	# 删除符合条件的第一个文档
	db.Collection_NAME.deleteOne(<query>)
	# 删除所有数据
	db.Collection_NAME.remove({})
	# 清空该集合(表)等价于上一条
	db.user.deleteMany({})

6、查询数据

# 查询所有:
## 查询数据的语法格式如下:
## 等同于db.user.find({})
db.user.find()
	
# 去重
db.user.distinct('name')

# find()方法以非结构的方式来显示所有的文档。如果需要以易读的方式来读取数据,可以使用pretty()方法,语法格式如下:

db.user.find().pretty()  # pretty()方法以格式化的方法来显示所有文档	
6.1 比较运算

image.png

6.2 逻辑运算

MongoDB中字典内用逗号分隔多个条件默认是and关系(and可忽略),其它逻辑运算符有 $and、$or、$not (与、或、非)

image.png

image.png

6.3 成员

成员运算无非in和not in,MongoDB中形式为 $in 、 $nin

image.png

$type:

MongoDB中可以使用的数据类型如下所示:
image.png

# 查询name是字符串类型的数据
db.user.find({name:{$type:2}})
# 正则
正则定义在 / / 内
# 投影:
## MongoDB投影意思是只选择必要的数据而不是选择一整个文件的数据
		
## 相当于 select xxx from xxxx(选择性的查询)
## 在MongoDB中,当执行find()方法,那么它会显示一个文档所有字段。要限制这一点,需要设置字段列表值为1或0。1用来显示,而0用来隐藏,_id会默认显示出来
```
> ![image.png](https://www.ishells.cn/upload/2022/01/image-044c0b5ec0ae463a9f4ca41640c1e9fc.png)

```bash
#数组:
## 从hobbies数组中开始,查找索引是2的值为dancing的元素(索引从0开始)
		db.find({
			"hobbies.2": "dancing"
		})
```
> ![image.png](https://www.ishells.cn/upload/2022/01/image-b953cf65c21645528623d0e63abe62e0.png)
>
> ![image.png](https://www.ishells.cn/upload/2022/01/image-78cc507266134d9bbef0e89bff5ac0fe.png)

```bash
# 排序
## 在MongoDB中使用sort()方法来对数据进行排序,sort()方法可以通过参数指定排序的字段,并使用1和-1来指定排序的方式,其中1为升序排列,而-1是用于降序排列 
## 多个排序条件时,如果多个排序条件都相同,就按照剩余的排序条件进行排序

image.png

# 分页
## limit表示取多少个document,skip表示跳过几个document
## 分页公式如下:
db.Collection_NAME.find().skip( ( pageNum - 1 ) * pageSize ).limit(pageSize)

image.png

统计

image.png

聚合
image.png
image.png

投影 $project
image.png

$sort排序 $limit、$skip分页
image.png

$sample 随机选择n个

		# 随机获取3个文档
		db.Collection_NAME.aggregate([
				{$sample: {size:3}}
		])

image.png

索引
image.png

查询索引
db.Collection_NAME.getIndexes(INDEX_NAME)

删除索引
db.Collection_NAME.dropIndex(INDEX_NAME)

0

评论