MongoDB的使用(索引、聚合、整合应用、副本集、分片集群)

MongoDB的使用(索引、聚合、整合应用、副本集、分片集群)

springboot整合mongodb的demo

gitee.com/w--kk/mongo...

索引 index

docs.mongodb.com/manual/inde...

说明

索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。

索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构。

原理

从根本上说,MongoDB中的索引与其他数据库系统中的索引类似。MongoDB在集合层面上定义了索引,并支持对MongoDB集合中的任何字段或文档的子字段进行索引。

操作

0、创建索引

db.集合名称.createIndex(keys, options)

db.集合名称.createIndex({"title":1,"description":-1})
说明: 语法中 Key 值为你要创建的索引字段,1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1 即可。

createIndex() 接收可选参数,可选参数列表如下:

1、查看集合索引
db.集合名称.getIndexes()

expireAfterSeconds

2、查看集合索引大小
db.集合名称.totalIndexSize()

3、删除集合所有索引
db.集合名称.dropIndexes()

4、删除集合指定索引
db.集合名称.dropIndex("索引名称")

复合索引

说明: 一个索引的值是由多个 key 进行维护的索引的称之为复合索引
db.集合名称.createIndex({"title":1,"description":-1})

注意: mongoDB 中复合索引和传统关系型数据库一致都是左前缀原则

聚合 aggregate

说明

MongoDB 中聚合 (aggregate) 主要用于处理数据 (诸如统计平均值,求和等),并返回计算后的数据结果。有点类似 SQL 语句中的 count(*)

使用

java 复制代码
{
   title: 'MongoDB Overview', 
   description: 'MongoDB is no sql database',
   by_user: 'runoob.com',
   url: 'http://www.runoob.com',
   tags: ['mongodb', 'database', 'NoSQL'],
   likes: 100
},
{
   title: 'NoSQL Overview', 
   description: 'No sql database is very fast',
   by_user: 'runoob.com',
   url: 'http://www.runoob.com',
   tags: ['mongodb', 'database', 'NoSQL'],
   likes: 10
},
{
   title: 'Neo4j Overview', 
   description: 'Neo4j is no sql database',
   by_user: 'Neo4j',
   url: 'http://www.neo4j.com',
   tags: ['neo4j', 'database', 'NoSQL'],
   likes: 750
}

现在我们通过以上集合计算每个作者所写的文章数,使用aggregate()计算结果如下:

db.集合名称.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : 1}}}])

常见聚合表达式

$sum

Mongodb连接navicat

传统启动方式

./mongod --help

./mongod --port=27017 --dbpath=../data --logpath=../logs/mongo.log --bind_ip=0.0.0.0

注意:如果连接失败检查下防火墙

docker启动方式

docker run -d --name mongo -p 27017:27017 mongo:5.0.5 --bind_ip=0.0.0.0

进入 mongo 容器: docker exec -it 95126bf045bd bash

整合应用

说明: 这里主要以 springboot 应用为基础应用进行整合开发。

Spring Data : Spring 数据框架 JPA 、Redis、Elasticsearch、AMQP、MongoDB

JdbcTemplate
RedisTemplate
ElasticTempalte
AmqpTemplate
MongoTemplate
SpringBoot Spring Data MongoDB

环境搭建

引入依赖

java 复制代码
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

编写配置

java 复制代码
# mongodb 没有开启任何安全协议
# mongodb(协议)://121.5.167.13(主机):27017(端口)/wkk(库名)
spring.data.mongodb.uri=mongodb://192.168.175.129:27017/wkk

# mongodb 存在密码
#spring.data.mongodb.host=192.168.175.129
#spring.data.mongodb.port=27017
#spring.data.mongodb.database=wkk
#spring.data.mongodb.username=root
#spring.data.mongodb.password=root

集合操作

  • 创建集合
java 复制代码
@Test
public void testCreateCollection(){
  mongoTemplate.createCollection("users");//参数: 创建集合名称
}

注意:创建集合不能存在,存在报错

  • 删除集合
java 复制代码
@Test
public void testDeleteCollection(){
  mongoTemplate.dropCollection("users");
}

相关注解

@Document

  • 修饰范围: 用在类上
  • 作用: 用来映射这个类的一个对象为 mongo 中一条文档数据
  • 属性:(value 、collection ) 用来指定操作的集合名称

@Id

  • 修饰范围: 用在成员变量、方法上
  • 作用: 用来将成员变量的值映射为文档的_id 的值

@Field

  • 修饰范围: 用在成员变量、方法上
  • 作用: 用来将成员变量以及值映射为文档中一个key、value对
  • 属性: ( name,value)用来指定在文档中 key 的名称,默认为成员变量名

@Transient

  • 修饰范围: 用在成员变量、方法上
  • 作用 : 用来指定改成员变量,不参与文档的序列化

文档操作

查询

  1. Criteria

  2. 常见查询

java 复制代码
@Test
public void testQuery(){
  //基于 id 查询
  template.findById("1",User.class);

  //查询所有
  template.findAll(User.class);
  template.find(new Query(),User.class);

  //等值查询
  template.find(Query.query(Criteria.where("name").is("编程不良人")), 
               User.class);

  // > gt  < lt  >= gte  <= lte
  template.find(Query.query(Criteria.where("age").lt(25)),
                User.class);
  template.find(Query.query(Criteria.where("age").gt(25)),
                User.class);
  template.find(Query.query(Criteria.where("age").lte(25)),
                User.class);
  template.find(Query.query(Criteria.where("age").gte(25)),
                User.class);

  //and
  template.find(Query.query(Criteria.where("name").is("编程不良人")
                            .and("age").is(23)),User.class);



  //or
  Criteria criteria = new Criteria()
    .orOperator(Criteria.where("name").is("编程不良人_1"),
     Criteria.where("name").is("编程不良人_2"));
  template.find(Query.query(criteria), User.class);

  //and or
  Criteria criteria1 = new Criteria()
    .and("age").is(23)
    .orOperator(
    Criteria.where("name").is("编程不良人_1"),
    Criteria.where("name").is("编程不良人_2"));
  template.find(Query.query(criteria1), User.class);

  //sort 排序
  Query query = new Query();
  query.with(Sort.by(Sort.Order.desc("age")));//desc 降序  asc 升序
  template.find(query, User.class);


  //skip limit 分页
  Query queryPage = new Query();
  queryPage.with(Sort.by(Sort.Order.desc("age")))//desc 降序  asc 升序
    .skip(0) //起始条数
    .limit(4); //每页显示记录数
  template.find(queryPage, User.class);


  //count 总条数
  template.count(new Query(), User.class);

  //distinct 去重
  //参数 1:查询条件 参数 2: 去重字段  参数 3: 操作集合  参数 4: 返回类型
  template.findDistinct(new Query(), "name", 
                        User.class, String.class);
  
  //使用 json 字符串方式查询 
        Query query = new BasicQuery(
          "{$or:[{name:'编程不良人'},{name:'徐凤年'}]}", 
          "{name:0}");

  template.find(query, User.class);
}

添加

java 复制代码
@Test
public void testSaveOrUpdate(){
  User user = new User();
  user.setId("1");
  user.setAge(23);
  user.setName("编程不良人_1");
  user.setBir(new Date());
  User userDB = mongoTemplate.insert(user);//返回保存的对象 insert or save
  System.out.println(userDB);
}
  • insert: 插入重复数据时:insertDuplicateKeyException提示主键重复;save对已存在的数据进行更新。
  • save: 批处理操作时:insert可以一次性插入整个数据,效率较高;save需遍历整个数据,一次插入或更新,效率较低。

更新

java 复制代码
@Test
public void  testUpdate(){
  //1.更新条件
  Query query = Query.query(Criteria.where("age").is(23));
  //2.更新内容
  Update update = new Update();
  update.set("name","编程小陈陈");

  //单条更新
  mongoTemplate.updateFirst(query, update, User.class);
  //多条更新
  mongoTemplate.updateMulti(query, update, User.class);
  //更新插入
  mongoTemplate.upsert(query,update,User.class);

  //返回值均为 updateResult
  //System.out.println("匹配条数:" + updateResult.getMatchedCount());
  //System.out.println("修改条数:" + updateResult.getModifiedCount());
  //System.out.println("插入id_:" + updateResult.getUpsertedId());
}

删除

java 复制代码
@Test
public void testDelete(){
  //删除所有
  mongoTemplate.remove(new Query(),User.class);
  //条件删除
  mongoTemplate.remove(
    Query.query(Criteria.where("name").is("编程不良人")),
    User.class
  );
}

副本集 Replica Set

说明

docs.mongodb.com/manual/repl...

MongoDB 副本集(Replica Set)是有自动故障恢复功能的主从集群,有一个Primary节点和一个或多个Secondary节点组成。副本集没有固定的主节点, 当主节点发生故障时整个集群会选举一个主节点为系统提供服务以保证系统的高可用。

Automatic Failover

自动故障转移机制: 当主节点未与集合的其他成员通信超过配置的选举超时时间(默认为 10 秒)时,合格的辅助节点将调用选举以将自己提名为新的主节点。集群尝试完成新主节点的选举并恢复正常操作。

搭建副本集

  1. 创建数据目录

在安装目录中创建

  • mkdir -p ../repl/data1
  • mkdir -p ../repl/data2
  • mkdir -p ../repl/data3
  1. 搭建副本集

$ mongod --port 27017 --dbpath ../repl/data1 --bind_ip 0.0.0.0 --replSet myreplace/[121.5.167.13:27018,121.5.167.13:27019]
$ mongod --port 27018 --dbpath ../repl/data2 --bind_ip 0.0.0.0 --replSet myreplace/[121.5.167.13:27019,121.5.167.13:27017]
$ mongod --port 27019 --dbpath ../repl/data3 --bind_ip 0.0.0.0 --replSet myreplace/[121.5.167.13:27017,121.5.167.13:27018]

注意: --replSet 副本集   myreplace 副本集名称/集群中其他节点的主机和端口

docker run -d --name mongo -p 27017:27017 mongo:5.0.5 --bind_ip=0.0.0.0

  1. 配置副本集,连接任意节点
  • use admin
  • 初始化副本集
java 复制代码
> var config = { 
		_id:"myreplace", 
		members:[
		{_id:0,host:"192.168.175.129:27017"},
		{_id:1,host:"192.168.175.129:27018"},
		{_id:2,host:"192.168.175.129:27019"}]
}
> rs.initiate(config);//初始化配置 

分页查询 db.users.find().skip(0).limit(100);

访问27018副本

报错,默认从节点没有读的权限

./mongo --port 27018
访问27019副本

报错,默认从节点没有读的权限

./mongo --port 27019

  • 设置客户端临时可以访问 (让从节点有读的权限)

rs.slaveOk();

rs.secondaryOk();

用客户端搭建副本集群

分片集群 Sharding Cluster

说明

docs.mongodb.com/manual/shar...

副本集:自动故障转移  主从复制  集群

解决问题: 1. 数据冗余备份  2. 架构高可用

不能解决: 1. 单节点压力问题(硬件限制 并发访问压力)

分片(sharding)是指将数据拆分,将其分散存在不同机器的过程,有时也用分区(partitioning)来表示这个概念, 将数据分散在不同的机器上,不需要功能强大的大型计算机就能存储更多的数据,处理更大的负载。

分片目的是通过分片能够增加更多机器来应对不断的增加负载和数据,还不影响应用运行。

MongoDB支持自动分片,可以摆脱手动分片的管理困扰,集群自动切分数据做负载均衡。MongoDB分片的基本思想就是将集合拆分成多个块,这些快分散在若干个片里,每个片只负责总数据的一部分,应用程序不必知道哪些片对应哪些数据,甚至不需要知道数据拆分了,所以在分片之前会运行一个路由进程,mongos进程,这个路由器知道所有的数据存放位置,应用只需要直接与mongos交互即可。mongos自动将请求转到相应的片上获取数据,从应用角度看分不分片没有什么区别。

架构

  • Shard: 用于存储实际的数据块,实际生产环境中一个shard server角色可由几台机器组个一个replica set承担,防止主机单点故障
  • Config Server: mongod实例,存储了整个 ClusterMetadata。
  • Query Routers : 前端路由,客户端由此接入,且让整个集群看上去像单一数据库,前端应用可以透明使用,前端路由不存储任何数据
  • Shard Key: 片键,设置分片时需要在集合中选一个键, 用该键的值作为拆分数据的依据, 这个片键称之为(shard key),片键的选取很重要, 片键的选取决定了数据散列是否均匀。

搭建

集群规划

  • Shard Server 1:27017
  • Shard Repl  1:27018
  • Shard Server 2:27019
  • Shard Repl  2:27020
  • Shard Server 3:27021
  • Shard Repl  3:27022
  • Config Server :27023
  • Config Server :27024
  • Config Server :27025
  • Route Process :27026

进入安装的 bin 目录创建数据目录

  • mkdir -p ../cluster/shard/s0
  • mkdir -p ../cluster/shard/s0-repl
  • mkdir -p ../cluster/shard/s1
  • mkdir -p ../cluster/shard/s1-repl
  • mkdir -p ../cluster/shard/s2
  • mkdir -p ../cluster/shard/s2-repl
  • mkdir -p ../cluster/shard/config1
  • mkdir -p ../cluster/shard/config2
  • mkdir -p ../cluster/shard/config3

启动3个 shard服务

  • 启动 s0、r0
    shardsvr: shard代表分片(分片就是只存储集合的某一部分数据),svr代表server简写

./mongod --port 27017 --dbpath ../cluster/shard/s0 --bind_ip 0.0.0.0 --shardsvr --replSet r0/192.168.175.129:27018
副本 :./mongod --port 27018 --dbpath ../cluster/shard/s0-repl --bind_ip 0.0.0.0 --shardsvr --replSet r0/192.168.175.129:27017
-- 1.登录任意节点 (副本集初始化)
./mongo --port 27017

-- 2. use admin

-- 3. 执行

java 复制代码
config = { _id:"r0", members:[
      {_id:0,host:"192.168.175.129:27017"},
      {_id:1,host:"192.168.175.129:27018"},
    	]
    }
    //没初始化前,配置是可以改的
   rs.initiate(config);//初始化
  • 启动 s1、r1
    shardsvr: shard代表分片(分片就是只存储集合的某一部分数据),svr代表server简写

./mongod --port 27019 --dbpath ../cluster/shard/s1 --bind_ip 0.0.0.0 --shardsvr --replSet r1/192.168.175.129:27020
副本 :./mongod --port 27020 --dbpath ../cluster/shard/s1-repl --bind_ip 0.0.0.0 --shardsvr --replSet r1/192.168.175.129:27019

-- 1.登录任意节点
./mongo --port 27019

-- 2. use admin

-- 3. 执行

java 复制代码
	config = { _id:"r1", members:[
      {_id:0,host:"192.168.175.129:27019"},
      {_id:1,host:"192.168.175.129:27020"},
    	]
    }
rs.initiate(config);//初始化
  • 启动 s2、r2
    shardsvr: shard代表分片(分片就是只存储集合的某一部分数据),svr代表server简写

./mongod --port 27021 --dbpath ../cluster/shard/s2 --bind_ip 0.0.0.0 --shardsvr --replSet r2/192.168.175.129:27022
副本 :./mongod --port 27022 --dbpath ../cluster/shard/s2-repl --bind_ip 0.0.0.0 --shardsvr --replSet r2/192.168.175.129:27021

-- 1.登录任意节点
./mongo --port 27021

-- 2. use admin

-- 3. 执行

java 复制代码
	config = { _id:"r2", members:[
      {_id:0,host:"192.168.175.129:27021"},
      {_id:1,host:"192.168.175.129:27022"},
    	]
    }
		rs.initiate(config);//初始化

启动3个 config服务

./mongod --port 27023 --dbpath ../cluster/shard/config1 --bind_ip 0.0.0.0 --replSet config/[192.168.175.129:27024,192.168.175.129:27025] --configsvr
./mongod --port 27024 --dbpath ../cluster/shard/config2 --bind_ip 0.0.0.0 --replSet config/[192.168.175.129:27023,192.168.175.129:27025] --configsvr
./mongod --port 27025 --dbpath ../cluster/shard/config3 --bind_ip 0.0.0.0 --replSet config/[192.168.175.129:27023,192.168.175.129:27024] --configsvr

初始化 config server 副本集

1.登录任意节点 congfig server
./mongo --port 27023

2.use admin

3.在admin中执行

java 复制代码
 config = { 
      _id:"config", 
      configsvr: true,
      members:[
          {_id:0,host:"192.168.175.129:27023"},
          {_id:1,host:"192.168.175.129:27024"},
          {_id:2,host:"192.168.175.129:27025"}
        ]
  }
> rs.initiate(config); //初始化副本集配置 

启动 mongos 路由服务

./mongos --port 27026 --configdb config/192.168.175.129:27023,192.168.175.129:27024,192.168.175.129:27025 --bind_ip 0.0.0.0

登录 mongos 服务

1.登录 mongo --port 27026

2.use admin

3.添加分片信息

  • db.runCommand({ addshard:"r0/192.168.175.129:27017,192.168.175.129:27018", "allowLocal":true });
  • db.runCommand({ addshard:"r1/192.168.175.129:27019,192.168.175.129:27020", "allowLocal":true });
  • db.runCommand({ addshard:"r2/192.168.175.129:27021,192.168.175.129:27022", "allowLocal":true });

指定分片的数据库

db.runCommand({ enablesharding:"wkk" });

只有启动库的权限,mongos才会把这个库的集合切分成很多数据块

设置库的片键信息

光指定库不够,还需要指定库中的集合采用哪种分片

以下代表启用_id作为片键

java 复制代码
db.runCommand({ shardcollection: "wkk.users", key: { _id:1}});
db.runCommand({ shardcollection: "wkk.emps", key: { _id: "hashed"}});

片键太有规律了,都分到r2上了

r0没有数据

r1没有数据

使用哈希散列更均匀
db.runCommand({ shardcollection: "wkk.emps", key: { _id: "hashed"}});

r0有emps库

r1有emps库

r2有emps库

给emps集合添加数据

相关推荐
Cosmoshhhyyy4 分钟前
LeetCode:3083. 字符串及其反转中是否存在同一子字符串(哈希 Java)
java·leetcode·哈希算法
AI人H哥会Java18 分钟前
【Spring】基于XML的Spring容器配置——<bean>标签与属性解析
java·开发语言·spring boot·后端·架构
计算机学长felix21 分钟前
基于SpringBoot的“大学生社团活动平台”的设计与实现(源码+数据库+文档+PPT)
数据库·spring boot·后端
sin220121 分钟前
springboot数据校验报错
spring boot·后端·python
开心工作室_kaic28 分钟前
springboot493基于java的美食信息推荐系统的设计与实现(论文+源码)_kaic
java·开发语言·美食
缺少动力的火车30 分钟前
Java前端基础—HTML
java·前端·html
loop lee37 分钟前
Redis - Token & JWT 概念解析及双token实现分布式session存储实战
java·redis
ThetaarSofVenice38 分钟前
能省一点是一点 - 享元模式(Flyweight Pattern)
java·设计模式·享元模式
InSighT__40 分钟前
设计模式与游戏完美开发(2)
java·游戏·设计模式