MongoDB
引言
在早期的程序开发过程中,单体+MySQL
足以应对业务需求,可随着业务不断发展,系统架构演变成分布式,而数据存储的需求亦是多样化,单纯依赖于MySQL
或其他某一款关系型数据库,再也难以支撑系统的存储需求,于是,现如今一个复杂系统中,可能会牵扯到MySQL、Redis、ES、MongoDB、FastDFS、CDH……
多种数据存储产品,根据不同的业务特性、响应要求,会将数据存储在不同组件中以满足系统需要。
MongoDB
是数据库家族中的一员,是一款专为扩展性、高性能和高可用而设计的数据库,它可以从单节点部署扩展到大型、复杂的多数据中心架构,也能提供高性能的数据读写操作;而且提供了数据复制、无感知的故障自动选主等功能,从而实现数据节点高可用。
但MongoDB
并不是一款关系型数据库,而是一款基于“分布式存储”的非关系型数据库(NoSQL
),由C++
编写而成。它与Redis、Memcached
这类传统NoSQL
不同,MongoDB
具有半结构化特性,啥意思呢?下面仔细聊聊。
一、MongoDB快速入门
MongoDB
的数据存储格式非常松散,采用“无模式、半结构化”的思想,通过BSON
格式来存储数据。
BSON
的全称为Binary JSON
,翻译过来是指二进制的JSON
格式,在存储和扫描效率高于原始版JSON
,但是对空间的占用会更高。同时,BSON
在JSON
基础之上,多加了一些类型及元数据描述,具体可参考《MongoDB官网-BSON类型》。
之所以说MongoDB
具有半结构化特性,这是由于BSON
拥有着JSON
的特性,JSON
天生具备结构性,可以便捷的存储复杂的结构数据。但是Mongo
的数据结构又特别灵活,没有关系型数据库那种“强结构化”的规范。
强结构化:所有数据入库前,库中必须先定义好结构,并且入库的数据,每个值要与定义好的字段一一对应,当结构发生变更时,再按原有结构插入数据则会报错,必须同步修改插入的数据,使用起来特别“繁琐”。
MongoDB
中用BSON
来存储数据,而BSON
是一种文档,所以它也被称之为:文档型数据库,文档由一或多个K-V
键值对组成,其中的Key
只能由字符串表示,值则可以是任意类型,如数组、字符串、数值……。简单来说,你可以把MongoDB
中的一个文档,看做成一个JSON
对象。
上面提到“文档”这个概念,初次接触的伙伴或许会疑惑,这里我们与MySQL
数据库对比一下,其实两者的结构很类似,如下:
MySQL | MongoDB |
---|---|
数据库(DataBase ) |
数据库(DataBase ) |
数据表(Table ) |
数据集合(Collection ) |
数据行(Row ) |
数据文档(Document ) |
列/字段(Column ) |
字段(Field ) |
索引(Index ) |
索引(Index ) |
MongoDB
中的一个集合,对应MySQL
中的一张表;一个文档对应着MySQL
一行数据……,层级结构的理念相同,只不过叫法和细节不同。当然,光看概念也很难帮大家认识Mongo
,接下来快速搭建下环境,实操理解的效果更佳。
1.1、MongoDB环境搭建
MongoDB
分为免费社区版、收费企业版,虽说前者功能有所阉割,但好在免费,并且可以满足大多数项目需求,这里咱们先去MongoDB官网-社区版选择对应的操作系统、版本下载安装包,同Redis
一样,版本号的第二位数,为奇数代表开发版,为偶数代表是稳定版。
❶创建MongoDB
目录,并通过工具上传安装包至服务器(或虚拟机):
[root@~]# mkdir /usr/local/mongodb/
[root@~]# cd /usr/local/mongodb/
如果不想这么麻烦,也可以用wget
命令在线拉取安装包:
# CentOS7的版本
[root@~]# wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-6.0.8.tgz
# Ubuntu22.04版本
[root@~]# wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu2204-6.0.8.tgz
没有wget
命令,可以通过yum
或apt
安装一下:
# CentOS7
[root@~]# yum -y install wget
# Ubuntu22.04
[root@~]# apt -y install wget
❷解压相应目录下的安装包,并重命名解压后的目录:
# CentOS7版本:
[root@~]# tar -xvzf mongodb-linux-x86_64-rhel70-6.0.8.tgz
[root@~]# mv mongodb-linux-x86_64-rhel70-6.0.8 mongodb6.0.8
# Ubuntu22.04版本
[root@~]# tar -xvzf mongodb-linux-x86_64-ubuntu2204-6.0.8.tgz
[root@~]# mv mongodb-linux-x86_64-ubuntu2204-6.0.8 mongodb6.0.8
由于我们下载的是压缩包,属于开箱即用版,无需任何额外配置。但为了方便启动,咱们可以将bin
目录拷贝出来:
[root@~]# cp -a /usr/local/mongodb/mongodb6.0.8/bin /usr/local/mongodb/bin
❸创建单机版MongoDB
存储配置文件、数据、日志的目录:
[root@~]# mkdir /usr/local/mongodb/data &&
mkdir /usr/local/mongodb/log &&
mkdir /usr/local/mongodb/conf &&
mkdir /usr/local/mongodb/data/standalone &&
mkdir /usr/local/mongodb/log/standalone &&
mkdir /usr/local/mongodb/conf/standalone
❹编写MongoDB
核心配置文件(没有会自动创建):
[root@~]# vi /usr/local/mongodb/conf/standalone/mongodb.conf
这里大家可以直接复制下述yaml
配置,配置的具体含义可参考注释(也支持传统.conf
形式配置):
systemLog:
destination: file
path: "/usr/local/mongodb/log/standalone/mongodb.log"
logAppend: true
storage:
dbPath: "/usr/local/mongodb/data/standalone"
processManagement:
fork: true
net:
bindIp: 0.0.0.0
port: 27017
- 配置文件具体含义如下:
# MongoDB日志存储相关配置
systemLog:
# 将所有日志写到指定文件中
destination: file
# 记录所有日志信息的文件路径
path: "/usr/local/mongodb/log/standalone/mongodb.log"
# 当服务重启时,将新日志以追加形式写到现有日志尾部
logAppend: true
# 指定MongoDB存储数据的目录
storage:
dbPath: "/usr/local/mongodb/data/standalone"
# 以后台进程方式运行MongoDB服务
processManagement:
fork: true
# ------ MongoDB网络相关配置 ------
net:
# 绑定服务实例的IP,默认是localhost,0.0.0.0表示任意IP(线上换成具体IP)
bindIp: 0.0.0.0
#绑定的端口,默认是27017
port: 27017
复制时记住把数据、日志的目录,改成自己前面新建的,接着按下Esc
,输入:wq
保存退出。
❺启动Mongo
服务,这里和Redis
类似,都是以启动脚本+配置文件的方式:
[root@~]# /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/conf/standalone/mongodb.conf
看到successfully
表示启动成功,大家也可以通过ps
命令来查看后台进程,确保正常启动:
[root@~]# ps aux | grep mongod
❻连接MongoDB
测试是否可以正常使用,不过下述命令只适用于6.x
以下:
[root@~]# /usr/local/mongodb/bin/mongo -port 27017
到了MongoDB6.x
版本,官方不再默认内置Shell
客户端工具,想要使用则需额外下载:官方Shell工具,这里咱们照样以wget
方式下载:
[root@~]# wget https://downloads.mongodb.com/compass/mongosh-1.10.3-linux-x64.tgz
接着将其解压,并拷贝bin
目录下的Shell
工具,放到原本的bin
目录中:
[root@~]# tar -zxvf mongosh-1.10.3-linux-x64.tgz
[root@~]# cp /usr/local/mongodb/mongosh-1.10.3-linux-x64/bin/mongosh /usr/local/mongodb/bin/
然后再通过mongosh
来连接测试:
[root@~]# /usr/local/mongodb/bin/mongosh -port 27017
test> show dbs;
如果上述操作执行完成后,能正常显示三个默认库,说明MongoDB
安装成功。
❼关闭MongoDB
服务,这里有三种方式:
# 1.通过启动项来关闭
[root@~]# /usr/local/mongodb/bin/mongod --shutdown -f /usr/local/mongodb/conf/standalone/mongodb.conf
# 2.先通过客户端连接MongoDB服务,再进入admin库关闭(执行完后客户端会报连接出错)
[root@~]# /usr/local/mongodb/bin/mongosh -port 27017
test> use admin;
test> db.shutdownServer();
# 3.通过kill命令+进程号强杀进程
[root@~]# kill -9 [pid]
一般推荐使用前面两种,因为kill
强杀进程,可能会导致数据损坏,如果损坏则需手动修复:
# 先删除数据目录下的所有锁文件
[root@~]# rm -rf /usr/local/mongodb/data/standalone/*.lock
# 再通过启动项对数据目录进行修复
[root@~]# /usr/local/mongodb/bin/mongod --repair --dbpath=/usr/local/mongodb/data/standalone
❽单纯的命令行不便于操作,所以通常会使用可视化工具来操作,而MongoDB
官方提供了一个可视化工具:MongoDB Compass
,可以先点击>>官网下载地址<<,直接下载解压即用的ZIP
包。
当然,其实官方这款工具并不算好用,如果你有
Navicat Premium
(黄色图标那款),可以直接通过Navicat
来连接MongoDB
操作。
❾为了外部可以连接,需要开放MongoDB
端口、更新防火墙(关闭防火墙也行):
[root@~]# firewall-cmd --zone=public --add-port=27017/tcp --permanent
[root@~]# firewall-cmd --reload
[root@~]# firewall-cmd --zone=public --list-ports
开放端口后,就可以通过可视化工具来连接操作了(如果你通过Navicat
工具连接,是看不到三个默认库的)。
1.2、MongoDB增删改查
搭建好MongoDB
的环境后,接着学习一下最基本的CRUD
操作,首先声明:MongoDB
中的多数操作都带有隐式创建特性,啥意思呢?好比你使用一个数据库,如果没有则会自动创建;使用一个集合时,没有也会自动创建……
这里咱们先创建一个数据库和集合,如下:
// 切换到zhuzi库,没有会自动创建
use zhuzi;
// 向xiong_mao集合插入一条数据(没有集合会自动创建)
db.xiong_mao.insert({_id:1});
和Python
一样,最后的分号可写可不写,不过建议写上,以此增加代码的可读性。
现在继续往xiong_mao
集合中插入两条数据:
db.xiong_mao.insert({_id:2, name:"肥肥", age:3, hobby:"竹子"});
db.xiong_mao.insert({name:"花花", color:"黑白色"});
此时大家会发现,这两条数据同样可以插入成功,前面咱们并没有像使用MySQL
一样,先定义好表结构!从这里就印证了一开始所说的“无模式”特点,接着学学查询:
// 查询xiong_mao集合的所有数据
db.xiong_mao.find();
// ------- 返回结果 ----------
[
{_id:1},
{_id:2,name:'肥肥',age:3,hobby:'竹子'},
{
_id:ObjectId("64d0b843fa3b00006f0044d6"),
name:'花花',
color:'黑白色'
}
]
注意观察第三条数据,在插入时我们并未指定_id
值,可为啥还是有这个字段呢?因为_id
是mongoDB
的默认字段,每个文档(每行数据)必须要有该字段,如果插入时未指定该字段,会自动生成一个类似于雪花ID
的ObjectId
值,mongoDB
会使用_id
来维护数据的存储结构(类似于InnoDB的隐藏列-row_id
,后面再细聊)。
如何根据指定条件查询数据呢?如下:
// 查询_id=2的数据
db.xiong_mao.find({_id:2});
// 查询name=花花的数据
db.xiong_mao.find({name:"花花"});
大家会发现,前面我们以Json
形式将数据插入到集合后,在查询时,咱们可以直接通过Json
中的字段名来操作,这也就印证了最开始所说的“半结构化”特征,因为像Redis、Memcached
这类NoSQL
,虽然也能把数据Json
序列化后存进去,但想要操作时,只能先读出来再反序列化成对象,不能直接对序列化后的Json
字符串进行操作!
最后再来学习一下修改、删除操作:
// 将_id=2的数据,爱好修改为:吃竹子、睡觉
db.xiong_mao.update({_id:2},{hobby:"吃竹子、睡觉"});
// 上面那种方式,会导致其他字段变null,要记得加{$set:},表示局部修改
db.xiong_mao.update({_id:2},{$set:{hobby:"吃竹子、睡觉"}});
// 删除_id=1的数据
db.xiong_mao.remove({_id:1});
OK,我们过了一下最简单的CRUD
操作,这里主要是让大家先熟悉下语法,诸位也可以自行多练习一下。
相较于SQL
的语法来说,MongoDB
的语法显得有点反人类;反观同为NoSQL
的Redis
,它的命令则显得简洁清爽许多。当然,熟悉JS
的小伙伴,接触这个语法不算太难,毕竟这就是JS
的语法,如果只是纯后端的开发者,想适应过来需要一定的练习。
1.3、库与集合的命令
熟悉了基本的CRUD
语法后,进阶一点的语法咱们放到后面慢慢学,这里先熟悉下常用的库、集合的命令:
- 查询所有数据库:
show dbs;
或show databases;
- 切换/创建数据库:
use 库名;
- 查看目前所在的数据库:
db;
- 查看
MongoDB
目前的连接信息:db.currentOp();
- 查看当前数据库的统计信息:
db.stats();
- 删除数据库:
db.dropDatabase("库名");
- 查看库中的所有集合:
show collections;
或show tables;
- 显式创建集合:
db.createCollection("集合名");
- 查看集合统计信息:
db.集合名.stats();
- 查看集合的数据大小:
db.集合名.totalSize();
- 删除指定集合:
db.集合名.drop();
上述这些管理数据库、集合的命令只需简单了解,毕竟大多情况下,都可以通过可视化工具来完成这些操作,所以下面开始学习MongoDB
中的文档操作命令。