提高查询效率,如果没有索引查询将是全集合扫描。
1、给单字段添加索引
> db.files.getIndexes() # 查看集合索引,默认有一个索引_id [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_" } ] > > > db.files.createIndex({id:-1}) # 添加索引 { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 } > db.files.getIndexes() # 查看集合索引 [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_" }, { "v" : 2, "key" : { "id" : -1 }, "name" : "id_-1" } ] >
2、添加复合索引
> db.files.createIndex({id:-1,typeName:1}) # 添加复合索引(按照id升序,typeName降序排序) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 2, "numIndexesAfter" : 3, "ok" : 1 } > db.files.getIndexes() # 查看集合索引 [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_" }, { "v" : 2, "key" : { "id" : -1 }, "name" : "id_-1" }, { "v" : 2, "key" : { "id" : -1, "typeName" : 1 }, "name" : "id_-1_typeName_1" } ]
3、删除索引
> db.files.dropIndex({id:-1}) # 删除指定索引 { "nIndexesWas" : 3, "ok" : 1 } > db.files.getIndexes() [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_" }, { "v" : 2, "key" : { "id" : -1, "typeName" : 1 }, "name" : "id_-1_typeName_1" } ] > db.files.dropIndex("id_-1_typeName_1") # 根据名称删除指定索引 { "nIndexesWas" : 2, "ok" : 1 } > > db.files.getIndexes() [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_" } ] > > db.files.dropIndexes() # 删除所有除‘_id’之外的索引 { "nIndexesWas" : 1, "msg" : "non-_id indexes dropped for collection", "ok" : 1 } > db.files.getIndexes() # 查询发现‘_id’索引依然存在 [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_" } ] >
4、索引的使用
4-1、执行计划
db.collection.find(query,options).explain(options)
> db.files.find().explain() # 全聚合扫描 { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "articledb.files", "indexFilterSet" : false, "parsedQuery" : { }, "queryHash" : "8B3D4AB8", "planCacheKey" : "8B3D4AB8", "winningPlan" : { "stage" : "COLLSCAN", # 全聚合扫描 "direction" : "forward" }, "rejectedPlans" : [ ] }, "serverInfo" : { "host" : "localhost", "port" : 27017, "version" : "4.4.14", "gitVersion" : "0b0843af97c3ec9d2c0995152d96d2aad725aab7" }, "ok" : 1 } > db.files.find({id:2}).explain() # 根据id查询数据 { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "articledb.files", "indexFilterSet" : false, "parsedQuery" : { "id" : { "$eq" : 2 } }, "queryHash" : "6DAB46EC", "planCacheKey" : "801B9D84", "winningPlan" : { "stage" : "FETCH", # fetch抓取,说明走了id索引 "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "id" : -1 }, "indexName" : "id_-1", "isMultiKey" : false, "multiKeyPaths" : { "id" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "id" : [ "[2.0, 2.0]" ] } } }, "rejectedPlans" : [ ] }, "serverInfo" : { "host" : "localhost", "port" : 27017, "version" : "4.4.14", "gitVersion" : "0b0843af97c3ec9d2c0995152d96d2aad725aab7" }, "ok" : 1 } >
SpringBoot操作MongoDB
1、配置依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency> </dependencies>
2、application.yml配置文件
spring: data: mongodb: database: articledb host: 192.168.2.120 password: port: 27017 # 也可以直接使用uri连接(连接副本集也可以使用uri) # uri: mongodb://192.168.2.120:27017/articledb server: port: 8090
3、编写实体类、repository、service、controller
// 1、bean import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.index.CompoundIndex; import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.Field; //{ "_id":ObjectId("62b4360a70d355701ebb38d9"), "id":3, "typeName":"演示专用", "typeInfo":"abc2", "fkParentId":2, "wordTplAddress":"", "fkBorrowFlow":11521 } //可以省略,如果省略,则默认使用类名小写映射集合 @Document("files") //复合索引(推荐使用命令行方式去添加索引) //@CompoundIndex(def = "{'id':1, 'typeInfo':-1}") public class File { //主键标识,该属性的值会自动对应mongodb的主键字段"_id",如果该属性名就叫"id",则该注解可以省略 @Id private String _id; //添加一个单字段索引 //@Indexed private Integer id; //该属性对应MongoDB的属性字段名称,如果一致可以省略这个注解 @Field("typeInfo") private String typeInfo; private String typeName; private Integer fkParentId; private String wordTplAddress; private String fkBorrowFlow; // get / set / toString } // 2、repository层 import com.re.po.File; import org.springframework.data.mongodb.repository.MongoRepository; //file文档对象的持久化层 public interface FilesRepository extends MongoRepository<File, String> { //根据typeName获取分页数据 Page<File> findByTypeName(String typeName, Pageable pageable); } // 3、service层 import com.re.dao.FilesRepository; import com.re.po.File; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class FileService { @Autowired private FilesRepository filesRepository; /** * 保存file实体类 */ public void saveFiles(File file){ //如果需要自定义主键,这里可以指定主键;如果不自定义主键,MongoDB会自动生成主键 //设置一些默认初始值 //调用dao filesRepository.save(file); } /** * 根据file的id删除file文档 * @param id */ public void deleteFileById(String id){ //调用dao filesRepository.deleteById(id); } /** * 根据file更新file文档数据 * @param file */ public void updateFile(File file){ //调用dao filesRepository.save(file); } /** * 查询所有文档 * @return */ public List<File> findFilesList(){ //调用dao return filesRepository.findAll(); } /** * 根据id查询file文档 * @return */ public File findFileById(String id){ //调用dao return filesRepository.findById(id).get(); } /** * 根据typeName分页查询文档 * @param typeName * @return */ public Page<File> findByTypeName(String typeName, int page, int size) { return filesRepository.findByTypeName(typeName, PageRequest.of(page-1,size)); } } // 4、controller层 import com.re.po.File; import com.re.service.FileService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController @RequestMapping("/file") public class FileController { @Autowired private FileService fileService; /** * 查询files集合内所有的文档 * @return */ @GetMapping("/getAllFiles") public List<File> getAllFiles(){ return fileService.findFilesList(); } /** * 根据id分页查询文档 * @param typeName * @return */ @GetMapping("/findByTypeName") public Page<File> findByTypeName(String typeName, Integer page, Integer size){ if (typeName==null || page == null || size == null){ return null; } Page<File> byTypeName = fileService.findByTypeName("typeName", page, size); return byTypeName; } }
副本集
1、两种类型三种角色
1、两种类型:(主节点:数据操作的主要连接点,可读写。)(从节点:备份,可以读或选举。)
2、三种角色:(主要成员:主要接收所有写操作。就是主节点。)(副本成员:从主节点复制相同数据集,不可写操作。是一种从节点类型。)(仲裁者:不保留任何数据的副本,只具有投票选举的作用。)
2、搭建副本集
2-1、主节点
1、创建目录
[root@localhost /]# mkdir -p /mongodb/myrs_27017/data/db [root@localhost /]# mkdir -p /mongodb/myrs_27017/log [root@localhost /]# mkdir -p /mongodb/myrs_27018/data/db [root@localhost /]# mkdir -p /mongodb/myrs_27018/log [root@localhost /]# mkdir -p /mongodb/myrs_27019/data/db [root@localhost /]# mkdir -p /mongodb/myrs_27019/log
2、配置文件
[root@localhost myrs_27017]# vi /mongodb/myrs_27017/mongod.conf
内容
systemLog: #MongoDB发送所有日志输出的目标指定为文件 destination: file #mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径 path: "/mongodb/myrs_27017/log/mongod.log" #当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。 logAppend: true storage: #mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。 dbPath: "/mongodb/myrs_27017/data/db" journal: #启用或禁用持久性日志以确保数据文件保持有效和可恢复。 enabled: true processManagement: #启用在后台运行mongos或mongod进程的守护进程模式。 fork: true # 指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID pidFilePath: "/mongodb/myrs_27017/log/mongod.pid" net: #服务实例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip #bindIpAll: true # 服务实例绑定的IP bindIp: localhost,192.168.2.120 #bindIp #绑定的端口,默认是27017 port: 27017 replication: # 副本集的名称(多个节点的副本集名称要一致) replSetName: myrs
3、启动
[root@localhost myrs_27017]# /usr/local/mongodb/bin/mongod -f /mongodb/myrs_27017/mongod.conf
2-2、从节点
1、配置文件
[root@localhost myrs_27018]# vi /mongodb/myrs_27018/mongod.conf
内容
systemLog: #MongoDB发送所有日志输出的目标指定为文件 destination: file #mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径 path: "/mongodb/myrs_27018/log/mongod.log" #当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。 logAppend: true storage: #mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。 dbPath: "/mongodb/myrs_27018/data/db" journal: #启用或禁用持久性日志以确保数据文件保持有效和可恢复。 enabled: true processManagement: #启用在后台运行mongos或mongod进程的守护进程模式。 fork: true # 指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID pidFilePath: "/mongodb/myrs_27018/log/mongod.pid" net: #服务实例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip #bindIpAll: true # 服务实例绑定的IP bindIp: localhost,192.168.2.120 #bindIp #绑定的端口,默认是27018 port: 27018 replication: # 副本集的名称(多个节点的副本集名称要一致) replSetName: myrs
2、启动
[root@localhost myrs_27018]# /usr/local/mongodb/bin/mongod -f /mongodb/myrs_27018/mongod.conf
2-3、仲裁节点(不保存数据)
1、配置文件
[root@localhost myrs_27019]# vi /mongodb/myrs_27019/mongod.conf
内容
systemLog: #MongoDB发送所有日志输出的目标指定为文件 destination: file #mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径 path: "/mongodb/myrs_27019/log/mongod.log" #当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。 logAppend: true storage: #mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。 dbPath: "/mongodb/myrs_27019/data/db" journal: #启用或禁用持久性日志以确保数据文件保持有效和可恢复。 enabled: true processManagement: #启用在后台运行mongos或mongod进程的守护进程模式。 fork: true # 指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID pidFilePath: "/mongodb/myrs_27019/log/mongod.pid" net: #服务实例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip #bindIpAll: true # 服务实例绑定的IP bindIp: localhost,192.168.2.120 #bindIp #绑定的端口,默认是27019 port: 27019 replication: # 副本集的名称(多个节点的副本集名称要一致) replSetName: myrs
2、启动
[root@localhost myrs_27019]# /usr/local/mongodb/bin/mongod -f /mongodb/myrs_27019/mongod.conf
2-4、初始化副本集
2-4-1、主节点
[root@localhost /]# /usr/local/mongodb/bin/mongo MongoDB shell version v4.4.14 connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb Implicit session: session { "id" : UUID("4a1f2f91-b631-479a-bd20-8efd08142bb3") } MongoDB server version: 4.4.14 .......... > rs.initiate() # 初始化 { "info2" : "no configuration specified. Using a default configuration for the set", "me" : "192.168.2.120:27017", "ok" : 1 } myrs:SECONDARY> myrs:PRIMARY> # PRIMARY主节点
2-4-2、从节点(在主节点添加从节点rs.add(host)或者rs.add(host, false) )
rs.add(host, arbiterOnly)
如果host是一个字符串应该包含ip和端口号,也可以是一个主机成员配置文档。arbiterOnly为true表示为仲裁节点,如果为false表示为从节点。
myrs:PRIMARY> rs.add("192.168.2.120:27018", false) # 在主节点添加从节点 { "ok" : 1, # 看到ok代表成功 "$clusterTime" : { "clusterTime" : Timestamp(1656549106, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1656549106, 1) } myrs:PRIMARY> rs.config() # 再次查看发现27018也进来了 { "_id" : "myrs", "version" : 2, "term" : 1, "protocolVersion" : NumberLong(1), "writeConcernMajorityJournalDefault" : true, "members" : [ { "_id" : 0, "host" : "192.168.2.120:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 1, "host" : "192.168.2.120:27018", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ]...... } myrs:PRIMARY>
2-4-3、仲裁节点
rs.addArb(host)或者rs.add(host,true)
myrs:PRIMARY> rs.addArb("192.168.2.120:27019") # 添加仲裁节点 { "ok" : 1, # 看到ok代表成功 "$clusterTime" : { "clusterTime" : Timestamp(1656549589, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1656549589, 1) } myrs:PRIMARY> rs.config() # 再次查看发现27019也进来了 { "_id" : "myrs", "version" : 3, "term" : 1, "protocolVersion" : NumberLong(1), "writeConcernMajorityJournalDefault" : true, "members" : [ { "_id" : 0, "host" : "192.168.2.120:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 1, "host" : "192.168.2.120:27018", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 2, "host" : "192.168.2.120:27019", "arbiterOnly" : true, "buildIndexes" : true, "hidden" : false, "priority" : 0, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ], ...... } myrs:PRIMARY>
2-5、添加数据
myrs:SECONDARY> show dbs uncaught exception: Error: listDatabases failed:{ "topologyVersion" : { "processId" : ObjectId("62bcec94c6af0385dfdd73ed"), "counter" : NumberLong(4) }, "operationTime" : Timestamp(1656551618, 1), "ok" : 0, "errmsg" : "not master and slaveOk=false", "code" : 13435, "codeName" : "NotPrimaryNoSecondaryOk", "$clusterTime" : { "clusterTime" : Timestamp(1656551618, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } } : _getErrorWithCode@src/mongo/shell/utils.js:25:13 Mongo.prototype.getDBs/<@src/mongo/shell/mongo.js:147:19 Mongo.prototype.getDBs@src/mongo/shell/mongo.js:99:12 shellHelper.show@src/mongo/shell/utils.js:937:13 shellHelper@src/mongo/shell/utils.js:819:15 @(shellhelp2):1:1 myrs:SECONDARY> rs.slaveOk() # 开启自己为从节点,开启读数据权限 WARNING: slaveOk() is deprecated and may be removed in the next major release. Please use secondaryOk() instead. myrs:SECONDARY> show dbs admin 0.000GB article 0.000GB config 0.000GB local 0.000GB myrs:SECONDARY> use article switched to db article myrs:SECONDARY> db.files.find() # 从节点也可以读取到数据 { "_id" : 1, "typeName" : "演示专用", "id" : 1001 } myrs:SECONDARY> rs.slaveOk(false) # 取消作为奴役节点(不具备读权限) WARNING: slaveOk() is deprecated and may be removed in the next major release. Please use secondaryOk() instead. myrs:SECONDARY> db.files.find() # 再次读取数据就报错了 Error: error: { "topologyVersion" : { "processId" : ObjectId("62bcec94c6af0385dfdd73ed"), "counter" : NumberLong(4) }, "operationTime" : Timestamp(1656558608, 1), "ok" : 0, "errmsg" : "not master and slaveOk=false", "code" : 13435, "codeName" : "NotPrimaryNoSecondaryOk", "$clusterTime" : { "clusterTime" : Timestamp(1656558608, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
2-6、主节点的选举
1、主节点故障。
2、主节点网络不可达(默认心跳为10秒)。
3、人工干预(rs.stepDown())。
当从节点挂掉后然后主节点数据发生变更后,从节点又重新启动起来时也可以读取到在这期间主节点变化的数据。
当主节点挂掉后,从节点会被选举为主节点(票数得到半数以上)。
3、SpringDataMongoDB连接副本集
3-1、配置文件
spring: data: mongodb: # database: articledb # host: 192.168.2.120 # password: # port: 27017 # 也可以直接使用uri连接 # 连接副本集 uri: mongodb://192.168.2.120:27017,192.168.2.120:27018,192.168.2.120:27019/articledb?connect=replicaSet&slaveOk=true&replicaSet=myrs server: port: 8090
目录
3、编写实体类、repository、service、controller