MongoDB基础入门

发布于:2025-02-11 ⋅ 阅读:(11) ⋅ 点赞:(0)

什么是Mongodb

MongoDB是NoSQL数据库中的佼佼者,目前是排名第一的文档型数据库。该数据库基于灵活的JSON文档模型,非常适合敏捷式的快速开发。与此同时,其与生俱来的高可用、高水平扩展能力使得它在处理海量、高并发的数据应用时颇具优势。

面向文档设计

在我们的系统中,通常会用分层来描述现实中的模型,如图所示:
在这里插入图片描述

从下往上看,每一层都提供了更简单、更容易表述的模型来隐藏下层的复杂性。最为典型的是,数据库系统屏蔽了所有磁盘中文件如何存取、压缩/解压等细节,向应用程序展示了一些通用的数据模型,如SQL表、列、或是基于JSON、XML的文档模型。而应用程序方面,面向对象的模型也已经被绝大多数人所熟知并接受。
在处理SQL数据模型时,应用程序需要通过代码做一些必要的转换工作,一般可以借助一些ORM框架来减少工作量,例如Hibernate。然而,SQL模型与面向对象之间仍然存在不少差异,这些差异并不能完全通过框架屏蔽。相较之下,基于JSON的文档模型则更能契合面向对象的设计准则,对于开发者来说,这在一定程度上降低了使用数据的门槛。
MongoDB是基于JSON来描述数据的,所有的“数据行”都可以通过一个JSON格式的文档来表示。基于JSON格式的数据模型可读性非常强,也更加灵活;除了基本的数据类型,文档中还可以使用数组、内嵌子对象等高级的字段类型。
此外,JSON还具备无模式的特点,可以轻松地进行扩展。在访问MongoDB地“表”之前,并不需要事先对表模型进行声明(尽管你可以这么做)。同时,当数据模型发生变更时,MongoDB不会强制要求你去执行表结构更新地相关操作,这提供了很大地便利性。
在MongoDB内部,BSON(一种二进制版本的JSON扩展)被真正用来存储这些JSON形式的文档数据。在JSON的基础上,进行了一些易用性方面的扩展,例如增加日期、二进制等类型的支持。
虽然MongoDB沿袭了JSON的特点,但是将它归为无模式数据库是不合适的。实际上,所有的读写都是基于一种内部隐含的模式,模式采取按需变更而非提前声明,因此动态模式一词更适合它。

特性

  1. 完备的索引
    与大多数数据库一样,mongodb支持各种丰富的索引类型,包括单键索引、复合索引,唯一索引等一些常用的结构。由于采用了灵活可变的文档类型,因此它也同样支持对嵌套字段、数组进行索引。通过建立合适的索引,我们可以极大提升数据的检索速度。值得一提的是,mongodb的索引实现与一般的关系型数据索引并没有太大区别,因此,我们几乎可以使用某种“一致的思路”来设计索引或完成一些性能调优的任务。
  2. 跨平台,支持各种编程语言
    MongoDB是用C++语言编写的,其官方网站提供了各种平台的编译版本,在客户端方面,mongodb提供了多种编程语言实现的驱动程序,除了Java、C/C++/C#等传统语言,像Python、NodeJS等动态语言也有对应的实现。
  3. 强大的聚合计算
    聚合计算是MongoDB面向数据分析领域的重要特性,可以用于实现数据的分类统计或一些管道计算。mongodb为聚合框架提供了大量常用的函数以简化开发,除此之外,聚合框架还用到了一种叫“管道”(pipeline)的概念,用于抽象各个数据处理的阶段,一个管道由多个“阶段”(stage)组成,通过对不同的阶段进行自由组合,我们就可以灵活应对各种场景中的计算需求。
  4. 复制、分布式
    mongodb通过副本集来实现数据库的高可用,这点类似于mysql的master/slave复制架构,不同的是,一个副本集可以由一个主节点和多个从节点组成,主节点和从节点基于oplog来实现数据同步。在主节点发生故障时,从节点将重新选举出新的主节点以继续提供服务,整个切换过程是自动完成的。
    在海量数据处理方面,mongodb原生就支持分布式计算能力。在一个分布式集群中,多个文档被划入一个逻辑数据块(chunk),这些数据块可以被存储于不同的计算节点(分片)上,在新的计算节点加入时,数据块可以借助自动均衡的算法机制被迁移到合适的位置(通常是压力较小的分片)。通过这种自动化的调度以及均衡工作,整个集群的数据库读写压力可以被分摊到多个节点上,从而实现负载均衡和水平扩展。

数据结构

SQL概念 MongoDB概念
数据库(database) 数据库(database)
表(table) 集合(collection)
行(row) 文档(document)
列(column) 字段(field)
索引(index) 索引(index)
主键(primary key) _id(字段)
视图(view) 视图(view)
表连接(table join) 聚合操作($lookup)

尽管这些概念大多与sql标准定义类似,但是mongodb与传统的RDBMS仍然存在差异,包括:

  • 半结构化,在一个集合中,文档所拥有的字段并不需要是相同的,而且也不需要对所用的字段进行声明。
  • 弱关系,mongodb没有外键的约束,也没有非常强大的表连接能力。类似的功能需要使用聚合管道技术来弥补

Mongodb下载

各位可以参考这篇博客,笔者亲测使用第1种手动安装的方式有效。
Mac安装MongoDb保姆级教程以及踩坑笔记(图文详解)
因为,笔者的电脑是MacBook的m芯片系列,下载最新的mongodb的安装包时,发现在bin目录下缺少mongo脚本,这时各位可以下载旧版本(5.x版本)的mongo安装包,直接将mongo脚本脚本复制到对应的目录下即可。
在这里插入图片描述

连接Mongodb服务

一般,我们在代码程序中是通过连接字符串的方式来连接数据库服务的,类似如下格式:

mongodb://abc:123456@localhost:27017,localhost:27018,localhost:27019/hzu_lwy?replicaSet=replica&authSource=admin

首先,我们先逐部分解释这个连接字符串的含义:

  1. 协议部分
    • mongodb://:这是协议头,表示使用MongoDB协议进行连接。
  2. 用户认证信息
    • abc:123456@:这部分包含了用于身份验证的用户名和密码。
      • abc:用户名。
      • 123456:密码。
        @:分隔符,表示用户名和密码的结束。
  3. 主机和端口
    • localhost:27017,localhost:27018,localhost:27019:这是MongoDB集群中3个节点的主机地址和端口。
      • localhost:27017:第一个节点的IP地址和端口。
      • localhost:27018:第二个节点的IP地址和端口。
      • localhost:27019:第三个节点的IP地址和端口。
        通过指���多个节点,客户端可以在连接时选择可用的节点,从而实现高可用性。
  4. 数据库名称
    • /hzu_lwy:斜杠后面指定了要连接的数据库名称。
      • hzu_lwy:这是默认连接的数据库名称。在进行身份验证时,如果没有指定authSource,则使用这个数据库。
  5. 连接选项
    • ?replicaSet=replica&authSource=admin:这是连接字符串的查询参数部分,用于指定连接选项。
      • replicaSet=replica:指定要连接的副本集名称为replica。这告诉客户端MongoDB集群是一个副本集,并且应该使用副本集的功能(如自动故障转移)。
      • authSource=admin:指定用于身份验证的数据库为admin。这意味着用户凭据存储在admin数据库中,而不是在默认连接的数据库中。

总结:这条连接字符串用于连接到一个MongoDB副本集,使用指定的用户名和密码进行身份验证,连接到hzu_lwy数据库,并且身份验证信息存储在admin数据库中。通过指定多个节点的IP和端口,客户端可以在连接时选择可用的节点,从而实现高可用性和故障转移。

启动Mongodb服务

我们通过配置文件的方式来启动mongodb服务。如果读者是通过brew的方式来安转mongodb的,那么配置文件会自动下载,具体的下载目录,各位就google一下;如果是手动安装mongodb的,那么我们就需要自己设置配置文件。
接下来,我们新建一个conf目录用来存放配置文件,配置文件部分信息如下:
在这里插入图片描述

# MongoDB 配置文件示例

# 系统日志配置
systemLog:
  destination: file  # 日志输出到文件
  path: /usr/local/mongodb/log/mongod.log  # 日志文件路径
  logAppend: true  # 以追加方式写入日志文件

# 存储配置
storage:
  dbPath: /usr/local/mongodb/data/db  # 数据目录路径

# 网络配置
net:
  bindIp: 0.0.0.0  # 允许所有ip连接
  port: 27017  # 端口号

# 安全配置
security:
  authorization: enabled  # 启用认证
  # 如果是副本集,还需要指定keyFile
  keyFile: /usr/local/mongodb/ssl/key

# 复制配置(在配置复制集时使用)
replication:
  replSetName: replica  # 复制集名称

当在MongoDB副本集中启用授权时,必须使用keyFile来进行成员之间的身份验证。这是因为副本集中的各个成员需要相互认证,以确保安全通信。
当在MongoDB副本集中启用授权时,必须使用keyFile来进行成员之间的身份验证。这是因为副本集中的各个成员需要相互认证,以确保安全通信。
解决方法:
创建一个Key File。首先,你需要创建一个密钥文件。这个文件应该包含一个随机生成的字符串,长度在6到1024个字符之间。可以使用以下命令生成一个随机密钥:

openssl rand -base64 756 > /path/to/keyfile

确保密钥文件的权限是严格的,只允许MongoDB用户读取:chmod 400 /path/to/keyfile。必须设置不然,mongodb是启动不起来的,笔者就被坑了,最后还是通过系统日志(配置文件中配置mongod.log文件)发现原因所在。

接下来按照如下步骤:

  • 启动多个MongoDB实例
    mongod --config /path/to/mongod.conf --port 27017 --dbpath /usr/local/mongodb/data/db1 #命令行中指定了dbpath参数,会覆盖配置文件里面默认设置的
    mongod --config /path/to/mongod.conf --port 27018 --dbpath /usr/local/mongodb/data/db2
    mongod --config /path/to/mongod.conf --port 27019 --dbpath /usr/local/mongodb/data/db3
    
  • 初始化副本集
    选择一个实例(例如,端口27017)作为初始配置的主节点,使用MongoDB Shell(基于js语法,可以在shell)连接到该实例:
    ./mongo --host localhost:27017
    
    在MongoDB Shell中,使用rs.initiate()命令初始化副本集:
    rs.initiate({
      _id: "replica",
      members: [
        { _id: 0, host: "localhost:27017" },
        { _id: 1, host: "localhost:27018" },
        { _id: 2, host: "localhost:27019" }
      ]
    })
    
    在这里插入图片描述
  1. 在mongodb shell中设置用户名和密码

    use admin
    db.createUser({"user":"abc","pwd":"123456","roles":[{role:"root",db:"admin"},{role:"clusterAdmin",db:"admin"}]})
    db.auth('abc','123456')
    

    注意:上面着条命令一定要在admin数据库下才能执行,并且命令中db属性必须时admin。

  2. 验证mogndb启动了副本集

    rs.status()
    

    在mongodb shell中执行上述命令,我们会看一共新建了3个mongodb节点,其中localhost:27017为主节点,localhost:27018和localhost:27019为从节点。并且,我们只能在主节点中执行数据库的增删改查命令,如果在从节点上执行则是会报错,并且当我们停掉任意一个节点时,剩下的2个节点会随机选举一个成为主节点。

BSON协议与类型

大多数情况下,使用JSON作为数据交互格式是理想的选择,但是JSON基于文本的解析效率并不是最好的,在某些场景下往往会考虑选择更合适的编/解码格式,如:

  • 在微服务架构中,使用gRPC(基于Google的Protobuf)可以获得更好的网络利用率。
  • 分布式中间件、数据库,使用私有定制的TCP数据包格式来提供高性能、低延时的计算能力。

BSON是二进制版本的JSON,其在性能方面有更优的表现。BSON在许多方面和JSON保持一致,其同样也支持内嵌的文档对象和数组结构。二者最大的区别在于JSON是基于文本的,而BSON则是二进制编/解码的形式。除此之外,BSON还提供了一些扩展的数据类型,比如日期,二进制数据等。mongodb在文档存储、命令协议上都采用了BSON作为编/解码格式。

注意:mongodb在使用日期类型时,通常需要注意时区的问题。mongodb的日期类型使用UTC进行存储,也就是+0时区的时间。一般客户端会根据本地时区自动转换为UTC时间。
我们在mongdb shell中进行如下测试:

> var date1=Date()
> var date2=new Date()
> var date3 = ISODate()
> var dateObject={d1:date1,d2:date2,d3:date3}
> db.dates.insert(dateObject)

> db.dates.find().pretty()
{
        "_id" : ObjectId("67a8dde169a818fdad7af3b9"),
        "d1" : "Mon Feb 10 2025 00:52:15 GMT+0800",
        "d2" : ISODate("2025-02-09T16:52:23.442Z"),
        "d3" : ISODate("2025-02-09T16:52:33.071Z")
}

可以看到,使用new Date和ISODate的语义是相同的,两者最终都会生成ISODate类型的字段(对应于UTC时间)。而Date与二者都不同,它会以字符串形式返回当前的系统时间。由于当前正处于+8时区,因此输出的时间值比ISODate多8个小时。

固定集合

固定集合(capped collection)是一种限定大小的集合,其中capped是覆盖、限额的意思。跟普通集合相比,数据在写入这种集合时遵循FIFO原则。可以将这种集合想象为一个环状的队列,新文档在写入时被插入队列的末尾,如果队列已满,那么之前的文档就会被新写入的文档覆盖。

使用示例

> db.createCollection("logs",{capped:true,size:4096,max:10})
{ "ok" : 1 }
> for(var i=0;i<15;i++){db.logs.insert({t:"row-"+i})}
WriteResult({ "nInserted" : 1 })
> db.logs.find()
{ "_id" : ObjectId("67a8e14e69a818fdad7af3bf"), "t" : "row-5" }
{ "_id" : ObjectId("67a8e14e69a818fdad7af3c0"), "t" : "row-6" }
{ "_id" : ObjectId("67a8e14e69a818fdad7af3c1"), "t" : "row-7" }
{ "_id" : ObjectId("67a8e14e69a818fdad7af3c2"), "t" : "row-8" }
{ "_id" : ObjectId("67a8e14e69a818fdad7af3c3"), "t" : "row-9" }
{ "_id" : ObjectId("67a8e14e69a818fdad7af3c4"), "t" : "row-10" }
{ "_id" : ObjectId("67a8e14e69a818fdad7af3c5"), "t" : "row-11" }
{ "_id" : ObjectId("67a8e14e69a818fdad7af3c6"), "t" : "row-12" }
{ "_id" : ObjectId("67a8e14e69a818fdad7af3c7"), "t" : "row-13" }
{ "_id" : ObjectId("67a8e14e69a818fdad7af3c8"), "t" : "row-14" }
> db.logs.stats()
{
        "ns" : "damin.logs",
        "size" : 355,
        "count" : 10,
        "avgObjSize" : 35,
        "storageSize" : 16384,
        "capped" : true,
        "max" : 10,
        "maxSize" : 4096,//必须是2的n次方。如果设定值不符合条件,则会被自动对齐。例如固定集合时指定size为500,那么最终的maxSize就是512
        "sleepCount" : 0,
        "sleepMS" : 0,
        "wiredTiger" : {
  1. max:指集合的文档数量最大值,这里是10条。
  2. size:指集合的空间占用最大值,这里是4096字节。
    这两个参数会同时对集合的上限产生影响。也就是说,只要任一条件达到阈值都会认为集合已经写满。其中size是必选,max是可选。

特征与限制

固定集合在底层使用的是顺序I/O操作,而普通集合使用的是随机I/O。总所周知,顺序I/O在磁盘操作上由于寻道次数少而比随机I/O要高效得多,因此固定集合的写入性能是很高的。此外,如果按写入顺序进行数据读取,也会获得非常好的性能表现。
但它也存在一些限制:

  • 无法动态修改存储的上限,如果需要修改max或size,则只能先执行collection.drop命令,将集合删除再重建。
  • 无法删除已有的数据。
  • 对已有的数据进行修改,新文档大小必须与原来的文档大小一致。
  • 默认情况下,固定集合只有一个_id索引,而且最好是按数据写入的顺序进行读取。
  • 固定集合不支持分片,同时,在mongodb4.2版本中规定了事务中无法对固定集合执行写操作。

适用场景

固定集合很适合用来存储一些“临时态”的数据。该数据在一定程度上可以被丢弃。同时,用户还应该关注最新的数据,随着时间的推移,数据的重要性逐渐减低,直至被淘汰处理。

  • 系统日志。
  • 存储少量文档,如最新发布的Top N条文章信息。得益于内部缓存的作用,对于这种少量文档的查询非常高效。