MongoDB快速入门到精通,看这篇就够了

一,MongoDB是什么

MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展,高性能和高可用性的数据存储解决方案。

它是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,也支持对数据建立索引。

一句话概况来说:MongoDB是NoSQL数据库中的文档数据库,使用BSON做为文档数据存储和网络传输格式。

官方文档:https://www.mongodb.com/docs/manual/

1,MongoDb核心概念

document

mongo中的记录是一个文档document,文档就是键值对的一个有序集合,类似于java中的json对象,对应关系数据库中的一行。其结构如下:
在这里插入图片描述

​主键:mongo自动将“_id”字段设置为主键,并自动索引,它的值在集合中必须是唯一的,是不可变的。默认情况下," _id “字段的类型为ObjectId,是MongoDB的BSON类型之一。如果需要,用户还可以将” _id “覆盖为ObjectID以外的其他值。通常,您不必担心生成ObjectID。如果未为文档分配” _id "值,MongoDB将自动生成一个。如:
{ " _id" : ObjectId("63f9d0fedf510f98a2c4905e"), "name" : "ChenYuRong", "age" : 15, "addr" : "sz" }

数据类型:BSON格式中,key必须是String类型,value可以是null,布尔,数字,数组,或者嵌套文档,还支持Date类型。注:数字默认是64位的浮点数,如表示整形值,可以使用NumberInt或者NumberLong表示,如db.user.insert({x:NumberInt(10)})

collection

​多个文档组成集合,类似关系数据库中的表。集合是无模式的,即集合中的文档可以是各式各样,不要求field字段名称和个数必须一致。可以使用“.”按照命名空间将集合划分为子集合。如blog集合的两个子集合blog.user,blog.article,这样划分只是让组织结构更好一些,如db.blogdb.blog.userdb.blog.article三个集合之间没有任何关系。

database

​多个集合组成数据库。一个mongod实例可以承载多个数据库,它们之间是相互独立的,每个数据库都有独立的权限控制。在磁盘上,不同的数据库存放在不同的文件中。

系统数据库:

  • Admin 数据库:一个权限数据库,如果创建用户的时候将该用户添加到admin 数据库中,那么该用户就自动继承了所有数据库的权限。
  • Local 数据库:这个数据库永远不会被复制,可以用来存储本地单台服务器的任意集合。
  • Config 数据库:当MongoDB 使用分片模式时,config数据库在内部使用,用于保存分片的信息。

2,MongoDB和sql方式对比

SQL术语/概念MongoDB术语/概念解释/说明
databasedatabase数据库
tablecollection数据库表/集合
rowdocument数据记录行/文档
columnfield数据字段/域
indexindex索引
table joins表连接,MongoDB不支持
primary keyprimary key主键,MongoDB自动将_id字段设置为主键

在这里插入图片描述

二,MongoDB应用场景

1,适用场景

  • 数据库具体数据格式不明确或者数据格式经常变化

  • 数据量巨大,数据价值低(如评论内容,粉丝信息),数据结构特别(如地理坐标)

  • 高性能读写需求

  • 对事务性要求不高

几个实际应用案例

  • 社交场景,使用 MongoDB 存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能。
  • 游戏场景,使用 MongoDB 存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、高效率存储和访问。
  • 物流场景,使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。
  • 物联网场景,使用 MongoDB 存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析。
  • 视频直播,使用 MongoDB 存储用户信息、点赞互动信息等。

2,确定选型

  • 是新应用,需求会变,数据模型无法确定,想快速迭代开发
  • 数据量是有亿万级或者需要不断扩容
  • 应用不需要事务及复杂 join 支持
  • 需要2000-3000以上的读写每秒
  • 系统需要99.999%高可用
  • 系统需要大量的地理位置查询
  • 系统需要提供最小的latency

三,MongoDB三种集群架构

1,主从复制

虽然 MongoDB 官方建议用副本集替代主从复制,但是本节还是从主从复制入手,让大家了解 MongoDB 的复制机制。

主从复制是 MongoDB 中最简单的数据库同步备份的集群技术,其基本的设置方式是建立一个主节点(Primary)和一个或多个从节点(Secondary),这种方式比单节点的可用性好很多,可用于备份、故障恢复、读扩展等。集群中的主从节点均运行 MongoDB 实例,完成数据的存储、查询与修改操作。

主从复制模式的集群中只能有一个主节点,主节点提供所有的增、删、查、改服务,从节点不提供任何服务,但是可以通过设置让从节点提供查询服务,以减少主节点的压力。

另外,每个从节点要知道主节点的地址,主节点记录在其上的所有操作在oplog日志文件中,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。

在主从复制的集群中,当主节点出现故障时,只能人工介入,指定新的主节点,从节点不会自动升级为主节点。同时,在这段时间内,该集群架构只能处于只读状态。

2,副本集

副本集是自带故障转移功能的主从复制。

此集群拥有一个主节点和多个从节点,这一点与主从复制模式类似,且主从节点所负责的工作也类似,但是副本集与主从复制的区别在于:当集群中主节点发生故障时,副本集可以自动投票,选举出新的主节点,并引导其余的从节点连接新的主节点,而且这个过程对应用是透明的。

副本集中的各节点会通过心跳信息来检测各自的健康状况,当主节点出现故障时,多个从节点会触发一次新的选举操作,选举其中一个从节点作为新的主节点。为了保证选举票数不同,副本集的节点数一般保持为奇数。

3,分片+副本集

在这里插入图片描述

副本集可以解决主节点发生故障导致数据丢失或不可用的问题,但遇到需要存储海量数据的情况时,副本集机制就束手无策了。副本集中的一台机器可能不足以存储海量数据,或者说集群不足以提供可接受的读写吞吐量。这就需要用到 MongoDB 的分片(Sharding)技术,这也是 MongoDB 的另外一种集群部署模式。

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

MongoDB 支持自动分片,可以使数据库架构对应用程序不可见,简化系统管理。对应用程序而言,就如同始终在使用一个单机的 MongoDB 服务器一样。
MongoDB 的分片机制允许创建一个包含许多台机器的集群,将数据子集分散在集群中,每个分片维护着一个数据集合的子集。与副本集相比,使用集群架构可以使应用程序具有更强大的数据处理能力。

构建一个 MongoDB 的分片集群,需要三个重要的组件,分别是分片服务器(Shard Server)、配置服务器(Config Server)和路由服务器(Route Server)。

1,Shard Server 或者叫mongod
一个 mongodb数据库实例,启动一个mongod进程,用于存储实际的数据块。整个数据库集合分成多个块存储在不同的 Shard Server 中。
在实际生产中,一个 Shard Server 通常由几台机器组成一个副本集来承担,防止因主节点单点故障导致整个系统崩溃。

2,Config Server
这是一个特殊的 mongod 进程,保存集群和分片的元数据,在集群启动最开始时建立,保存各个分片包含数据的信息。

3,Route Server 或者叫mongos 就是 “MongoDB Shard” 的简写
这是独立的一个 mongos 进程,Route Server 在集群中可作为路由使用,客户端由此接入,让整个集群看起来像是一个单一的数据库,提供客户端应用程序和分片集群之间的接口。
Route Server 本身不保存数据,启动时从 Config Server 加载集群信息到缓存中,并将客户端的请求路由给每个 Shard Server,在各 Shard Server 返回结果后进行聚合并返回客户端。

4,生产环境部署案例

以上介绍了 MongoDB 的三种集群模式,副本集已经替代了主从复制,通过备份保证集群的可靠性,分片机制为集群提供了可扩展性,以满足海量数据的存储和分析的需求。

在实际生产环境中,副本集和分片是结合起来使用的,可满足实际应用场景中高可用性和高可扩展性的需求。

如下是一个“三分片+三副本”的高可用集群的部署信息:

进程节点1节点2节点启动端口
shard0172.18.17.73172.18.17.74172.18.17.7527017
shard1172.18.17.73172.18.17.74172.18.17.7527018
shard2172.18.17.73172.18.17.74172.18.17.7527019
config server172.18.17.70172.18.17.71172.18.17.7217017
mongos172.18.17.70172.18.17.71172.18.17.7220017

一台节点上27017,27018,27019三个不同的端口表示启动了三个mongod实例,代表三个分片副本。每个分片由部署在三个节点上的副本共同构成副本集。

默认端口
27017:mongod默认端口
17017 :config server进程
20017 :mongos进程端口,即客户端连接用的端口

四,MongoDB认证登录

1,客户端连接

#mongodb认证登录
mongo 172.18.17.70:20017
use admin
db.auth("username","*************此处键入密码***************")

#一键认证登录
mongo 172.18.17.70:20017/admin -u username -p ***********此处键入密码**********

2,认证数据库

创建用户时,是以数据库为单位来建立的,所以需要先切换到指定库。该数据库是用户的认证的数据库,简称验证库。登录时,需要在它的验证库下。删除用户时,也要切换的用户的验证库下。
用户可以跨不同数据库拥有权限; 即用户的权限不限于认证数据库。 通过分配给其他数据库中的用户角色,在一个数据库中创建的用户可以拥有对其他数据库的操作权限。

用户名和验证库作为该用户的唯一标识符。 也就是说,如果两个用户具有相同的名称,但是在不同的数据库中创建,则它们是两个不同的用户。 如果您打算拥有具有多个数据库权限的单个用户,请在适用的数据库中创建具有角色的单个用户,而不是在不同数据库中多次创建用户。

五,MongoDB基本shell命令

1,基本命令

db --查看当前数据库
show dbs/database --展示数据库列表(没有数据的数据库不展示)
use myDb --创建数据库或者切换数据库
db.dropDatabase() --删除当前数据库,需要在use选择数据库后执行
show collections --展示集合
db.user.drop() --删除user集合,user为自己创建的集合
db.user.count() --统计集合的大小

help --查看使用说明
db.help() --当前数据库支持的方法,查看增删改查方法
db.user.help() --查看当前集合支持的方法
db.user.function --查看方法声明

2,新增操作

#新增数据,往user集合新增一条文档,user不存在会自动创建
--对象的键一般可不加引号(方便看),但是查看集合数据时系统会自动加;mongodb会给每条数据增加一个全球唯一的_id键
db.user.insert({     
...     name: "wang",
...     age: 25,
...     addr: "sz"
... })

#批量插入数据,方法传入一个json数组即可
db.user.insert([
... {
...     "name": "shengr",
...     "age": 15,
...     "addr": "sz"
... },
... {
...     "name": "xiaoming",
...     "age": 65,
...     "addr": "sz"
... },
... {
...     "name": "wang",
...     "age": 27,
...     "addr": "gd"
... }])

3,删除操作

#语法
db.user.remove( {条件} [,是否删除一条])
--是否删除一条,默认值false,匹配到的数据都删除;true表示只删除第一条数据

#删除所有叫做wang的
db.user.remove({"name":"wang"})
#清空所有数据
db.user.remove({})

4,修改操作

# 语法
db.user.update( {条件},{修改器:{k:v}} [upsert没有匹配的行是否新增,multi匹配多个是否都修改])
--修改器:inc增加列值  set修改列值 unset删除列 rename重命名列
--没有匹配是否新增,默认false
--匹配多个是否都修改,默认false

# 修改wang的名字
db.user.update({name:'wang'},{$set:{name:'laowang'}})

# wang的年龄加5岁,减5岁,值改为-5即可
db.user.update({name:'wang'},{$inc:{age:5}})

# score列追加一个值,该列不存在会新增
db.user.update({name:'laowang'},{$push:{score:16}})

# 移除core列的某些值
db.user.update({name:'laowang'},{$pullAll:{score:[10,20,30]}})

# 循环插入,并自动过滤重复数据,如果没有$each,相当于插入一个数组类型子元素
db.user.update({name:'laowang'},{$addToSet:{score:{$each:[10,2,3,4,5,6]}}})

# 增加日期列currentDate,可用$type指定日期类型为date或timestamp
db.user.update({name:'laowang'},{$currentDate:{currentDate:true}})
db.user.update({name:'wang'},{$currentDate:{currentDate:{$type:"timestamp"}}})

# setOnInsert示例,生效的前提是过滤器没有匹配到行
db.user.update({name:'shengr'},{$setOnInsert:{weight:80}},true)
db.user.update({name:'shengrr'},{$set:{age:888},$setOnInsert:{weight:80}},true)

常用mongo修改器

--普通类型修改器:
$inc --对数字类型的值进行增减
$set --修改列值,并且不存在的列默认添加
$setOnInsert --如果更新导致插入文档(新增文档),则设置字段的值,对修改现有文档的更新操作无效。upsert选项必须设置为true,可同时与set搭配使用。
$unset  --删除列, {$unset:{'name':0/1/-1}}  值是0或者1或者-1 都会被删除。
$currentDate --设置值为当前日期

--数组修改器:
$push --如果数组列不存在则添加,如果存在则给数组的末尾追加一个值。
$pop  --值为1,末尾移除一位。值为-1,开头移除一位。移除了了最后一个元素为空数组。
$pull  --移除数组中指定的数值
$pullAll  --移除数组中指定的某几个值。
$addToSet  --如果数组中已经存在要添加的值则无法添加成功。

5,查询操作

#查询语法
db.collection.find(query, [projection]) 
--query是个json,指定查询条件, projection投影操作也是json,key为需要显示的列名,value 为 1(显示) 或 0(不显示)。
#query查询条件写法
db.user.find({
            键:{运算符:值}
            })

#示例
db.user.find() --不加条件,则查找全部
db.user.find().limit(1)  --limit操作,限制查询条数
db.user.find( {age:15} ) --查询年龄=15的
db.user.find( {age:15,addr:"sz"} ) --查询年龄=15,并且是sz的
db.user.find( {age:{$gt:15}} ) --查询年龄>15的


--特殊查询:查找一个并修改
--该方法会修改匹配到的第一个文档,如果没有匹配到文档,默认不会新增,除非设置upsert选项为true,则没有匹配到会新增一个文档。
--重点是:该方法默认返回的是修改前的文档,要想返回新文档,可设置returnNewDocument为true。

db.user.findOneAndUpdate({name:"han"},{$inc:{weight:1}})
db.user.findOneAndUpdate({name:"han"},{$inc:{weight:1}},{returnNewDocument:true})
db.user.findOneAndUpdate({name:"hanhan"},{$inc:{weight:1}},{returnNewDocument:true,upsert:true})

运算符作用
$gt大于
$gte大于等于
$lt小于
$lte小于等于
$ne不等于
$inin
$ninnot in

6,其他操作

#and操作
即用逗号分隔的k:v对
#or操作
db.user.find(
   {
      $or: [
	     {key1: value1}, {key2:value2}
      ]
   }
).pretty()

#排序操作
db.user.find().sort({KEY:1})  --KEY指定字段,1 为升序排列,而-1是用于降序排列

六,MongoDB索引介绍

默认情况下,集合中的_id字段就是索引,我们可以通过getIndexes()方法来查看一个集合中的索引:db.user.getIndexes() 。但是基于查询条件中的普通列做查询时,如db.user.flind({age:13}),查找年龄为13的所有用户,默认是全表扫描,很低效,此时可以给该字段创建索引。

单字段索引

db.user.createIndex({age:1},{name:"myfirstIndex"})

1表示升序,-1表示降序。当我们给age字段建立索引之后,再根据age字段去查询,速度就非常快了。
注:老版本可能存在ensureIndex()方法,使用方式和上面是一样的。

复合索引

基于两个字段以上的索引。复合索引中列出的字段的顺序很重要。索引将包含对文档的引用,如下,这些文档首先按a字段的值排序,然后在该字段的每个值内,再按b字段的值排序。复合索引除了支持在所有索引字段上都匹配的查询之外,还可以支持在索引字段的前缀上匹配的查询,如匹配a的查询。

db.products.createIndex( { "a": 1, "b": 1 } )

此外,索引创建还有很多其他可选参数,如下

db.user.createIndex({age:1},{name:"myfirstIndex",dropDups:true,background:true,unique:true,sparse:true,v:1,weights:99999})

#参数说明
1. name表示索引的名称
2.dropDups表示创建唯一性索引时如果出现重复,则将重复的删除,只保留第一个
3.background是否在后台创建索引,在后台创建索引不影响数据库当前的操作,默认为false
4.unique是否创建唯一索引,默认false
5.sparse对文档中不存在的字段是否不起用索引,默认false
6.v表示索引的版本号,默认为2
7.weights表示索引的权重

索引其他命令

--查看索引大小
db.user.totalIndexSize()
--按名称删除索引
db.user.dropIndex("myfirstIndex")
--删除所有索引(不包括'_id')
db.user.dropIndexes()

索引是个好东西,可以有效的提高查询速度,但是索引过多会降低插入、更新和删除的速度,因为这些操作不仅要更新文档,还要更新索引,MongoDB 限制每个集合上最多有64个索引,我们在创建索引时要仔细斟酌索引的字段。另外,在索引的构建期间,应用程序可能会遇到性能下降,包括对集群的读写访问受限。

七,MongoDB的Java 客户端操作

引入pom依赖

<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongo-java-driver</artifactId>
    <version>3.11.2</version>
</dependency>

CRUD操作

package mongoClient;
import com.mongodb.*;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.FindOneAndUpdateOptions;
import com.mongodb.client.model.ReturnDocument;
import org.bson.Document;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;


/**
 * Created by shengR on 2023/3/2
 * @desc: todo
 */
public class ClientTest {
    public static void main(String[] args) {
        //创建服务地址
        ServerAddress serverAddress = new ServerAddress("$ip", 20017);
        //创建凭证,三个参数:用户名,验证库,密码
        MongoCredential mc = MongoCredential.createScramSha1Credential("$username","$db","$password".toCharArray());
        //创建客户端连接选项
        MongoClientOptions options = MongoClientOptions.builder()
                //设置连接超时时间为10s
                .connectTimeout(1000*10)
                //设置最长等待时间为10s
                .maxWaitTime(1000*10)
                .build();
        //创建客户端,其中可选参数options,用于设置连接所需的配置信息
        MongoClient client = new MongoClient(serverAddress,mc,options);

        //获取数据库,没有则创建
        MongoDatabase db = client.getDatabase("test");
        //获取集合,没有则创建
        MongoCollection<Document> teacher = db.getCollection("teacher");

        //一,查询数据
        //全量查询
        FindIterable<Document> result1 = teacher.find();
        //条件查询1 :过滤name为shengr的文档
        FindIterable<Document> result2 = teacher.find(Filters.eq("name", "shengr"));
        //条件查询2 :过滤课程中同时包含Math和China的文档
        BasicDBObject query = new BasicDBObject("courses", new BasicDBObject("$elemMatch", new BasicDBObject("$eq", "Math").append("$eq","China")));
        FindIterable<Document> result3 = teacher.find(query);
        //遍历结果
        MongoCursor<Document> iterator = result2.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

        //二,插入数据(基本数据以及数组示范)
        Document d1=new Document();
        List<String> coursesList=new ArrayList<>();
        coursesList.add("Math");
        coursesList.add("English");
        coursesList.add("China");
        d1.append("name","shengr").append("age","26").append("addr","sz").append("courses",coursesList);
        teacher.insertOne(d1);

        //三,更新数据,updateOne为修改查到的第一条,updateMany修改查到的全部数据
        teacher.updateOne(Filters.eq("name", "shengr"), new Document("$set", new Document("name", "asseghuh")));
        teacher.updateMany(Filters.eq("name", "shengr"), new Document("$set", new Document("name", "asseghuh")));

        //findOneAndUpdate用法,该方法默认返回修改前的数据,且没有匹配的不会新增,可以设置如下选项更改默认行为
        FindOneAndUpdateOptions findOneAndUpdateOptions = new FindOneAndUpdateOptions()
                .returnDocument(ReturnDocument.AFTER)
                .upsert(true);

        Document d=new Document()
                .append("$set", new Document("addr", "shanghai"))
                .append("$set",new Document("age","99").append("updateTime",new Date()).append("flag","1"));

        Document result = teacher.findOneAndUpdate(Filters.eq("name", "ggg"), d, findOneAndUpdateOptions);

        //四,删除操作,one和many的区别同上
        teacher.deleteOne(Filters.eq("name", "aaaaa"));
        teacher.deleteMany(Filters.eq("name", "bbbbb"));

    }
}

find()常用的两个重载方法说明

//无参,表示全量查询
FindIterable<TDocument> find();
//传入一个Bson类型的查询条件
FindIterable<TDocument> find(Bson query);

BasicDBObject对象说明

一个Bson对象的基本实现,可用于构造mongo查询条件,更新mongo等。其继承关系如下:
public class BasicDBObject extends BasicBSONObject implements DBObject, Bson

1,构造方法

public BasicDBObject()
public BasicDBObject(final String key, final Object value) 
public BasicDBObject(final Map map)

2,使用案例

//基本用法
BasicDBObject query = new BasicDBObject();
query.put( key, value );

//设置单个字段条件
BasicDBObject query = new BasicDBObject("name", "shengr");

//in 的用法
BasicDBObject query = new BasicDBObject("age", new BasicDBObject("$in", ageList));

//数组字段元素匹配
BasicDBObject query = new BasicDBObject();
query.put("courses", new BasicDBObject("$elemMatch", new BasicDBObject("$eq", "Math").append("$eq", "China")));

//exists 用法,  1代表存在该字段,值为null也示为存在
BasicDBObject query = new BasicDBObject("courses", new BasicDBObject("$exists", 1));

八,小结

继续深入学习看这里:
MongoDb官方文档:点这里跳转
MongoDb中文社区:点这里跳转

在这里插入图片描述

  • 13
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值