官方文档中文版

mongo中文社区

代码地址

1.认识mongdb

mongodb是一个开源,文档类型的非关系型数据库,是最像关系型数据库的非关系型数据库。
mongodb存储的是由字段和值组成的数据类型称为BSON,类似于JSON对象。包含字段的值,数组,文档数组。
与mysql的对比:

mysql mongodb 解释/说明
column field 数据字段/域
database database 数据库
index index 索引
primary key primary key 主键,MongoDB自动将_id字段设置为主键
row document 数据记录行/文档
table collection 数据库表/集合
table joins 表连接/mongo只能嵌入文档

主要特性

  • 高性能
    • 对嵌入式数据模型的支持减少了数据库系统上的I / O操作
    • 索引支持更快的查询,并且可以包含来自嵌入式文档和数组的键。
  • 高可用 -副本集
    • 自动_故障转移
    • 数据冗余
  • 水平拓展
    • 分片将数据分布在一个集群的集群上
    • 3.4以后支持基于分片键创建数据区域
  • 丰富的查询支持
    • 支持CRUD 聚合,空间地理查询

应用场景

  1. 数据量较大;
  2. 读写频繁;
  3. 价值较低的数据,对事物要求不高。

集群模式-副本集
为了保证数据的冗余和可靠性和提高整个系统的负载 所以需要搭建mongo集群,mongo不是mysql那种主备模式,mongo是有自动故障恢复功能的主从集群
主从集群和副本集最大的区别就是副本集没有固定的“主节点”;整个集群会选出一个“主节点”,当其挂掉后,又在剩下的从节点中选中其他节点为“主节点”,副本集总有一个活跃点(主、primary)和一个或多个备份节点(从、econdary)。

搭建mongo副本集 至少需要三个节点 有两种模式可选择

  • 主节点(Primary)类型:数据操作的主要连接点,可读写。

1.png

  • 次要(辅助、从)节点(Secondaries)类型:数据冗余备份节点,可以读或选举。


2.png

主节点类型:当主节点挂点后,两个从节点会竞争产生一个主节点 ,再当其中一个节点挂掉号 整个集群不可用 ,当挂掉的主节点启动后会变成从节点
从节点类型:带有仲裁节点,仲裁节点不存储数据,可以节省成本 当主节点挂掉后 仲裁节点可以投票 从节点变成主节点

2.docker 安装 mongdb 单机与副本集

创建单机只需要执行一次,也不需要指定副本集名称
此次没有挂载日志和存储目录 采用一主一从一仲裁

  1. docker run --name mongo3 -p 57017:27017 -id mongo --replSet "rs"
  2. docker run --name mongo2 -p 47017:27017 -id mongo --replSet "rs"
  3. docke run --name mongo1 -p 37017:27017 -id mongo --replSet "rs"

随便进入一个容器执行,如果是阿里云服务器,需指定外网ip
arbiterOnly:true 指定为仲裁节点

  1. rs.initiate({"_id":"rs",members:[{_id:0,host:"106.14.72.13:37017"},{_id:1,host:"106.14.72.13:47017"},{_id:2,host:"106.14.72.13:57017",arbiterOnly:true}]})

3.可视化工具与基本操作命令

nosqlbooster 可视化
mongdb-compass-可视化
文档的操作

  1. /********************数据库 操作*********/
  2. // 选择或创建数据库 use 数据库名称
  3. use articledb
  4. //查看有权限查看的所有的数据库命令
  5. show dbs
  6. show databases
  7. //查看当前正在使用的数据库
  8. db
  9. //删除数据库
  10. db.dropDatabase()
  11. /********************集合/表 操作*********/
  12. //显示创建集合 /表 db.createCollection(name)
  13. db.createCollection("mycollection")
  14. //当前库中的表
  15. show collections
  16. show tables
  17. //删除集合 db.collection.drop()
  18. db.comment.drop()
  19. //插入文档(单条) 不存在则会隐式创建
  20. db.comment.insert({ "articleid": "100000", "content": "今天天气真好,阳光明媚", "userid": "1001", "nickname": "Rose", "createdatetime": new Date(), "likenum": NumberInt(10), "state": null })
  21. //批量插入
  22. db.comment.insertMany([{ "_id": "1", "articleid": NumberInt(100001), "content": "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。", "userid": NumberInt(1002), "nickname": "相忘于江湖", "createdatetime": new Date("2019-08-05T22:08:15.522Z"), "likenum": NumberInt(1000), "state": NumberInt(1) }, { "_id": "2", "articleid": NumberInt(100001), "content": "我夏天空腹喝凉开水,冬天喝温开水", "userid": NumberInt(1005), "nickname": "伊人憔悴", "createdatetime": new Date("2019-08-05T23:58:51.485Z"), "likenum": NumberInt(888), "state": NumberInt(1) }, { "_id": "3", "articleid": NumberInt(100001), "content": "我一直喝凉开水,冬天夏天都喝。", "userid": NumberInt(1004), "nickname": "杰克船长", "createdatetime": new Date("2019-08-06T01:05:06.321Z"), "likenum": NumberInt(666), "state": NumberInt(1) }, { "_id": "4", "articleid": NumberInt(100001), "content": "专家说不能空腹吃饭,影响健康。", "userid": NumberInt(1003), "nickname": "凯撒", "createdatetime": new Date("2019-08-06T08:18:35.288Z"), "likenum": NumberInt(2000), "state": NumberInt(1) }, { "_id": "5", "articleid": NumberInt(100001), "content": "研究表明,刚烧开的水千万不能喝,因为烫嘴。", "userid": NumberInt(1003), "nickname": "凯撒", "createdatetime": new Date("2019-08-06T11:01:02.521Z"), "likenum": NumberInt(3000), "state": NumberInt(1) }]);
  23. //批量插入 某行报错不会回滚可以加try catch
  24. try {
  25. db.comment.insertMany([{ "_id": "1", "articleid": NumberInt(100001), "content": "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。", "userid": NumberInt(1002), "nickname": "相忘于江湖", "createdatetime": new Date("2019-08-05T22:08:15.522Z"), "likenum": NumberInt(1000), "state": NumberInt(1) }, { "_id": "2", "articleid": NumberInt(100001), "content": "我夏天空腹喝凉开水,冬天喝温开水", "userid": NumberInt(1005), "nickname": "伊人憔悴", "createdatetime": new Date("2019-08-05T23:58:51.485Z"), "likenum": NumberInt(888), "state": NumberInt(1) }, { "_id": "3", "articleid": NumberInt(100001), "content": "我一直喝凉开水,冬天夏天都喝。", "userid": NumberInt(1004), "nickname": "杰克船长", "createdatetime": new Date("2019-08-06T01:05:06.321Z"), "likenum": NumberInt(666), "state": NumberInt(1) }, { "_id": "4", "articleid": NumberInt(100001), "content": "专家说不能空腹吃饭,影响健康。", "userid": NumberInt(1003), "nickname": "凯撒", "createdatetime": new Date("2019-08-06T08:18:35.288Z"), "likenum": NumberInt(2000), "state": NumberInt(1) }, { "_id": "5", "articleid": NumberInt(100001), "content": "研究表明,刚烧开的水千万不能喝,因为烫嘴。", "userid": NumberInt(1003), "nickname": "凯撒", "createdatetime": new Date("2019-08-06T11:01:02.521Z"), "likenum": NumberInt(3000), "state": NumberInt(1) }]);
  26. } catch (e) {
  27. print(e);
  28. }
  29. /********************文档 操作*********/
  30. //查询所有
  31. db.comment.find()
  32. db.comment.find({})
  33. //条件查询
  34. db.comment.find({userid:'1003'})
  35. //查询单条
  36. db.comment.findOne({userid:'1003'})
  37. //投影查询
  38. db.comment.find({userid:NumberInt(1003)},{userid:1,nickname:1})
  39. //覆盖修改 其他字段全不见
  40. db.comment.update({_id:"1"},{likenum:NumberInt(1001)})
  41. //局部修改
  42. db.comment.update({_id:"2"},{$set:{likenum:NumberInt(889)}})
  43. //批量更新 增加标志 multi
  44. db.comment.update({userid:"1003"},{$set:{nickname:"凯撒大帝"}},{multi:true})
  45. //自增长
  46. db.comment.update({_id:"3"},{$inc:{likenum:NumberInt(1)}})
  47. //根据条件删除 没有条件删除全部
  48. db.comment.remove({_id:"1"})
  49. //统计记录数 没有参数统计全部
  50. db.comment.count({userid:"1003"})
  51. //分页操作 ->截取前三位
  52. db.comment.find().limit(3)
  53. //分页操作 跳过多少行,查询多少行 类似sql limit 4,6
  54. db.comment.find().skip(3).limit(2)
  55. //排序操作 -1 倒序 1升序
  56. db.comment.find().sort({userid:-1,likenum:1})
  57. //复杂查询 ->正则表达式匹配
  58. db.comment.find({content:/^专家/})
  59. //比较查询
  60. db.comment.find({ "field" : { $gt: value }}) // 大于: field > value
  61. db.comment.find({ "field" : { $lt: value }}) // 小于: field < value
  62. db.comment.find({ "field" : { $gte: value }}) // 大于等于: field >= value
  63. db.comment.find({ "field" : { $lte: value }}) // 小于等于: field <= value
  64. db.comment.find({ "field" : { $ne: value }}) // 不等于: field != value
  65. //包含查询
  66. db.comment.find({userid:{$in:["1003","1004"]}})
  67. //不包含查询
  68. db.comment.find({userid:{$nin:["1003","1004"]}})
  69. //连接查询 and
  70. db.comment.find({$and:[{likenum:{$gte:NumberInt(700)}},{likenum:{$lt:NumberInt(2000)}}]})
  71. //连接查询 or
  72. db.comment.find({$or:[ {userid:"1003"} ,{likenum:{$lt:1000} }]})

4.索引

官网链接
索引支持在MongoDB中高效地执行查询。如果没有索引,MongoDB必须执行全集合扫描,这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。如果查询存在适当的索引,MongoDB可以使用该索引限制必须检查的文档数。
mongodb的数据结构是B-tree

4.1 单字段索引

在文档上的单个字段创建自定义的升序/降序索引,称为单字段索引
11.png

4.2 复合索引

复合索引中列出的字段顺序具有重要意义。例如,如果复合索引由{ userid: 1, score: -1 } 组成,则索引首先按userid正序排序,然后在每个userid的值内,再在按score倒序排序。
22.png

3.3 其他索引
地理空间索引(Geospatial Index)、文本索引(Text Indexes)、哈希索引(Hashed Indexes)。

3.4 关于索引的语法

  1. //查看索引
  2. db.collection.getIndexes()
  3. //创建索引
  4. db.collection.createIndex(keys, options)
  5. //移除索引
  6. db.collection.dropIndex(index)

5.分片集群

分片是一种将数据分配到多个机器上的方法。MongoDB通过分片技术来支持具有海量数据集和高吞吐量操作的部署方案
数据库系统的数据集或应用的吞吐量比较大的情况下,会给单台服务器的处理能力带来极大的挑战。例如,高查询率会耗尽服务器的CPU资源。工作的数据集大于系统的内存压力、磁盘驱动器的I/O容量。

分片集群包含组件

  • 分片:每个shard(分片)包含被分片的数据集中的一个子集。每个分片可以被部署为副本集架构。
  • mongs:mongos充当查询路由器,在客户端应用程序和分片集群之间提供接口。
  • config服务器:mongos充当查询路由器,在客户端应用程序和分片集群之间提供接口。

分片的优势

  • 读写负载
  • 存储容量
  • 高可用性

    6.安全认证

    1.启用访问控制
    在实例启动时添加选项—auth 或指定启动配置文件中添加选项auth=true 容器启动配置-auth为true
    2.角色
    在MongoDB中通过角色对用户授予相应数据库资源的操作权限,每个角色当中的权限可以显式指定,也可以通过继承其他角色的权限,或者两都都存在的权限。
    3.权限
    权限由指定的数据库资源(resource)以及允许在指定资源上进行的操作(action)组成。
  1. 资源(resource)包括:数据库、集合、部分集合和集群;
  2. 操作(action)包括:对资源进行的增、删、改、查(CRUD)操作。
    在角色定义时可以包含一个或多个已存在的角色,新创建的角色会继承包含的角色所有的权限。在同一个数据库中,新创建角色可以继承其他角色的权限,在admin 数据库中创建的角色可以继承在其它任意数据库中角色的权限。
  1. // 查询所有角色权限(仅用户自定义角色)
  2. db.runCommand({ rolesInfo: 1 })
  3. // 查询所有角色权限(包含内置角色)
  4. db.runCommand({ rolesInfo: 1, showBuiltinRoles: true })
  5. // 查询当前数据库中的某角色的权限
  6. db.runCommand({ rolesInfo: "<rolename>" })
  7. // 查询其它数据库中指定的角色权限
  8. db.runCommand({ rolesInfo: { role: "<rolename>", db: "<database>" } }
角色 权限描述
read 可以读取指定数据库中任何数据。
readWrite 可以读写指定数据库中任何数据,包括创建、重命名、删除集合。
readAnyDatabase 可以读取所有数据库中任何数据(除了数据库config和local之外)。
readWriteAnyDatabase 可以读写所有数据库中任何数据(除了数据库config和local之外)。
userAdminAnyDatabase 可以在指定数据库创建和修改用户(除了数据库config和local之外)。
dbAdminAnyDatabase 可以读取任何数据库以及对数据库进行清理、修改、压缩、获取统计信息、执行检查等操作(除了数据库config和local之外)。
dbAdmin 可以读取指定数据库以及对数据库进行清理、修改、压缩、获取统计信息、执行检查等操作。
userAdmin 可以在指定数据库创建和修改用户。
clusterAdmin 可以对整个集群或数据库系统进行管理操作。
backup 备份MongoDB数据最小的权限。
restore 从备份文件中还原恢复MongoDB数据(除了system.profile集合)的权限。
root 超级账号,超级权限

7.springboot连接mongdb

加入jia包支持 自动跟随springboot版本

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-mongodb</artifactId>
  4. </dependency>

配置mongodb连接 权限认证如果不是在本库创建的用户 则需要在admin库进行权限认证。可以在/直接跟admin
然后在database 指定要连接的库

  1. spring:
  2. data:
  3. mongodb:
  4. #test 指需要连接的库 authSource=admin指权限在admin认证
  5. uri: mongodb://rem:123456@106.14.72.13:37017,106.14.72.13:47017/test?replicaSet=rs&readPreference=secondaryPreferred&connectTimeoutMS=15000&slaveOk=true&write=1&authSource=admin
  6. uuid-representation: standard
  7. auto-index-creation: true
  8. #database:

使用springboot连接mongodb 和使用其他springdata操作一样 继承MongoRepository

  1. package com.rem.mongodb.dao;
  2. import com.rem.mongodb.entity.Comment;
  3. import org.springframework.data.mongodb.repository.MongoRepository;
  4. import org.springframework.stereotype.Repository;
  5. /**
  6. * 集成springDate 基本操作
  7. * 评论的持久层接口
  8. *
  9. * @author Rem
  10. * @date 2021/11/9
  11. */
  12. @Repository
  13. public interface CommentRepository extends MongoRepository<Comment, String> {
  14. }

简单的CRUD以及分页操作,也可以直接使用mongoTemplate操作对象和dao层操作数据一样,如果不需要查询出对象再进行操作 比如加一操作,直接使用template操作更加简单。

springboot集成的事务对mongodb依然有效在方法上加上事务注解,在启动类加上开启事务或者单独配置事务类,当报错时不会对mongodb中数据进行操作。

  1. package com.rem.mongodb.service;
  2. import com.rem.mongodb.dao.CommentRepository;
  3. import com.rem.mongodb.entity.Comment;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.data.domain.*;
  6. import org.springframework.data.mongodb.core.MongoTemplate;
  7. import org.springframework.data.mongodb.core.query.Criteria;
  8. import org.springframework.data.mongodb.core.query.Query;
  9. import org.springframework.data.mongodb.core.query.Update;
  10. import org.springframework.stereotype.Service;
  11. import org.springframework.transaction.annotation.Transactional;
  12. import java.util.List;
  13. /**
  14. * 文章 服务层
  15. *
  16. * @author Rem
  17. * @date 2021/11/9
  18. */
  19. @Service
  20. public class CommentService {
  21. @Autowired
  22. private CommentRepository commentRepository;
  23. @Autowired
  24. private MongoTemplate mongoTemplate;
  25. /**
  26. * 保存一个评论
  27. *
  28. * @param comment
  29. */
  30. @Transactional
  31. public void saveComment(Comment comment) {
  32. //如果需要自定义主键,可以在这里指定主键;如果不指定主键,MongoDB会自动生成主键 设置一些默认初始值
  33. commentRepository.save(comment);
  34. }
  35. /**
  36. * 更新评论
  37. *
  38. * @param comment
  39. */
  40. public void updateComment(Comment comment) {
  41. commentRepository.save(comment);
  42. }
  43. /**
  44. * 根据id删除评论
  45. *
  46. * @param id
  47. */
  48. public void deleteCommentById(String id) {
  49. commentRepository.deleteById(id);
  50. }
  51. /**
  52. * 查询所有评论
  53. * ,
  54. *
  55. * @return
  56. */
  57. public List<Comment> findCommentList() {
  58. return commentRepository.findAll();
  59. }
  60. /**
  61. * 根据id查询评论
  62. *
  63. * @param id
  64. * @return
  65. */
  66. public Comment findCommentById(String id) {
  67. return commentRepository.findById(id).get();
  68. }
  69. /**
  70. * 根据多个id 批量查询评论
  71. *
  72. * @return
  73. */
  74. public List<Comment> findCommentList(List<String> ids) {
  75. return (List<Comment>) commentRepository.findAllById(ids);
  76. }
  77. /**
  78. * 根据条件查询
  79. *
  80. * @param comment
  81. * @return
  82. */
  83. public List<Comment> findComment(Comment comment) {
  84. return commentRepository.findAll(Example.of(comment));
  85. }
  86. /**
  87. * 模糊查询
  88. * StringMatcher 枚举中可选择各种匹配模式
  89. * ExampleMatcher.StringMatcher.DEFAULT 默认
  90. * ExampleMatcher.StringMatcher.EXACT 精确匹配
  91. * ExampleMatcher.StringMatcher.STARTING 前缀匹配
  92. * ExampleMatcher.StringMatcher.ENDING 后缀匹配
  93. * ExampleMatcher.StringMatcher.CONTAINING 模糊查询
  94. * ExampleMatcher.StringMatcher.REGEX 正则表达式
  95. * matching 匹配
  96. *
  97. * @param comment
  98. * @return
  99. */
  100. public List<Comment> findCommentDim(Comment comment) {
  101. ExampleMatcher matcher = ExampleMatcher.matching().withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING).withIgnoreCase(true);
  102. return commentRepository.findAll(Example.of(comment, matcher));
  103. }
  104. /**
  105. * 分页查询 并且按照 likenum 点赞数倒序
  106. *
  107. * @param comment
  108. * @return
  109. */
  110. public Page<Comment> findCommentByPage(Comment comment, Integer page, Integer size) {
  111. Sort sort = Sort.by(Sort.Direction.DESC, "likenum");
  112. //分页 0代表第一页
  113. Pageable pageable = PageRequest.of(page, size, sort);
  114. return commentRepository.findAll(Example.of(comment), pageable);
  115. }
  116. /**
  117. * 使用mongoTemplate 简单操作
  118. * 点赞数+1
  119. *
  120. * @param id
  121. */
  122. public void updateCommentLikenum(String id) {
  123. //查询对象
  124. Query query = Query.query(Criteria.where("_id").is(id));
  125. //更新对象
  126. Update update = new Update();
  127. //局部更新,相当于$set
  128. // update.set(key,value)
  129. //递增$inc
  130. // update.inc("likenum",1);
  131. update.inc("likenum");
  132. //参数1:查询对象
  133. //参数2:更新对象
  134. //参数3:集合的名字或实体类的类型Comment.class
  135. mongoTemplate.updateFirst(query, update, "comment");
  136. }
  137. }