MongoDB 高级特性与原理
# 第四部分 MongoDB 应用实战
# 4.1 MongoDB 的适用场景
- 网站数据:Mongo 非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高 度伸缩性。
- 缓存:由于性能很高,Mongo 也适合作为信息基础设施的缓存层。在系统重启之后,由 Mongo 搭建的持久化缓存层可以避免下层的数据源过载。
- **大尺寸、低价值的数据:**使用传统的关系型数据库存储一些大尺寸低价值数据时会比较浪费, 在此之前,很多时候程序员往往会选择传统的文件进行存储。
- 高伸缩性的场景:Mongo 非常适合由数十或数百台服务器组成的数据库,Mongo 的路线图中 已经包含对 MapReduce 引擎的内置支持以及集群高可用的解决方案。
- 用于对象及 JSON 数据的存储:Mongo 的 BSON 数据格式非常适合文档化格式的存储及查询。
# 4.2 MongoDB 的行业具体应用场景
- 游戏场景,使用 MongoDB 存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存 储,方便查询、更新。
- 物流场景,使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。
- 社交场景,使用 MongoDB 存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能。
- 物联网场景,使用 MongoDB 存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析。
- 直播,使用 MongoDB 存储用户信息、礼物信息等。
- 各种记录。点赞记录,收藏记录,日志,一些不重要、低价值的数据。
# 4.3 如何抉择是否使用 MongoDB
应用特征 | Yes / No |
---|---|
应用不需要事务及复杂 join 支持 | 必须 Yes |
新应用,需求会变,数据模型无法确定,想快速迭代开发 | ? |
应用需要 2000-3000 以上的读写 QPS(更高也可以) | ? |
应用需要 TB 甚至 PB 级别数据存储 | ? |
应用发展迅速,需要能快速水平扩展 | ? |
应用要求存储的数据不丢失 | ? |
应用需要 99.999%高可用 | ? |
应用需要大量的地理位置查询、文本查询 | ? |
# 4.4 Java 访问 MongoDB
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.10.1</version>
</dependency>
2
3
4
5
文档添加
MongoClient mongoClient = new MongoClient("192.168.211.133", 37017); MongoDatabase database = mongoClient.getDatabase("lg_resume"); MongoCollection<Document> collection = database.getCollection("lg_resume_preview"); Document document = Document.parse( "{name:'lisi',city:'bj',birth_day:new ISODate('2001-08- 01'),expectSalary:18000}"); collection.insertOne(document ); mongoClient.close();
1
2
3
4
5
6
7
8
9文档查询
MongoClient mongoClient = new MongoClient("192.168.211.133", 37017); MongoDatabase database = mongoClient.getDatabase("lg_resume"); MongoCollection<Document> collection = database.getCollection("lg_resume_preview"); Document sdoc=new Document(); //按expectSalary倒序 sdoc.append("expectSalary", -1); FindIterable<Document> findIterable = collection.find().sort(sdoc); for (Document document : findIterable) { System.out.println(document); } mongoClient.close();
1
2
3
4
5
6
7
8
9
10
11
12文档查询过滤
MongoClient mongoClient = new MongoClient("192.168.211.133", 37017); MongoDatabase database = mongoClient.getDatabase("lg_resume"); MongoCollection<Document> collection = database.getCollection("lg_resume_preview"); Document sdoc=new Document(); //按expectSalary倒序 sdoc.append("expectSalary", -1); FindIterable<Document> findIterable = collection.find(Filters.gt("expectSalary",21000)).sort(sdoc); for (Document document : findIterable) { System.out.println(document); } mongoClient.close();
1
2
3
4
5
6
7
8
9
10
11
12
13
# 4.5 Spring Boot 访问 MongoDB
# 4.5.1 MongoTemplate 的方式
第 1 步:基于 maven 新建 springboot 工程
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> <version>2.2.2.RELEASE</version> </dependency>
1
2
3
4
5第 2 步: 配置文件 application.properties
spring.data.mongodb.host=192.168.211.133 spring.data.mongodb.port=37017 spring.data.mongodb.database=lg_resume
1
2
3第 3 步: DAO 实现类 注入 MongoTemplate 完成增删改查
@Autowired protected MongoTemplate mongoTemplate
1
2第 4 步: 从 Spring 容器中获取 DAO 对象 进行测试
# 4.5.2 MongoRepository 的方式
第 1 步:基于 maven 新建 springboot 工程
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> <version>2.2.2.RELEASE</version> </dependency>
1
2
3
4
5第 2 步: 配置文件 application.properties
spring.data.mongodb.host=192.168.211.133 spring.data.mongodb.port=37017 spring.data.mongodb.database=lg_resume
1
2
3第 3 步:第 3 步:编写实体类 并在实体类上打@Document(“集合名”)
第 4 步:编写 Repository 接口 继承 MongoRepository
方法具体参考:https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-m ethods.query-creation 如果内置方法不够用 就自己定义 如:定义 find|read|get 等开头的方法进行查询
第 5 步: 从 Spring 容器中获取 Repository 对象 进行测试
# 第五部分 MongoDB 架构
# 5.1 MongoDB 逻辑结构
MongoDB 与 MySQL 中的架构相差不多,底层都使用了可插拔的存储引擎以满足用户的不同需要。用户可以根据程序的数据特征选择不同的存储引擎,在最新版本的 MongoDB 中使用了 WiredTiger 作为默认的存储引擎,WiredTiger 提供了不同粒度的并发控制和压缩机制,能够为不同种类的应用提供了最好的性能和存储率。
在存储引擎上层的就是 MongoDB 的数据模型和查询语言了,由于 MongoDB 对数据的存储与 RDBMS 有较大的差异,所以它创建了一套不同的数据模型和查询语言。
# 5.2 MongoDB 的数据模型
# 5.2.1 描述数据模型
内嵌
内嵌的方式指的是把相关联的数据保存在同一个文档结构之中。MongoDB 的文档结构允许一个字段或者一个数组内的值作为一个嵌套的文档。
引用
引用方式通过存储数据引用信息来实现两个不同文档之间的关联,应用程序可以通过解析这些数据引用来访问相关数据。
# 5.2.2 如何选择数据模型
选择内嵌:
数据对象之间有包含关系 ,一般是数据对象之间有一对多或者一对一的关系 。
需要经常一起读取的数据。
有 map-reduce/aggregation 需求的数据放在一起,这些操作都只能操作单个 collection。
选择引用:
- 当内嵌数据会导致很多数据的重复,并且读性能的优势又不足于覆盖数据重复的弊端 。
- 需要表达比较复杂的多对多关系的时候 。
- 大型层次结果数据集嵌套不要太深。
# 5.3 MongoDB 存储引擎
# 5.3.1 存储引擎概述
存储引擎是 MongoDB 的核心组件,负责管理数据如何存储在硬盘和内存上。MongoDB 支持的存储引擎有 MMAPv1 ,WiredTiger 和 InMemory。InMemory 存储引擎用于将数据只存储在内存中,只将少量的元数据 (meta-data)和诊断日志(Diagnostic)存储到硬盘文件中,由于不需要 Disk 的 IO 操作,就能获取所需的数据,InMemory 存储引擎大幅度降低了数据查询的延迟(Latency)。从 mongodb3.2 开始默认的存储引擎是 WiredTiger,3.2 版本之前的默认存储引擎是 MMAPv1,mongodb4.x 版本不再支持 MMAPv1 存储引擎。
storage:
journal:
enabled: true
dbPath: /data/mongo/
##是否一个库一个文件夹
directoryPerDB: true
##数据引擎
engine: wiredTiger
##WT引擎配置
WiredTiger:
engineConfig:
##WT最大使用cache(根据服务器实际情况调节)
cacheSizeGB: 2
##是否将索引也按数据库名单独存储
directoryForIndexes: true
journalCompressor:none (默认snappy)
##表压缩配置
collectionConfig:
blockCompressor: zlib (默认snappy,还可选none、zlib)
##索引配置
indexConfig:
prefixCompression: true
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 5.3.2 WiredTiger 存储引擎优势
文档空间分配方式
WiredTiger 使用的是 BTree 存储 MMAPV1 线性存储 需要 Padding
并发级别
WiredTiger 文档级别锁 MMAPV1 引擎使用表级锁
数据压缩 snappy (默认) 和 zlib ,相比 MMAPV1(无压缩) 空间节省数倍。
内存使用 WiredTiger 可以指定内存的使用大小。
Cache 使用 WT 引擎使用了二阶缓存 WiredTiger Cache, File System Cache 来保证 Disk 上的数据的最终一致性。 而 MMAPv1 只有 journal 日志。
# 5.3.3 WiredTiger 引擎包含的文件和作用
- WiredTiger.basecfg: 存储基本配置信息,与 ConfigServer 有关系
- WiredTiger.lock: 定义锁操作 table*.wt: 存储各张表的数据 *
- WiredTiger.wt: 存储 table 的元数据
- WiredTiger.turtle: 存储 WiredTiger.wt 的元数据
- journal: 存储 WAL(Write Ahead Log)
# 5.3.4 WiredTiger 存储引擎实现原理
写请求
WiredTiger 的写操作会默认写入 Cache ,并持久化到 WAL (Write Ahead Log),每 60s 或 Log 文件达到 2G 做一次 checkpoint
(当然我们也可以通过在写入时传入 j: true
的参数强制 journal 文件的同步 ,writeConcern { w: , j: , wtimeout: })
产生快照文件。WiredTiger 初始化时,恢复至最新的快照状态,然后再根据 WAL 恢复数据,保证数据的完整性。
Cache 是基于 BTree 的,节点是一个 page,root page 是根节点,internal page 是中间索引节点,leaf page 真正存储数据,数据以 page 为单位读写。WiredTiger 采用 Copy on write 的方式管理写操作 (insert、update、delete),写操作会先缓存在 cache 里,持久化时,写操作不会在原来的 leaf page 上进行,而是写入新分配的 page,每次 checkpoint 都会产生一个新的 root page。
checkpoint 流程
1.对所有的table进行一次checkpoint,每个table的checkpoint的元数据更新至WiredTiger.wt
2.对WiredTiger.wt进行checkpoint,将该table checkpoint的元数据更新至临时文件
WiredTiger.turtle.set
3.将WiredTiger.turtle.set重命名为WiredTiger.turtle。
4.上述过程如果中间失败,WiredTiger在下次连接初始化时,首先将数据恢复至最新的快照状态,然后根据WAL恢复数据,以保证存储可靠性。
2
3
4
5
Journaling
在数据库宕机时 , 为保证 MongoDB 中数据的持久性,MongoDB 使用了 Write Ahead Logging 向磁盘 上的 journal 文件预先进行写入。除了 journal 日志,MongoDB 还使用检查点(checkpoint)来保证数据的一致性,当数据库发生宕机时,我们就需要 checkpoint 和 journal 文件协作完成数据的恢复工作。
- 在数据文件中查找上一个检查点的标识符
- 在 journal 文件中查找标识符对应的记录
- 重做对应记录之后的全部操作
# 第六部分 MongoDB 集群高可用
# 6.1 MongoDB 主从复制架构原理和缺陷
master-slave 架构中 master 节点负责数据的读写,slave 没有写入权限只负责读取数据。
在主从结构中,主节点的操作记录成为 oplog(operation log)。oplog 存储在系统数据库 local 的 oplog.$main 集合中,这个集合的每个文档都代表主节点上执行的一个操作。从服务器会定期从主服务器中获取 oplog 记录,然后在本机上执行!对于存储 oplog 的集合,MongoDB 采用的是固定集合,也就是说随着操作过多,新的操作会覆盖旧的操作!
主从结构没有自动故障转移功能,需要指定 master 和 slave 端,不推荐在生产中使用。
mongodb4.0 后不再支持主从复制!
[main] Master/slave replication is no longer supported
# 6.2 复制集 replica sets
# 6.2.1 什么是复制集
复制集是由一组拥有相同数据集的 mongod 实例做组成的集群。 复制集是一个集群,它是 2 台及 2 台以上的服务器组成,以及复制集成员包括 Primary 主节点,secondary 从节点和投票节点。 复制集提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性,保证数据的安全性。
# 6.2.2 为什么要使用复制集
高可用
防止设备(服务器、网络)故障。
提供自动 failover 功能。
技术来保证高可用
灾难恢复
当发生故障时,可以从其他节点恢复 用于备份。
功能隔离 我们可以在备节点上执行读操作,减少主节点的压力 比如:用于分析、报表,数据挖掘,系统任务等。
# 6.2.3 复制集集群架构原理
一个复制集中 Primary 节点上能够完成读写操作,Secondary 节点仅能用于读操作。Primary 节点需要记录所有改变数据库状态的操作,这些记录保存在 oplog 中,这个文件存储在 local 数据库,各个 Secondary 节点通过此 oplog 来复制数据并应用于本地,保持本地的数据与主节点的一致。oplog 具有幂等性,即无论执行几次其结果一致,这个比 mysql 的二进制日志更好用。
oplog 的组成结构
{
"ts" : Timestamp(1446011584, 2),
"h" : NumberLong("1687359108795812092"),
"v" : 2,
"op" : "i",
"ns" : "test.nosql",
"o" : { "_id" : ObjectId("563062c0b085733f34ab4129"), "name" : "mongodb",
"score" : "10"}
}
2
3
4
5
6
7
8
9
ts:操作时间,当前 timestamp + 计数器,计数器每秒都被重置
h:操作的全局唯一标识
v:oplog 版本信息
op:操作类型
i:插入操作
u:更新操作
d:删除操作
c:执行命令(如 createDatabase,dropDatabase)
n:空操作,特殊用途
ns:操作针对的集合
o:操作内容
o2:更新查询条件,仅 update 操作包含该字段
复制集数据同步分为初始化同步和 keep 复制同步。初始化同步指全量从主节点同步数据,如果 Primary 节点数据量比较大同步时间会比较长。而 keep 复制指初始化同步过后,节点之间的实时同步一般是增量同步。
初始化同步有以下两种情况会触发:
(1) Secondary 第一次加入。
(2) Secondary 落后的数据量超过了 oplog 的大小,这样也会被全量复制。
MongoDB 的 Primary 节点选举基于心跳触发。一个复制集 N 个节点中的任意两个节点维持心跳,每个节 点维护其他 N-1 个节点的状态。
# 心跳检测:
整个集群需要保持一定的通信才能知道哪些节点活着哪些节点挂掉。mongodb 节点会向副本集中的其他节点每 2 秒就会发送一次 pings 包,如果其他节点在 10 秒钟之内没有返回就标示为不能访问。每个节点内部都会维护一个状态映射表,表明当前每个节点是什么角色、日志时间戳等关键信息。如果主节点发现自己无法与 大部分节点通讯则把自己降级为 secondary 只读节点。
# 主节点选举触发的时机:
第一次初始化一个复制集
Secondary 节点权重比 Primary 节点高时,发起替换选举
Secondary 节点发现集群中没有 Primary 时,发起选举
Primary 节点不能访问到大部分(Majority)成员时主动降级
当触发选举时,Secondary 节点尝试将自身选举为 Primary。主节点选举是一个二阶段过程+多数派协议。
第一阶段:
检测自身是否有被选举的资格。如果符合资格会向其它节点发起本节点是否有选举资格的 FreshnessCheck,进行同僚仲裁
第二阶段:
发起者向集群中存活节点发送 Elect(选举)请求,仲裁者收到请求的节点会执行一系列合法性检查,如果检查通过,则仲裁者(一个复制集中最多 50 个节点 其中只有 7 个具有投票权)给发起者投一票。 pv0 通过 30 秒选举锁防止一次选举中两次投票。 pv1 使用了 terms(一个单调递增的选举计数器)来防止在一次选举中投两次票的情况。
多数派协议:
发起者如果获得超过半数的投票,则选举通过,自身成为 Primary 节点。获得低于半数选票的原因,除了常见的网络问题外,相同优先级的节点同时通过第一阶段的同僚仲裁并进入第二阶段也是一个原因。因此,当选票不足时,会 sleep[0,1]秒内的随机时间,之后再次尝试选举。
# 6.2.4 复制集搭建
1.主节点配置 mongo_37017.conf
# 主节点配置
dbpath=/data/mongo/data/server1
bind_ip=0.0.0.0
port=37017
fork=true
logpath=/data/mongo/logs/server1.log
replSet=lagouCluster
2
3
4
5
6
7
2.从节点 1 配置 mongo_37018.conf
dbpath=/data/mongo/data/server2
bind_ip=0.0.0.0
port=37018
fork=true
logpath=/data/mongo/logs/server2.log
replSet=lagouCluster
2
3
4
5
6
3.从节点 2 配置 mongo_37019.conf
dbpath=/data/mongo/data/server3
bind_ip=0.0.0.0
port=37019
fork=true
logpath=/data/mongo/logs/server3.log
replSet=lagouCluster
2
3
4
5
6
4.初始化节点配置
启动三个节点 然后进入任意一个节点 运行如下命令:
var cfg ={"_id":"lagouCluster",
"protocolVersion" : 1,
"members":[
{"_id":1,"host":"192.168.211.133:37017","priority":10},
{"_id":2,"host":"192.168.211.133:37018"}
]
}
rs.initiate(cfg)
rs.status()
2
3
4
5
6
7
8
9
5.节点的动态增删
增加节点
rs.add("192.168.211.133:37019")
删除slave 节点
rs.remove("192.168.211.133:37019")
2
3
4
6.复制集操作演示
进入主节点 ----- 插入数据 ------ 进入从节点验证
注意:默认节点下从节点不能读取数据。调用 rs.slaveOk() 解决
为了保证高可用,在集群当中如果主节点挂掉后,会自动在从节点中选举一个重新做为主节点。
rs.status()
节点说明:
PRIMARY 节点: 可以查询和新增数据
SECONDARY 节点:只能查询 不能新增 基于 priority 权重可以被选为主节点
ARBITER 节点: 不能查询数据和新增数据 ,不能变成主节点
# 6.2.5 复制集成员的配置参数
# 6.2.6 有仲裁节点复制集搭建
和上面的配置步骤相同 只是增加了 一个特殊的仲裁节点 注入节点 执行
rs.addArb("IP:端口");
rs.addArb("192.168.211.133:37020")
# 6.3 分片集群 Shard Cluster
# 6.3.1 什么是分片
分片(sharding)是 MongoDB 用来将大型集合水平分割到不同服务器(或者复制集)上所采用的方法。 不需要功能强大的大型计算机就可以存储更多的数据,处理更大的负载。
# 6.3.2 为什么要分片
1.存储容量需求超出单机磁盘容量。
2.活跃的数据集超出单机内存容量,导致很多请求都要从磁盘读取数据,影响性能。
3.IOPS 超出单个 MongoDB 节点的服务能力,随着数据的增长,单机实例的瓶颈会越来越明显。
4.副本集具有节点数量限制。
垂直扩展:增加更多的 CPU 和存储资源来扩展容量。
水平扩展:将数据集分布在多个服务器上。水平扩展即分片。
# 6.3.3 分片的工作原理
分片集群由以下 3 个服务组成:
Shards Server: 每个 shard 由一个或多个 mongod 进程组成,用于存储数据。
Router Server: 数据库集群的请求入口,所有请求都通过 Router(mongos)进行协调,不需要在应用程序添加一个路由选择器,Router(mongos)就是一个请求分发中心它负责把应用程序的请求转发到对应的 Shard 服务器上。
Config Server: 配置服务器。存储所有数据库元信息(路由、分片)的配置
片键(shard key)
为了在数据集合中分配文档,MongoDB 使用分片主键分割集合。
区块(chunk)
在一个 shard server 内部,MongoDB 还是会把数据分为 chunks,每个 chunk 代表这个 shard server 内部一部分数据。MongoDB 分割分片数据到区块,每一个区块包含基于分片主键的左闭右开的区间范围。
分片策略
范围分片(Range based sharding)
范围分片是基于分片主键的值切分数据,每一个区块将会分配到一个范围。 范围分片适合满足在一定范围内的查找,例如查找 X 的值在[20,30)之间的数据,mongo 路由根据 Config server 中存储的元数据,可以直接定位到指定的 shard 的 Chunk 中。
缺点: 如果 shard key 有明显递增(或者递减)趋势,则新插入的文档多会分布到同一个 chunk,无法扩展写的能力。
hash 分片(Hash based sharding)
Hash 分片是计算一个分片主键的 hash 值,每一个区块将分配一个范围的 hash 值。 Hash 分片与范围分片互补,能将文档随机的分散到各个 chunk,充分的扩展写能力,弥补了范围分片的不足,
缺点是不能高效的服务范围查询,所有的范围查询要分发到后端所有的 Shard 才能找出满足条件的文档。
组合片键 A + B(散列思想不能是直接 hash)
数据库中没有比较合适的片键供选择,或者是打算使用的片键基数太小(即变化少如星期只有 7 天 可变化),可以选另一个字段使用组合片键,甚至可以添加冗余字段来组合。一般是粗粒度+细粒 度进行组合。
合理的选择 shard key
无非从两个方面考虑,数据的查询和写入,最好的效果就是数据查询时能命中更少的分片,数据写入时能够随机的写入每个分片,关键在于如何权衡性能和负载。
# 6.3.4 分片集群的搭建过程
# 1.配置 并启动 config 节点集群
节点 1 config-17017.conf
# 数据库文件位置
dbpath=config/config1
#日志文件位置
logpath=config/logs/config1.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork = true
bind_ip=0.0.0.0
port = 17017
# 表示是一个配置服务器
configsvr=true
#配置服务器副本集名称
replSet=configsvr
2
3
4
5
6
7
8
9
10
11
12
13
14
节点 2 config-17018.conf
# 数据库文件位置
dbpath=config/config2
#日志文件位置
logpath=config/logs/config.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork = true
bind_ip=0.0.0.0
port = 17018
# 表示是一个配置服务器
configsvr=true
#配置服务器副本集名称
replSet=configsvr
2
3
4
5
6
7
8
9
10
11
12
13
14
节点 3 config-17019.conf
# 数据库文件位置
dbpath=config/config3
#日志文件位置
logpath=config/logs/config3.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork = true
bind_ip=0.0.0.0
port = 17019
# 表示是一个配置服务器
configsvr=true
#配置服务器副本集名称
replSet=configsvr
2
3
4
5
6
7
8
9
10
11
12
13
14
启动配置节点
./bin/mongod -f config/config-17017.conf
./bin/mongod -f config/config-17018.conf
./bin/mongod -f config/config-17019.conf
2
3
进入任意节点的 mongo shell 并添加 配置节点集群 注意 use admin
/bin/mongo --port 17017
use admin
var cfg ={"_id":"configsvr",
"members":[
{"_id":1,"host":"192.168.211.133:17017"},
{"_id":2,"host":"192.168.211.133:17018"},
{"_id":3,"host":"192.168.211.133:17019"}]
};
rs.initiate(cfg)
2
3
4
5
6
7
8
9
# 2.配置 shard 集群
shard1 集群搭建 37017 到 37019
dbpath=shard/shard1/shard1-37017
bind_ip=0.0.0.0
port=37017
fork=true
logpath=shard/shard1/shard1-37017.log
replSet=shard1
shardsvr=true
dbpath=shard/shard1/shard1-37018
bind_ip=0.0.0.0
port=37018
fork=true
logpath=shard/shard1/logs/shard1-37018.log
replSet=shard1
shardsvr=true
dbpath=shard/shard1/shard1-37019
bind_ip=0.0.0.0
port=37019
fork=true
logpath=shard/shard1/logs/shard1-37019.log
replSet=shard1
shardsvr=true
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
启动每个mongod 然后进入其中一个进行集群配置
var cfg ={"_id":"shard1",
"protocolVersion" : 1,
"members":[
{"_id":1,"host":"192.168.211.133:37017"},
{"_id":2,"host":"192.168.211.133:37018"},
{"_id":3,"host":"192.168.211.133:37019"}
]
};
rs.initiate(cfg)
rs.status()
2
3
4
5
6
7
8
9
10
11
shard2 集群搭建 47017 到 47019
dbpath=shard/shard2/shard2-47017
bind_ip=0.0.0.0
port=47017
fork=true
logpath=shard/shard2/logs/shard2-47017.log
replSet=shard2
shardsvr=true
dbpath=shard/shard2/shard2-47018
bind_ip=0.0.0.0
port=47018
fork=true
logpath=shard/shard2/logs/shard2-47018.log
replSet=shard2
shardsvr=true
dbpath=shard/shard2/shard2-47019
bind_ip=0.0.0.0
port=47019
fork=true
logpath=shard/shard2/logs/shard2-47019.log
replSet=shard2
shardsvr=true
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
启动每个mongod 然后进入其中一个进行集群配置
var cfg ={"_id":"shard2",
"protocolVersion" : 1,
"members":[
{"_id":1,"host":"192.168.211.133:47017"},
{"_id":2,"host":"192.168.211.133:47018"},
{"_id":3,"host":"192.168.211.133:47019"}
]
};
rs.initiate(cfg)
rs.status()
2
3
4
5
6
7
8
9
10
11
# 3.配置和启动 路由节点
route-27017.conf
port=27017
bind_ip=0.0.0.0
fork=true
logpath=route/logs/route.log
configdb=configsvr/192.168.211.133:17017,192.168.211.133:17018,192.168.211.133:17019
2
3
4
5
启动路由节点使用 mongos (注意不是 mongod)
./bin/mongos -f route/route-27017.conf
# 4. mongos(路由)中添加分片节点
进入路由 mongos
mongo --port 27017
sh.status()
sh.addShard("shard1/192.168.211.133:37017,192.168.211.133:37018,192.168.211.133:
37019");
sh.addShard("shard2/192.168.211.133:47017,192.168.211.133:47018,192.168.211.133:
47019");
sh.status()
2
3
4
5
6
7
# 5. 开启数据库和集合分片(指定片键)
继续使用 mongos 完成分片开启和分片大小设置
为数据库开启分片功能
sh.enableSharding("lagou_resume")
为指定集合开启分片功能
sh.shardCollection("lagou_resume.lagou_resume_datas",{"片键字段名如 name":索引说
明})
2
3
4
5
# 6. 向集合中插入数据测试
通过路由循环向集合中添加数
use lagou_resume;
for(var i=1;i<= 1000;i++){
db.lagou_resume_datas.insert({"name":"test"+i,
salary:(Math.random()*20000).toFixed(2)});
}
2
3
4
5
# 7.验证分片效果
分别进入 shard1 和 shard2 中的数据库 进行验证
# 第七部分 MongoDB 安全认证
# 7.1 安全认证概述
MongoDB 默认是没有账号的,可以直接连接,无须身份验证。实际项目中肯定是要权限验证的,否则后果不堪设想。从 2016 年开始 发生了多起 MongoDB 黑客赎金事件,大部分 MongoDB 安全问题暴露出了安全问题的短板其实是用户,首先用户对于数据库的安全不重视,其次用户在使用过程中可能没有养成定期备份的好习惯,最后是企业可能缺乏有经验和技术的专业人员。所以对 MongoDB 进行安全认证 是必须要做的。
# 7.2 用户相关操作
# 7.2.1 切换到 admin 数据库对用户的添加
use admin;
db.createUser(userDocument):
用于创建 MongoDB 登录用户以及分配权限的方法
db.createUser(
{
user: "账号",
pwd: "密码",
roles: [
{ role: "角色", db: "安全认证的数据库" },
{ role: "角色", db: "安全认证的数据库" }
]
}
)
2
3
4
5
6
7
8
9
10
user:创建的用户名称,如 admin、root 、lagou
pwd:用户登录的密码
roles:为用户分配的角色,不同的角色拥有不同的权限,参数是数组,可以同时设置多个
role:角色,MonngoDB 已经约定好的角色,不同的角色对应不同的权限 后面会对 role 做详细解释
db:数据库实例名称,如 MongoDB 4.0.2 默认自带的有 admin、local、config、test 等,即为哪个数据库实例 设置用户
举例:
db.createUser(
{
user:"root",
pwd:"123321",
roles:[{role:"root",db:"admin"}]
}
)
2
3
4
5
6
7
# 7.2.2 修改密码
db.changeUserPassword( 'root' , 'rootNew' );
# 7.2.3 用户添加角色
db.grantRolesToUser( '用户名' , [{ role: '角色名' , db: '数据库名'}])
# 7.2.4 以 auth 方向启动 mongod
./bin/mongod -f conf/mongo.conf --auth
(也可以在 mongo.conf 中添加 auth=true 参数)
# 7.2.5 验证用户
db.auth("账号","密码")
# 7.2.6 删除用户
db.dropUser("用户名")
# 7.3 角色
# 7.3.1 数据库内置的角色
read:允许用户读取指定数据库
readWrite:允许用户读写指定数据库
dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问 system.profile
userAdmin:允许用户向 system.users 集合写入,可以找指定数据库里创建、删除和管理用户
clusterAdmin:只在 admin 数据库中可用,赋予用户所有分片和复制集相关函数的管理权限
readAnyDatabase:只在 admin 数据库中可用,赋予用户所有数据库的读权限
readWriteAnyDatabase:只在 admin 数据库中可用,赋予用户所有数据库的读写权限
userAdminAnyDatabase:只在 admin 数据库中可用,赋予用户所有数据库的 userAdmin 权限
dbAdminAnyDatabase:只在 admin 数据库中可用,赋予用户所有数据库的 dbAdmin 权限 root:只在 admin 数据库中可用。超级账号,超级权限
dbOwner:库拥有者权限,即 readWrite、dbAdmin、userAdmin 角色的合体
# 7.3.2 各个类型用户对应的角色
数据库用户角色:read、readWrite
数据库管理角色:dbAdmin、dbOwner、userAdmin
集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager
备份恢复角色:backup、restore;
所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、 dbAdminAnyDatabase
超级用户角色:root 这里还有几个角色间接或直接提供了系统超级用户的访问(dbOwner 、userAdmin、 userAdminAnyDatabase)
- 01
- idea 热部署插件 JRebel 安装及破解,不生效问题解决04-10
- 02
- spark中代码的执行位置(Driver or Executer)12-12
- 03
- 大数据技术之 SparkStreaming12-12