文章目录

  • MongoDB - 基于《MongoDB进阶与实战:唐卓章》
    • 一、首次安装
      • 服务安装
      • 配置文件修改
      • 可视化工具
      • Docker部署
    • 二、基本使用
      • 2.1 概念解析
      • 2.2 MongoShell增删改查
        • 创建集合
        • 删除集合
        • 插入/更新/删除/查询文档
        • Limit/Skip/Sort(排序)/Aggregate(聚合)
      • 2.3 索引基础
      • 2.4 数据模型(BSON)
      • 2.5 SpringBoot集成
      • 2.6 审计实现
      • 2.7 异步驱动
      • 2.8 监听接口
      • 2.9 自定义序列化方式
        • 去掉_class属性
        • 实体类中属性为对象的处理
      • 2.10 文件上传/下载/预览
    • 三、架构部署(一)
      • 3.1 集群
        • 简述
        • 实时复制
        • 复制延迟/数据回滚
        • 初始化同步
        • 集群部署
      • 3.2 分片
      • 3.3 性能基准
        • WiredTiger缓存引擎介绍
        • 性能监控工具
        • 压力测试工具YCSB
        • nmon监视服务器性能
      • 3.4 索引高级
        • 覆盖索引
        • explain
        • 待补充.....
    • 四、并发处理
      • 4.1 内置锁
      • 4.2 MVCC
      • 4.3 Java实现MongoDB分布式锁
      • 4.4 应用设计
    • 五、高级特性
      • 5.1 待补充
    • 六、架构管理(二)
      • 6.1 安全管理
        • 角色(roles)管理
      • 6.2 高可用
      • 6.3 数据治理
  • over

MongoDB - 基于《MongoDB进阶与实战:唐卓章》

文章目录
MongoDB 官方网址 Community Download | MongoDB
MongoDB 教程 | 菜鸟教程 (runoob.com)
了解 MongoDB 看这一篇就够了【华为云分享】_华为云官方博客-CSDN博客
MongoDB CRUD操作 - MongoDB-CN-Manual (mongoing.com)

一、首次安装

服务安装

CentOS

## 安装依赖
yum -y install libcurl openssl

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UwwhBfWs-1664863195507)(https://csdn-pic-1301850093.cos.ap-guangzhou.myqcloud.com/csdn-pic/mongodb初始安装-1.png)]

## 从官网下载包,安装
wget -i -c https://repo.mongodb.org/yum/redhat/7/mongodb-org/5.0/x86_64/RPMS/mongodb-org-server-5.0.3-1.el7.x86_64.rpm
rpm -ivh mongodb-org-server-5.0.3-1.el7.x86_64.rpm
## 启动
systemctl restart mongod
systemctl status mongod

然后来看看它的默认端口27017

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-24WOzc78-1664863195508)(https://csdn-pic-1301850093.cos.ap-guangzhou.myqcloud.com/csdn-pic/mongodb初始安装-查看端口-1.png)]

配置文件修改

vim /etc/mongod.conf

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hq1dv2ox-1664863195508)(https://csdn-pic-1301850093.cos.ap-guangzhou.myqcloud.com/csdn-pic/mongodb-配置文件查看-1.png)]

可视化工具

MongoDB Compass Download | MongoDB

就无脑安装,没啥说明的,可惜的是好像没有CentOS版本,所以我装了Windows版

开始连接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Dfeu1WM-1664863195509)(https://csdn-pic-1301850093.cos.ap-guangzhou.myqcloud.com/csdn-pic/mongodb-可视化工具界面-1.png)]

Docker部署

Docker 安装 MongoDB | 菜鸟教程 (runoob.com)

二、基本使用

通过Linux命令行进入数据

root@9ac1e753f9ba:/# mongo
MongoDB shell version v5.0.3
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("93ea0c3d-6584-4668-b716-b13f27a88622") }
MongoDB server version: 5.0.3
================
Warning: the "mongo" shell has been superseded by "mongosh",
which delivers improved usability and compatibility.The "mongo" shell has been deprecated and will be removed in
an upcoming release.
We recommend you begin using "mongosh".
For installation instructions, see
https://docs.mongodb.com/mongodb-shell/install/
================
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, seehttps://docs.mongodb.com/
Questions? Try the MongoDB Developer Community Forumshttps://community.mongodb.com
---
The server generated these startup warnings when booting: 2021-11-12T04:57:02.666+00:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted2021-11-12T04:57:02.666+00:00: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. We suggest setting it to 'never'2021-11-12T04:57:02.666+00:00: /sys/kernel/mm/transparent_hugepage/defrag is 'always'. We suggest setting it to 'never'
---
---Enable MongoDB's free cloud-based monitoring service, which will then receive and displaymetrics about your deployment (disk utilization, CPU, operation statistics, etc).The monitoring data will be available on a MongoDB website with a unique URL accessible to youand anyone you share the URL with. MongoDB may use this information to make productimprovements and to suggest MongoDB products and deployment options to you.To enable free monitoring, run the following command: db.enableFreeMonitoring()To permanently disable this reminder, run the following command: db.disableFreeMonitoring()
---
> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
>

2.1 概念解析

宏观SQL与MongoDB

SQL术语/概念 MongoDB术语/概念 解释/说明
database database 数据库
table collection 数据库表/集合
row document 数据记录行/文档
column field 数据字段/域
index index 索引
table joins 表连接,MongoDB不支持
primary key primary key 主键,MongoDB自动将_id字段设置为主键

聚合(aggregate)

数据统计,类似于SQL中的count(*)

表达式 描述 实例
$sum 计算总和。 db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", num_tutorial : {sum:"sum : "sum:"likes"}}}])
$avg 计算平均值 db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", num_tutorial : {avg:"avg : "avg:"likes"}}}])
$min 获取集合中所有文档对应值得最小值。 db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", num_tutorial : {min:"min : "min:"likes"}}}])
$max 获取集合中所有文档对应值得最大值。 db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", num_tutorial : {max:"max : "max:"likes"}}}])
$push 将值加入一个数组中,不会判断是否有重复的值。 db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", url : {push:"push: "push:"url"}}}])
$addToSet 将值加入一个数组中,会判断是否有重复的值,若相同的值在数组中已经存在了,则不加入。 db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", url : {addToSet:"addToSet : "addToSet:"url"}}}])
$first 根据资源文档的排序获取第一个文档数据。 db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", first_url : {first:"first : "first:"url"}}}])
$last 根据资源文档的排序获取最后一个文档数据 db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", last_url : {last:"last : "last:"url"}}}])

2.2 MongoShell增删改查

创建集合

## 语法 name名称 options可选参数
db.createCollection(name, options)
字段 类型 描述
capped 布尔 (可选)如果为 true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。 当该值为 true 时,必须指定 size 参数。
autoIndexId 布尔 3.2 之后不再支持该参数。(可选)如为 true,自动在 _id 字段创建索引。默认为 false。
size 数值 (可选)为固定集合指定一个最大值,即字节数。 如果 capped 为 true,也需要指定该字段。
max 数值 (可选)指定固定集合中包含文档的最大数量。

删除集合

## delete collectionName
db.collection.drop()
## show All collection
show collections

插入/更新/删除/查询文档

插入

## insert(): 若插入的数据主键已经存在,则会抛 org.springframework.dao.DuplicateKeyException 异常,提示主键重复,不保存当前数据
db.COLLECTION_NAME.insert(document)
## 如果 _id 主键存在则更新数据,如果不存在就插入数据
db.collection.insertOne()
db.collection.replaceOne()
## 插入多个文档
db.collection.insertMany()

更新

## query : update的查询条件,类似sql update查询内where后面的
## update : update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的
## upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入
## multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新
## writeConcern :可选,抛出异常的级别
db.collection.update(<query>,<update>,{upsert: <boolean>,multi: <boolean>,writeConcern: <document>}
)

删除

## query :(可选)删除的文档的条件
## justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档
## writeConcern :(可选)抛出异常的级别
db.collection.remove(<query>,{justOne: <boolean>,writeConcern: <document>}
)

查询

## query :可选,使用查询操作符指定查询条件
## projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)
db.collection.find(query, projection)
## 易读格式
db.col.find().pretty()

条件依赖

操作 格式 范例 RDBMS中的类似语句
等于 {<key>:<value>} db.col.find({"by":"菜鸟教程"}).pretty() where by = '菜鸟教程'
小于 {<key>:{$lt:<value>}} db.col.find({"likes":{$lt:50}}).pretty() where likes < 50
小于或等于 {<key>:{$lte:<value>}} db.col.find({"likes":{$lte:50}}).pretty() where likes <= 50
大于 {<key>:{$gt:<value>}} db.col.find({"likes":{$gt:50}}).pretty() where likes > 50
大于或等于 {<key>:{$gte:<value>}} db.col.find({"likes":{$gte:50}}).pretty() where likes >= 50
不等于 {<key>:{$ne:<value>}} db.col.find({"likes":{$ne:50}}).pretty() where likes != 50

Limit/Skip/Sort(排序)/Aggregate(聚合)

  • Limit/Skip 就是类似于SQL中的limit,显示多少行,跳过多少行
  • Sort() 指定某个字段进行排序,就是order by
  • Aggregate() 统计平均值的,类似于count(*)

2.3 索引基础

  • 按字段数量分类,分为单键索引和组合索引(或复合索引)

  • 按字段类型,可以分为主键索引和非主键索引

  • 索引节点与物理记录的对应方式来分,可以分为聚簇索引和非聚簇索引

  • 按照索引的特性不同,又可以分为唯一索引、稀疏索引、文本索引、地理空间索引等

底层也是B+树的结构,但是跟其他NoSQL不一样的是,它可以跟普通的SQL数据库一样进行**explain()**调优

## 语法格式
db.collection.createIndex(keys, options)
##  Key 为你要创建的索引字段,1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1 即可
Parameter Type Description
background Boolean 建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 “background” 可选参数。 “background” 默认值为false
unique Boolean 建立的索引是否唯一。指定为true创建唯一索引。默认值为false.
name string 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。
dropDups Boolean **3.0+版本已废弃。**在建立唯一索引时是否删除重复记录,指定 true 创建唯一索引。默认值为 false.
sparse Boolean 对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 false.
expireAfterSeconds integer 指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。
v index version 索引的版本号。默认的索引版本取决于mongod创建索引时运行的版本。
weights document 索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重。
default_language string 对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语
language_override string 对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language.
## 查看集合索引
db.col.getIndexes()
## 查看集合索引大小
db.col.totalIndexSize()
## 删除集合所有索引
db.col.dropIndexes()
## 删除集合指定索引
db.col.dropIndex("索引名称")

另外,还有条件索引文本索引模糊索引

接下来看看索引优化怎么玩

db.demo_collection_test.explain().find({"title":"标题"})
{ explainVersion: '1',queryPlanner: { namespace: 'demodb01.demo_collection_test',indexFilterSet: false,parsedQuery: { title: { '$eq': '标题' } },queryHash: '6E0D6672',planCacheKey: '1953B8BB',maxIndexedOrSolutionsReached: false,maxIndexedAndSolutionsReached: false,maxScansToExplodeReached: false,## 表示获胜的计划winningPlan: { ## 扫描形式,stage=COLLSCAN则说明这是一个全表扫描stage: 'FETCH',inputStage: { ## 未能命中索引结果,会显示 COLLSCAN## 命中索引的结果,使用 IXSCAN## 出现了内存排序,显示为 SORTstage: 'IXSCAN',keyPattern: { title: 1 },indexName: 'title_1',isMultiKey: false,multiKeyPaths: { title: [] },isUnique: false,isSparse: false,isPartial: false,indexVersion: 2,direction: 'forward',indexBounds: { title: [ '["标题", "标题"]' ] } } },rejectedPlans: [] },command: { find: 'demo_collection_test',filter: { title: '标题' },'$db': 'demodb01' },serverInfo: { host: '9ac1e753f9ba',port: 27017,version: '5.0.3',gitVersion: '657fea5a61a74d7a79df7aff8e4bcf0bc742b748' },serverParameters: { internalQueryFacetBufferSizeBytes: 104857600,internalQueryFacetMaxOutputDocSizeBytes: 104857600,internalLookupStageIntermediateDocumentMaxSizeBytes: 104857600,internalDocumentSourceGroupMaxMemoryBytes: 104857600,internalQueryMaxBlockingSortMemoryUsageBytes: 104857600,internalQueryProhibitBlockingMergeOnMongoS: 0,internalQueryMaxAddToSetBytes: 104857600,internalDocumentSourceSetWindowFieldsMaxMemoryBytes: 104857600 },ok: 1 }

2.4 数据模型(BSON)

BSON(Binary JSON)是二进制版本的JSON,有如下数据类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-coZ7Hc5b-1664863195509)(https://csdn-pic-1301850093.cos.ap-guangzhou.myqcloud.com/csdn-pic/mongodb-bson数据类型-1.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EFATORaN-1664863195509)(https://csdn-pic-1301850093.cos.ap-guangzhou.myqcloud.com/csdn-pic/mongodb-bson数据类型-2.png)]

为什么MongoDB需要用到这种数据模型呢?因为在部分数据表示的场景下,默认的JSON数据类型虽然能应对大部分的场景,但是总有一些是不太方便进行表示的,所以引入了BSON作为辅助,其实,在整个mongoDB中,都是使用JSON+BSON作为数据存储格式的

还有一个固定集合的数据类型,放在后面针对SpringBoot进行操作使用

2.5 SpringBoot集成

依赖导入

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.20</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13</version><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>2.2.2.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><version>2.2.2.RELEASE</version></dependency><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-mongodb --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId><version>2.2.13.RELEASE</version></dependency>

application.yaml

server:port: 18081
spring:data:mongodb:uri: mongodb://192.168.247.173:27017/demodb01application:name: mongodb-test-01

封装实体类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import java.io.Serializable;@Document(collection = "demo_collection_test")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DemoEntity implements Serializable {@Idprivate Long id;private String title;private String description;
}

玩起来

import com.ljm.common.ToResult;
import com.ljm.entity.DemoEntity;
import com.mongodb.client.result.DeleteResult;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;@RestController
@RequestMapping("/test")
public class TestController {@Resourceprivate MongoTemplate mongoTemplate;@RequestMapping("/saveOrMongo")public ToResult saveOrMongo() {// 插入数据DemoEntity demoEntity = new DemoEntity(1001L, "我是标题", "我是描述信息");mongoTemplate.save(demoEntity);return new ToResult("saveOrMongo", "ok");}@RequestMapping("/getOrMongo")public ToResult getOrMongo() {// 查询所有List<DemoEntity> templateAll = mongoTemplate.findAll(DemoEntity.class);return new ToResult("getOrMongo", templateAll);}@RequestMapping("/removeOrMongo")public ToResult removeOrMongo() {// 获取集合名称String collectionName = mongoTemplate.getCollectionName(DemoEntity.class);DemoEntity demoEntity = new DemoEntity();demoEntity.setId(1001L);// deleteDeleteResult result = mongoTemplate.remove(demoEntity, collectionName);return new ToResult("getOrMongo", result);}
}

2.6 审计实现

我们可能需要对数据写一些审计字段

  • 数据在什么时间创建

  • 数据由谁创建

  • 数据最近一次发生变化的时间

  • 数据最近一次的修改者

我们可以在实体类上加多几个审计字段,或者加AOP,但是那样代码量有点大,所以SpringData提供了非常便利的审计(auditing)功能,这个功能需要在启动类上加注解@EnableMongoAuditing开启

写好审计类,让普通实体类继承于它

import lombok.Data;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import java.time.LocalDateTime;/*** 审计类** @author 李家民*/
@Data
public class AuditEnt {/*** 创建时间*/@CreatedDateprivate LocalDateTime createdDate;/*** 最后修改时间*/@LastModifiedDateprivate LocalDateTime updatedDate;/*** 创建者*/@CreatedByprivate String createdBy;/*** 最后修改者*/@LastModifiedByprivate String updatedBy;}

除了启动类,@EnableMongoAuditing也可以在包含@Configuration的配置类中添加。,在开启审计功能后,框架在保存审计类对象时,会自动对注解的字段进行注入

  • 对于@CreatedDate和@LastModifiedDate注解的字段会采用当前的时间值

  • 对于@CreatedBy、@LastModifiedBy注解的字段则通过AuditorAware接口处理

import org.springframework.data.domain.AuditorAware;
import org.springframework.stereotype.Component;
import java.util.Optional;@Component
public class AuditAwareImpl implements AuditorAware {@Overridepublic Optional getCurrentAuditor() {// 对于CreatedBy和LastModifiedBy注解的字段的自动注入
//        return Optional.empty();return Optional.of("我是李家民");}
}

2.7 异步驱动

引入依赖

<!-- https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-reactivestreams -->
<dependency><groupId>org.mongodb</groupId><artifactId>mongodb-driver-reactivestreams</artifactId><version>4.3.4</version>
</dependency>

做这个比较麻烦的可能是,做两个客户端,同步/异步,感觉不如用Future

2.8 监听接口

MongoDB Java Driver从3.1版本起开始增加了CommandListener接口用于支持对于命令操作的监听

import com.mongodb.event.CommandFailedEvent;
import com.mongodb.event.CommandListener;
import com.mongodb.event.CommandStartedEvent;
import com.mongodb.event.CommandSucceededEvent;
import org.springframework.context.annotation.Configuration;@Configuration
public class TestListener implements CommandListener {@Overridepublic void commandStarted(CommandStartedEvent commandStartedEvent) {// 监听命令启动事件}@Overridepublic void commandSucceeded(CommandSucceededEvent commandSucceededEvent) {// 监听命令完成事件}@Overridepublic void commandFailed(CommandFailedEvent commandFailedEvent) {// 监听命令失败事件}
}

另外,我们还可以通过标准的JMX的监视接口,实现监视驱动的连接数

2.9 自定义序列化方式

去掉_class属性

如果是通过注解@Document实现映射的实体类,在MongoDB中都会出现一个叫_class的字段,里面包含的是该实体类的全限定名路径,其实有些多余,是可以去掉的,通过定制TypeMapper的实现来去掉它,在配置类中进行处理

@BeanMappingMongoConverter mappingMongoConverter(MongoDbFactory factory,MongoMappingContext context,MongoCustomConversions conversions){DefaultDbRefResolver defaultDbRefResolver = new DefaultDbRefResolver(factory);MappingMongoConverter mongoConverter = new MappingMongoConverter(defaultDbRefResolver, context);mongoConverter.setCustomConversions(conversions);// 构造 DefaultMongoTypeMapper 把typeKey设置为空mongoConverter.setTypeMapper(new DefaultMongoTypeMapper(null));return mongoConverter;}

实体类中属性为对象的处理

就是当我们的实体类里面有属性是对象的情况下(且这个对象可能还是键值对的数据结构),就需要进行处理,大概流程如下

  1. 做两个静态类,实现Converter接口,分别处理读转换和写转换
  2. MongoCustomConversions这个Bean中进行注册

自行百度,不写了困了,我先睡一觉

2.10 文件上传/下载/预览

我直接上代码

/*** Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).* <p>* Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at* <p>* http://www.apache.org/licenses/LICENSE-2.0* <p>* Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package org.springblade.resource.service.impl;import com.baomidou.mybatisplus.core.metadata.IPage;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import org.bson.Document;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.resource.entity.Attach;
import org.springblade.resource.mapper.AttachMapper;
import org.springblade.resource.service.IAttachService;
import org.springblade.resource.vo.AttachVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsOperations;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.Map;/*** 附件表 服务实现类* @author Blade* @since 2022-10-03*/
@Service
public class AttachServiceImpl extends BaseServiceImpl<AttachMapper, Attach> implements IAttachService {@Autowiredprivate GridFsTemplate gridFsTemplate;@Autowiredprivate GridFsOperations gridFsOperations;@Overridepublic IPage<AttachVO> selectAttachPage(IPage<AttachVO> page, AttachVO attach) {return page.setRecords(baseMapper.selectAttachPage(page, attach));}/*** 上传文件* @param file* @return*/@Overridepublic Attach putFile(MultipartFile file) {try {long fileSize = file.getSize();String originalFilename = file.getOriginalFilename();InputStream fileInputStream = file.getInputStream();// 上传前的基础校验 checkFile()// ...Attach attach = new Attach(null, null,originalFilename.substring(0, originalFilename.lastIndexOf(".")), originalFilename,originalFilename.substring(1 + originalFilename.lastIndexOf(".")),fileSize);if (this.save(attach)) {DBObject metaData = new BasicDBObject();metaData.put("attach", attach);gridFsTemplate.store(fileInputStream, String.valueOf(attach.getId()), file.getContentType(), metaData);String link = "/blade-resource/attach/endpoint/download/" + attach.getId();String domainUrl = "http://127.0.0.1:8089";attach.setLink(link);attach.setDomainUrl(domainUrl);this.updateById(attach);return attach;}} catch (IOException e) {throw new RuntimeException(e);}return null;}/*** 下载文件* @param id* @return*/@Overridepublic ResponseEntity<byte[]> downloadFile(String id) {GridFSFile gridFSFile = gridFsTemplate.findOne(new Query(Criteria.where("filename").is(id)));if (gridFSFile == null) {return null;}GridFsResource resource = gridFsOperations.getResource(gridFSFile);try {InputStream inputStream = resource.getInputStream();byte[] buffer = new byte[1024];ByteArrayOutputStream bos = new ByteArrayOutputStream();int i = 0;while (-1 != (i = inputStream.read(buffer))) {bos.write(buffer, 0, i);}buffer = bos.toByteArray();Document document = (Document) gridFSFile.getMetadata().get("attach");MultiValueMap responseHeaderMap = new HttpHeaders();String extension = (String) document.get("extension");if (extension.contains("png") || extension.contains("jpg") || extension.contains("jpeg")) {responseHeaderMap.add("content-type", "image/" + extension);} else {responseHeaderMap.add("content-type", "application/x-msdownload");responseHeaderMap.add("Content-Disposition","attachment; filename=" + URLEncoder.encode((String) document.get("originalName"),"utf-8"));}ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(buffer, responseHeaderMap, HttpStatus.OK);return responseEntity;} catch (Exception e) {e.printStackTrace();}return null;}}

三、架构部署(一)

3.1 集群

简述

集群内部使用Raft选举算法来决定主从关系,但是,如果存在偶数集群节点的情况下可能出现平票

​ 如果希望避免平票结果的产生,最好使用奇数个节点成员,比如3个或5个。当然,在MongoDB副本集的实现中,对于平票问题已经提供了解决方案:

  • 为选举定时器增加少量的随机时间偏差,这样避免各个节点在同一时刻发起选举,提高成功率

  • 使用仲裁者角色,该角色不做数据复制,也不承担读写业务,仅仅用来投票

角色名称 作用
Primary 主节点,其接收所有的写请求,然后把修改同步到所有备节点。一个副本集只能有一个主节点,当主节点“挂掉”后,其他节点会重新选举出来一个主节点
Secondary 备节点,与主节点保持同样的数据集。当主节点“挂掉”时,参与竞选主节点
Arbiter 仲裁者节点,该节点只参与投票,不能被选为主节点,并且不从主节点中同步数据,是一个可选项的节点
Priority0 优先级为0的节点,该节点永远不会被选举为主节点,也不会主动发起选举
Hidden 隐藏节点,具备Priority0的特性,即不能被选为主节点(Priority为0),同时该节点对客户端不可见。由于隐藏节点不会接受业务访问,因此可通过隐藏节点做一些数据备份、离线计算的任务,这并不会影响整个副本集
Delayed 延迟节点,必须同时具备隐藏节点和Priority0的特性,并且其数据落后于主节点一段时间,该时间是可配置的。由于延迟节点的数据比主节点落后一段时间,当错误或者无效的数据写入主节点时,可通过延迟节点的数据来恢复到之前的时间点
Vote0 无投票权的节点,必须同时设定为Priority0节点。由于一个副本集中最多只有7个投票成员,因此多出来的成员则必须将其vote属性值设置为0,即这些成员将无法参与投票

集群副本集的模式有这几种

  1. PSS

    一主两备

  2. PSA

    一个主节点、一个备节点和一个仲裁者节点

  3. PSH

    一个主节点、一个备节点和一个隐藏节点

实时复制

主备节点之间同步数据是用oplog来操作的,这是一个固定集合,也可以看作是一个先入先出(FIFO)的缓冲队列

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-34BM3a4n-1664863195510)(https://csdn-pic-1301850093.cos.ap-guangzhou.myqcloud.com/csdn-pic/mongodb主从复制-1.png)]

主节点(生产者)不断地往里面写入增量日志,写满多出来的,先入先出删除,而从节点(消费者)不断地pull拉取

一个oplog结构如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZniWY6Fn-1664863195510)(https://csdn-pic-1301850093.cos.ap-guangzhou.myqcloud.com/csdn-pic/mongodb-实时复制-oplog-1.png)]

字段解释

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h1dpeeHJ-1664863195510)(https://csdn-pic-1301850093.cos.ap-guangzhou.myqcloud.com/csdn-pic/mongodb-实时复制-oplog-2.png)]

其中,ts字段描述了oplog产生的时间戳,可称之为optime,optime是备节点实现增量日志同步的关键,每一条oplog记录都描述了一次数据的原子性变更,对于oplog来说,必须保证是幂等性的

复制延迟/数据回滚

因为oplog集合是一个固定集合,先入先出的,如果从节点复制的不够快,就会导致有部分记录没有保存到而被冲刷,一旦时间撕裂过大就会导致更严重的问题,所以需要采取一些措施

  • 增加oplog的容量大小,并保持对复制窗口的监视

  • 通过一些扩展手段降低主节点的写入速度

  • 优化主备节点之间的网络

  • 避免字段使用太大的数组(可能导致oplog膨胀)

oplog集合的大小可以通过参数replication.oplogSizeMB设置,对于64位系统来说,oplog的默认值为

oplogSizeMB = min(磁盘可用空间 * 5% , 50GB)

但是,由于上述的情况始终是无法完全避免的,所以我们就提出数据回滚的概念

当副本集中的主节点宕机时,备节点会重新选举成为新的主节点。那么,当旧的主节点重新加入时,必须回滚掉之前的一些“脏日志数据”,以保证数据集与新的主节点一致。主备复制集合的差距越大,发生大量数据回滚的风险就越高

这些由旧主节点回滚的数据会被写到单独的rollback目录下,必要的情况下仍然可以恢复这些数据

初始化同步

在开始时,备节点仍然需要向主节点获得一份全量的数据用于建立基本快照,这个过程就称为初始化同步(initial sync)

我们来看看整个同步的过程

  1. 备节点记录主节点的同步时间戳optime=t1
  2. 从主节点上复制所有非local数据库的集合数据,同时创建这些集合上的索引
  3. 备节点fork出一个线程,将集合复制过程中的增量oplog(t1之后产生)也复制到本地

上述描述的是主从之间的同步,但是实际上,备节点之间也可以进行同步,这里不再阐述细节

集群部署

baidu.com

就不写了,大概流程是

  1. 配置好相应的公共文件mongo.conf,启动时使用–replSet集群模式
  2. 进入shell进行配置,做一个变量,把所有节点的地址信息写进去
  3. 启动集群,开始进入数据复制状态
命令
db.isMaster 查看副本集的节点信息
rs.status 对复制成员的监视,members一列体现了所有副本集成员的状态
rs.printReplicationInfo 查看当前节点oplog的情况
rs.printSlaveReplicationInfo 所有备节点成员的同步延迟情况

3.2 分片

分片(shard)是指在将数据进行水平切分之后,将其存储到多个不同的服务器节点上的一种扩展方式

不写了不写了,baidu.com

3.3 性能基准

WiredTiger缓存引擎介绍

理想情况下,MongoDB可以提供近似内存式的读写性能

WiredTiger引擎实现了数据的二级缓存,第一层是操作系统的页面缓存,第二层则是引擎提供的内部缓存

而在当数据发生写入时,MongoDB并不会立即持久化到磁盘上,而是先在内存中记录这些变更,之后通过CheckPoint机制将变化的数据写入磁盘,所以在单机下,保证数据可靠性的机制分成两个部分

  1. CheckPoint(检查点)机制:MongoDB每60s建立一次CheckPoint,当建立CheckPoint时,WiredTiger会在内存中建立所有数据的一致性快照,并将该快照覆盖的所有数据变化一并进行持久化(fsync),成功之后,内存中数据的修改才得以真正保存
  2. Journal日志:如果开启了Journal日志,那么WiredTiger会将每个写操作的redo日志写入Journal缓冲区,该缓冲区会频繁地将日志持久化到磁盘上

默认情况下,WiredTiger会对集合数据和索引使用压缩算法,当页面被写入磁盘时执行压缩,而从磁盘中读入缓存时对页面进行解压

  1. 对集合采用块压缩
  2. 对索引采用前缀压缩

性能监控工具

  1. mongostat:查看当前的QPS/内存使用/连接数

    123123

  2. mongotop:查看数据库的热点表

    123123

  3. Profiler:记录、分析MongoDB的详细操作日志,可以设置开启级别

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bwYw5R2v-1664863195511)(https://csdn-pic-1301850093.cos.ap-guangzhou.myqcloud.com/csdn-pic/mongodb-profiler日志记录模块等级-1.png)]

    ## 开启日志等级
    db.setProfilingLevel(2)
    { was: 0, slowms: 100, sampleRate: 1, ok: 1 }
    ## 查看日志
    db.system.profile.find()
    { op: 'update',ns: 'demodb01.demo_collection_test',command: { q: { _id: 1001 },u: { _id: 1001, title: '我是标题', description: '我是描述信息' },multi: false,upsert: true },keysExamined: 1,docsExamined: 1,nMatched: 1,nModified: 1,nUpserted: 0,keysInserted: 0,keysDeleted: 0,numYield: 0,locks: { ParallelBatchWriterMode: { acquireCount: { r: 1 } },ReplicationStateTransition: { acquireCount: { w: 1 } },Global: { acquireCount: { w: 1 } },Database: { acquireCount: { w: 1 } },Collection: { acquireCount: { w: 1 } },Mutex: { acquireCount: { r: 1 } } },flowControl: { acquireCount: 1, timeAcquiringMicros: 2 },millis: 0,planSummary: 'IDHACK',execStats: { stage: 'UPDATE',nReturned: 0,executionTimeMillisEstimate: 0,works: 2,advanced: 0,needTime: 1,needYield: 0,saveState: 0,restoreState: 0,isEOF: 1,nMatched: 1,nWouldModify: 1,nWouldUpsert: 0,inputStage: { stage: 'IDHACK',nReturned: 1,executionTimeMillisEstimate: 0,works: 1,advanced: 1,needTime: 0,needYield: 0,saveState: 1,restoreState: 1,isEOF: 1,keysExamined: 1,docsExamined: 1 } },ts: 2021-11-14T08:37:15.597Z,client: '192.168.247.1',allUsers: [],user: '' }
    

不写了,还有好几个

压力测试工具YCSB

是一个Java写的,雅虎提供的用于云服务的压测工具

nmon监视服务器性能

yum -y install nmon

3.4 索引高级

MongoDB和elasticsearch一样,都是用B+树和聚簇索引/非聚簇索引

覆盖索引

是一种查询优化的行为

我们知道,在一棵二级索引的B+树上,索引的值存在于树的叶子节点上,因此,如果我们希望查询的字段被包含在索引中,则直接查找二级索引树就可以获得,而不需要再次通过_id索引查找出原始的文档,可以减少一次对最终文档数据的检索操作(该操作也被称为回表)。大部分情况下,二级索引树常驻在内存中,覆盖索引式的查询可以保证一次检索行为仅仅发生在内存中,即避免了对磁盘的I/O操作,这对于性能的提升有显著的效果。

实际场景:我只查询索引的字段实现覆盖索引优化,不用去进行磁盘I/O的动作(索引的值存在于树的叶子节点上)

explain

有三种执行模式

  1. queryPlanner:默认的模式,仅进行查询计划分析,输出计划中的阶段信息
  2. executionStats:执行模式,在查询计划分析后,将按照winningPlan执行查询并统计过程信息
  3. allPlansExecution:全计划执行模式,将执行所有计划(包括winningPlan和rejectPlans),并返回全部的过程统计信息

待补充…

还有使用图形化界面进行索引优化的

索引优化原则

  1. 使用前缀匹配
  2. 避免低效的操作符
  3. 使用覆盖索引优化
  4. 高基数优先原则
  5. 控制索引的数量
  6. 避免设计过长的数组索引
  7. 避免创建重复的索引
  8. 删除无用的索引
  9. 避免深度分页
  10. 避免一次性返回大量结果集
  11. 谨防内存排序
  12. 避免大量的扫描
  13. 为索引预留足够的内存

四、并发处理

4.1 内置锁

MongoDB采用多粒度锁机制,针对不同层级的数据库对象进行加锁

  1. Global 全局
  2. Database 数据库
  3. Collection 集合
  4. Document 文档(WiredTiger引擎内部使用乐观锁方式实现)

同时,针对不同的读写方式,又分为如下几种锁类型

  • 读锁(R),代表共享锁(S)

  • 写锁(W),代表排它锁(X)

  • 意向读锁(r),代表意向的共享锁(IS)

  • 意向写锁(w),代表意向的排它锁(IX)

MongoDB锁的兼容

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xvKOun7H-1664863195511)(https://csdn-pic-1301850093.cos.ap-guangzhou.myqcloud.com/csdn-pic/mongodb-锁的兼容性-1.png)]

接下来解释一下什么叫意向锁,此时我们需要更新某种表的某一条记录,需要经过的流程有

  1. Global 增加意向写锁
  2. Database 增加意向写锁
  3. Collection 增加意向写锁
  4. 对这一行记录执行更新(乐观锁)

如果这个时候,有其他操作进入

  1. 对其他行记录执行更新,不同用户文档的写操作便不会产生互斥了(同一集合的意向写锁可以存在多个)
  2. 修改表名,对Collection增加写锁(互斥,必须要等该表下的操作进行结束)

常见操作对应的锁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dui3UljC-1664863195511)(https://csdn-pic-1301850093.cos.ap-guangzhou.myqcloud.com/csdn-pic/mongodb基本操作对应的锁-1.png)]

可以通过命令db.currentOp()查看锁的状态,并且在某些情况下,锁会出现让步(例如某些操作过于长时间未执行完成)

4.2 MVCC

MVCC(Multi Version Concurrency Control)的多版本并发控制,是行级锁的一种妥协

是目前数据库中使用最广泛的一种经典机制,包括MongoDB、MySQL、PostGreSQL等流行数据库都在使用

4.3 Java实现MongoDB分布式锁

这个问题我想了一下,有如下几种解决方案

  1. 原生的MongoDB处理
  2. Zookeeper
  3. Redis

我比较推崇用Redis,数据驻于内存,性能高,并且有分布式锁框架,支持重入

4.4 应用设计

三范式,批处理。。。。

五、高级特性

5.1 待补充

1

六、架构管理(二)

6.1 安全管理

角色(roles)管理

MongoDB 4.0 角色(roles)详解_m0_37867491的CSDN博客

数据库用户角色(Database User Roles)

  • read : 授权User只读数据的权限,允许用户读取指定的数据库
  • readWrite 授权User读/写数据的权限,允许用户读/写指定的数据库

数据库管理角色(Database Admininstration Roles)

  • dbAdmin:在当前的数据库中执行管理操作,如索引的创建、删除、统计、查看等
  • dbOwner:在当前的数据库中执行任意操作,增、删、改、查等
  • userAdmin :在当前的数据库中管理User,创建、删除和管理用户

备份和还原角色(Backup and Restoration Roles)

  • backup
  • restore

跨库角色(All-Database Roles)

  • readAnyDatabase:授权在所有的数据库上读取数据的权限,只在admin 中可用
  • readWriteAnyDatabase:授权在所有的数据库上读写数据的权限,只在admin 中可用
  • userAdminAnyDatabase:授权在所有的数据库上管理User的权限,只在admin中可用
  • dbAdminAnyDatabase: 授权管理所有数据库的权限,只在admin 中可用

集群管理角色(Cluster Administration Roles)

  • clusterAdmin:授权管理集群的最高权限,只在admin中可用
  • clusterManager:授权管理和监控集群的权限
  • clusterMonoitor:授权监控集群的权限,对监控工具具有readonly的权限
  • hostManager:管理server

超级角色(super master Roles)

  • root :超级账户和权限,只在admin中可用

6.2 高可用

1

6.3 数据治理

1

over

MongoDB+集成SpringBoot+索引+并发优化 - 基于《MongoDB进阶与实战:唐卓章》相关推荐

  1. mongodb 对象唯一索引_什么是MongoDB?简介,架构,功能和示例

    通过这个教程,我们将学习如下内容:- 什么是MongoDB? MongoDB功能 MongoDB示例演示 MongoDB架构的关键组件 为什么要使用MongoDB MongoDB中的数据建模 Mong ...

  2. 速递!MongoDB最新书籍出版啦:MongoDB进阶与实战-微服务整合、性能优化、架构管理

    新书速递 近期,MongoDB中文社区核心成员之一唐卓章老师出了一本MongoDB最新书籍--<MongoDB进阶与实战:微服务整合.性能优化.架构管理>,全面涵盖了MongoDB的基本原 ...

  3. Solr与MongoDB集成,实时增量索引[转]

    http://www.123905.com/ 一. 概述 大量的数据存储在MongoDB上,需要快速搜索出目标内容,于是搭建Solr服务. 另外一点,用Solr索引数据后,可以把数据用在不同的项目当中 ...

  4. 使用NOSQL的MongoDB时建立索引需要注意的几点建议和Explain优化分析

    第一,MongoDB索引和MySQL索引非常相似并且对于MySQL的索引优化有很多也适用于MongoDB. 第二,更重要的是,这些索引的建议对你的应用提高也是有限的. 对于应用的最佳索引策略应该基于很 ...

  5. MongoDB 教程五: MongoDB固定集合和性能优化 (索引Indexes, 优化器, 慢查询profile)

    mongodb索引详解(Indexes) 索引介绍 索引在mongodb中被支持,如果没有索引,mongodb必须扫描每一个文档集合选择匹配的查询记录.这样扫描集合效率并不高,因为它需要mongod进 ...

  6. mongodb如何根据字段(数组类型)的长度排序_大数据存储技术选型(七)——MongoDB设计模式及索引优化...

    关系数据库的时代 关系数据库的设计理念 假设你有一台车,你需要给它存起来,给它找个停车位.传统数据库的存储方式就相当于,把这个车的所有零件拆下来,放在存放对应零件的盒子里,需要用的时候,再把他们取出来 ...

  7. Hazelcast与MongoDB集成

    Hazelcast与MongoDB集成 作者:chszs,未经博主允许不得转载.经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs 一.Hazelcast与Mong ...

  8. mongodb安装_MongoDB索引策略和索引类型

    mongodb安装 1. MongoDB索引策略和索引类型–简介 MongoDB是一个开放源代码,面向文档的跨平台数据库,它使用C ++开发,并且是最流行和使用最广泛的NoSQL类型数据库之一. 它可 ...

  9. MongoDB 教程五: MongoDB固定集合和性能优化

    MongoDB 固定集合(Capped Collections) MongoDB 固定集合(Capped Collections)是性能出色且有着固定大小的集合,对于大小固定,我们可以想象其就像一个环 ...

最新文章

  1. java调用clang编译的so_写Java这么久,JDK源码编译过没?编译JDK源码踩坑纪实
  2. Apk文件破解反编译(转)
  3. linux显示没有网卡
  4. Linux 0.12内核的内存管理基础
  5. S5PV210开发 -- UART 详解
  6. 15.5.2 【Task实现细节】骨架方法的结构
  7. ASP.NET企业开发框架IsLine FrameWork系列之十三--框架配置信息大全(上)
  8. NB-IoT模块 移远BC26接入OneNET
  9. GMQ区块链生态系统平行链,未来将应用于众多产业
  10. AcWing 888. 求组合数 IV(高精度求组合数问题)
  11. Mysql索引查询失效的情况
  12. CSSG:Cobalt Strike Shellcode生成工具
  13. 【Mysql密码管理】-【管理员密码已知、未知(忘记密码)、破解】
  14. 挂载WINDOWS虚机镜像
  15. 微信小程序跳转公众号图文内容
  16. 三菱plc支持c语言,5.三菱FX系列PLC支持哪种编程方式.
  17. python画图皮卡丘_用python画一只可爱的皮卡丘
  18. 中富金石投教怎么样?让专业投资创造更多财富机会
  19. iso 国家名称列表
  20. 【54期】Java序列化三连问,是什么?为什么需要?如何实现?

热门文章

  1. 315MHz 遥控器固定码信号分析和重放
  2. Visual Studio 2022在线安装教程
  3. 频率控制和滞环控制的半桥/全桥LLC电路仿真对比 两种方式下均可实现输出电压闭环控制 ,模型中包含负载的阶跃变化过程 ,可以验证闭环系统稳定性
  4. 领先特斯拉,中国电车制造商“登陆”东南亚,电气化潮流一触即发
  5. torch.conv2d 参数解析与 NumPy 复现
  6. html转pdf工具安卓版,html转pdf工具(wkhtmltopdf)v2018 官方版
  7. 嵌入式系统移植【8】——设备树和根文件系统移植
  8. 一个简单的在线调色板
  9. 一个完整的神经网络包括,混沌理论的应用领域
  10. 通过u盘启动计算机使用ghost安装系统步骤,天意u盘启动工具通过u盘启动计算机使用ghost安装系统步骤...