mongodb学习篇

前言

本篇来简单的学习mongodb。后期深入学习还需要多看官网文档https://www.mongodb.com/docs/manual/
mongodb是一个nosql类型的数据库,其存储数据简单看就是json类型的数据格式,mongodb中也有数据库的概念,数据库中有集合,集合中有文档,一个文档其实就是一个json类型的数据,这里的集合和文档其实可以类比于mysql中表和数据行的概念。

基本概念

数据库-database

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

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

集合-collection

集合就是一组文档,类似于关系数据库中的表。集合是无模式的,集合中的文档可以是各式各样的。例如,{“hello,word”:“Mike”}{“foo”: 3},它们的键不同,值的类型也不同,但是它们可以存放在同一个集合中,也就是不同模式的文档都可以放在同一个集合中。既然集合中可以存放任何类型的文档,那么为什么还需要使用多个集合?这是因为所有文档都放在同一个集合中,无论对于开发者还是管理员,都很难对集合进行管理,而且这种情形下,对集合的查询等操作效率都不高。所以在实际使用中,往往将文档分类存放在不同的集合中,例如,对于网站的日志记录,可以根据日志的级别进行存储,Info级别日志存放在Info 集合中,Debug 级别日志存放在Debug 集合中,这样既方便了管理,也提供了查询性能。但是需要注意的是,这种对文档进行划分来分别存储并不是MongoDB 的强制要求,用户可以灵活选择。
可以使用“.”按照命名空间将集合划分为子集合。例如,对于一个博客系统,可能包括blog.user 和blog.article 两个子集合,这样划分只是让组织结构更好一些,blog 集合和blog.user、blog.article 没有任何关系。虽然子集合没有任何特殊的地方,但是使用子集合组织数据结构清晰,这也是MongoDB 推荐的方法。

文档-document

文档是MongoDB中数据的基本单位,类似于关系数据库中的行(但是比行复杂)。多个键及其关联的值有序地放在一起就构成了文档。不同的编程语言对文档的表示方法不同,在JavaScript 中文档表示为:
{“greeting”:“hello,world”}
这个文档只有一个键“greeting”,对应的值为“hello,world”。多数情况下,文档比这个更复杂,它包含多个键/值对。例如:
{“greeting”:“hello,world”,“foo”: 3}
文档中的键/值对是有序的,下面的文档与上面的文档是完全不同的两个文档。
{“foo”: 3 ,“greeting”:“hello,world”}
文档中的值不仅可以是双引号中的字符串,也可以是其他的数据类型,例如,整型、布尔型等,也可以是另外一个文档,即文档可以嵌套。文档中的键类型只能是字符串。

部署mongodb

官方文档:https://www.mongodb.com/docs/manual/
mongodb下载中心:https://www.mongodb.com/download-center/community/releases

linux安装mongodb

官方文档:https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-red-hat/#std-label-install-mdb-community-redhat-centos

# 1、联网yum安装mongodb(建议使用yum方式安装mongodb)
#环境初始化
yum install chrony -y && systemctl enable --now chronyd
yum install vim lsof net-tools zip unzip tree wget curl bash-completion gcc make tcpdump bind-utils -y
sed -ri 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config 
setenforce 0
systemctl stop firewalld.service && systemctl disable firewalld.service
hostnamectl set-hostname mongodb
#mongodb建议的优化,退出终端重新登录生效
echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled
echo 'never' > /sys/kernel/mm/transparent_hugepage/defrag
chmod +x /etc/rc.d/rc.local
cat >> /etc/rc.d/rc.local <<'EOF'
echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled
echo 'never' > /sys/kernel/mm/transparent_hugepage/defrag
EOF
cat >> /etc/security/limits.conf <<'EOF'
* soft nofile 65536
* hard nofile 65536
* soft nproc  65536
* hard nproc  65536 
EOF
#配置yum源
cat >/etc/yum.repos.d/mongodb-org-7.0.repo <<'EOF'
[mongodb-org-7.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/7/mongodb-org/7.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-7.0.asc
EOF
#安装mongodb
sudo yum install -y mongodb-org
#或安装指定版本 sudo yum install -y mongodb-org-7.0.3 mongodb-org-database-7.0.3 mongodb-org-server-7.0.3 mongodb-mongosh-7.0.3 mongodb-org-mongos-7.0.3 mongodb-org-tools-7.0.3

#禁止mongodb自动更新
echo 'exclude=mongodb-org,mongodb-org-database,mongodb-org-server,mongodb-mongosh,mongodb-org-mongos,mongodb-org-tools' >> /etc/yum.conf

#使用yum安装的mongodb默认创建了mongod的用户,因为mongodb的启动需要使用单独的用户
#mongodb的配置文件/etc/mongod.conf
#从配置文件中可以看到默认的db目录是/var/lib/mongo,日志目录是/var/log/mongodb
#修改绑定的主机IP
vim /etc/mongod.conf
net.port: 27017							#mongodb的默认端口,保持默认即可
net.bindIp: 127.0.0.1,192.168.100.6		#添加主机IP
#启动mongodb
systemctl start mongod.service
systemctl status mongod.service
systemctl enable mongod.service
#检查日志和数据目录
tail -222f /var/log/mongodb/mongod.log
ls /var/lib/mongo/

#完整卸载mongodb,慎重
sudo systemctl stop mongod 
sudo yum erase $(rpm -qa | grep mongodb-org)
sudo rm -r /var/log/mongodb
sudo rm -r /var/lib/mongo
sudo userdel -r mongod 
sudo groupdel mongod 

# 2、使用tgz包安装mongodb
#环境初始化,安装一些linux常用的依赖
yum install chrony -y && systemctl enable --now chronyd
yum install vim lsof net-tools zip unzip tree wget curl bash-completion gcc make tcpdump bind-utils -y
sed -ri 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config 
setenforce 0
systemctl stop firewalld.service && systemctl disable firewalld.service
hostnamectl set-hostname mongodb
#安装mongodb所需的依赖
sudo yum install libcurl openssl xz-libs
#mongodb建议的优化,退出终端重新登录使临时修改立即生效
echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled
echo 'never' > /sys/kernel/mm/transparent_hugepage/defrag
chmod +x /etc/rc.d/rc.local
cat >> /etc/rc.d/rc.local <<'EOF'
echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled
echo 'never' > /sys/kernel/mm/transparent_hugepage/defrag
EOF
cat >> /etc/security/limits.conf <<'EOF'
* soft nofile 65536
* hard nofile 65536
* soft nproc  65536
* hard nproc  65536 
EOF
#下载mongodb的tgz安装包并解压,下载地址https://www.mongodb.com/try/download/community
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-7.0.4.tgz
tar -zxvf mongodb-linux-x86_64-rhel70-7.0.4.tgz 
sudo cp mongodb-linux-x86_64-rhel70-7.0.4/bin/* /usr/local/bin/
#下载并安装mongodb-shell,下载安装地址https://www.mongodb.com/docs/mongodb-shell/install/
wget https://downloads.mongodb.com/compass/mongodb-mongosh-2.1.1.x86_64.rpm
yum install ./mongodb-mongosh-2.1.1.x86_64.rpm
#创建用户创建目录
sudo groupadd mongod
sudo useradd  mongod -g mongod -d /var/lib/mongo -s /bin/bash
sudo mkdir -p /var/lib/mongo
sudo mkdir -p /var/log/mongodb
sudo chown -R mongod:mongod /var/lib/mongo
sudo chown -R mongod:mongod /var/log/mongodb
#切换用户,启动mongodb
su mongod
mongod --dbpath /var/lib/mongo --logpath /var/log/mongodb/mongod.log --port 27017 --bind_ip 0.0.0.0 --fork
#检查
tail -222f /var/log/mongodb/mongod.log 
netstat  -lntup | grep 27017
ps -ef | grep mongod
#更多参数可以通过命令行帮助查看
mongod -h 
#切换用户,停止mongodb
su mongod
mongod --shutdown --dbpath /var/lib/mongo
#使用配置文件启动mongodb,tag包安装的mongodb需要自己创建配置文件.Yum安装的mongodb默认会创建一个配置文件
#配置文件参考官网:https://www.mongodb.com/docs/manual/reference/configuration-options/
cat >>/etc/mongod.conf <<'EOF'
# mongod.conf
# for documentation of all options, see:
#   http://docs.mongodb.org/manual/reference/configuration-options/
processManagement:
   fork: true
   timeZoneInfo: /usr/share/zoneinfo
net:
   bindIp: localhost,127.0.0.1,192.168.244.5,/tmp/mongod.sock
   port: 27017
storage:
   dbPath: /var/lib/mongo
systemLog:
   destination: file
   path: "/var/log/mongodb/mongod.log"
   logAppend: true
#security:
#operationProfiling:
#replication:
#sharding:
## Enterprise-Only Options
#auditLog:
EOF
#然后使用配置文件启动即可
su mongod
mongod -f /etc/mongod.conf
#直接使用配置文件停止即可
su mongod
mongod -f /etc/mongod.conf  --shutdown
#使用systemd管理mongodb
#创建service服务文件
cat >/usr/lib/systemd/system/mongod.service << 'EOF'
# /usr/lib/systemd/system/mongod.service
[Unit]
Description=MongoDB Database Server
Documentation=https://docs.mongodb.org/manual
After=network-online.target
Wants=network-online.target

[Service]
User=mongod
Group=mongod
Environment="OPTIONS=-f /etc/mongod.conf"
Environment="MONGODB_CONFIG_OVERRIDE_NOFORK=1"
EnvironmentFile=-/etc/sysconfig/mongod
ExecStart=/usr/local/bin/mongod $OPTIONS
RuntimeDirectory=mongodb
# file size
LimitFSIZE=infinity
# cpu time
LimitCPU=infinity
# virtual memory size
LimitAS=infinity
# open files
LimitNOFILE=64000
# processes/threads
LimitNPROC=64000
# locked memory
LimitMEMLOCK=infinity
# total threads (user+kernel)
TasksMax=infinity
TasksAccounting=false
# Recommended limits for mongod as specified in
# https://docs.mongodb.com/manual/reference/ulimit/#recommended-ulimit-settings

[Install]
WantedBy=multi-user.target
EOF
#现在用root用户systemd管理mongodb即可,mongodb进程还是mongod用户启动的进程
usermod mongod -s /bin/false	#可选,禁止mongod用户登录
systemctl  start mongod.service
systemctl  status mongod.service
systemctl  enable mongod.service
systemctl  stop mongod.service 

docker安装mongodb

#安装docker
参考: https://blog.csdn.net/MssGuo/article/details/122694156
#docker安装mongodb官方文档
https://www.mongodb.com/docs/manual/tutorial/install-mongodb-community-with-docker/
https://hub.docker.com/r/mongodb/mongodb-community-server/tags
#1、官网方式安装mongodb
docker pull mongodb/mongodb-community-server
docker run --name mongo -d mongodb/mongodb-community-server:latest
docker log -f mongo 
docker inspect mongo 
#2、自定义安装mongodb
#官网启动的mongodb默认使用的匿名卷且没有映射端口到宿主机
mkdir /var/lib/mongo
#发现只有给777权限才能正常启动mongo容器,不然一直报创建文件权限不足,不晓得为啥子
chmod 777 -R /var/lib/mongo		
#启动mongodb容器
docker run --name mongo -p 27017:27017 -v /var/lib/mongo:/data/db -d mongodb/mongodb-community-server:latest 
#查看挂载的文件属主属组是101 nfsnobody
[root@node1 lib]# ll /var/lib/mongo/
-rw------- 1 101 nfsnobody 4096 Dec 28 14:29 collection-0-7528388073393714379.wt
-rw------- 1 101 nfsnobody 4096 Dec 28 14:29 collection-2-7528388073393714379.wt
#登录mongodb,容器里面内置了mongosh命令行工具
docker exec -it mongo mongosh

MongoDB Shell (mongosh)命令行工具

MongoDB Shell (mongosh)是一个JavaScript和Node.js REPL环境,用于与Atlas、本地或另一个远程主机上的MongoDB部署进行交互。
使用MongoDB Shell来测试查询并与MongoDB数据库中的数据进行交互。

#下载mongosh
#yum安装的mongodb默认会安装mongosh命令,如果没有安装,也可以从官网下载mongosh进行安装
#官方文档:https://www.mongodb.com/docs/mongodb-shell/
#下载地址:https://www.mongodb.com/try/download/shell
wget https://downloads.mongodb.com/compass/mongodb-mongosh-2.1.1.x86_64.rpm
yum install ./mongodb-mongosh-2.1.1.x86_64.rpm
which mongosh
#使用mongosh链接mongodb
# 1、链接到指定mongodb,无auth,所以没有写账号密码,默认进入test库
mongosh mongodb://192.168.244.5:27017/
# 2、链接到指定mongodb的foo库,无auth,所以没有写账号密码
mongosh mongodb://192.168.244.5:27017/foo

mongodb可视化-mongodb-compass、mongo-express

#1、官网提供的mongodb可视化是mongodb-compass
#用windows安装mongodb-compass,linux安装mongodb-compass需要图形化支持的
下载地址:https://www.mongodb.com/try/download/compass
https://downloads.mongodb.com/compass/mongodb-compass-1.41.0-win32-x64.exe

#2、第二种图形化是mongo-express,这里使用docker部署mongo-express,默认mongodb已经使用容器启动了
#查看mongodb容器
[root@node2 ~]# docker ps 
CONTAINER ID   IMAGE                                     COMMAND                  CREATED         STATUS         PORTS                                           NAMES
713fa33002c8   mongodb/mongodb-community-server:latest   "python3 /usr/local/…"   3 minutes ago   Up 3 minutes   0.0.0.0:27017->27017/tcp, :::27017->27017/tcp   mongo
#启动mongo-express
#link参数语法:--link 容器名称:别名  --link的原理就是在容器里面的/etc/hosts里面添加了链接对象容器的一个alias名称
docker run -d --link mongo:mongo --name mongo-express -p 8081:8081 mongo-express
#查看容器,状态正常
[root@node2 ~]# docker ps 
CONTAINER ID   IMAGE                                     COMMAND                  CREATED          STATUS          PORTS                                           NAMES
0a0944df7397   mongo-express                             "tini -- /docker-ent…"   18 seconds ago   Up 17 seconds   0.0.0.0:8081->8081/tcp, :::8081->8081/tcp       mongo-express
713fa33002c8   mongodb/mongodb-community-server:latest   "python3 /usr/local/…"   10 minutes ago   Up 10 minutes   0.0.0.0:27017->27017/tcp, :::27017->27017/tcp   mongo
[root@node2 ~]# docker exec -it mongo-express cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.2	mongo 713fa33002c8	#alias的名称
172.17.0.3	0a0944df7397
[root@node2 ~]# 
#在浏览器访问mongo-express
http://192.168.158.130:8081

mongodb配置文件

如果是使用yum安装的mongodb,则默认的配置文件为/etc/mongod.conf。下面简单讲解一下配置文件的关键参数。

#更多配置参数请查看官网:https://www.mongodb.com/docs/manual/reference/configuration-options/
[root@node2 ~]# cat  /etc/mongod.conf 
# mongod.conf

# for documentation of all options, see:
#   http://docs.mongodb.org/manual/reference/configuration-options/

# where to write logging data.
systemLog:						#mongodb日志相关参数
  destination: file				#日志要发送到哪里,可选值: file or syslog
  logAppend: true				#为true时,当mongos或mongod实例重新启动时,在现有日志文件的末尾添加新的条目。
  								#如果没有这个选项,mongod将备份现有日志并创建一个新文件。Default: false
  path: /var/log/mongodb/mongod.log		#日志destination是file则要指定文件路径

# Where and how to store data.
storage:						#mongodb数据存储相关参数
  dbPath: /var/lib/mongo		#存储路径

# how the process runs
processManagement:
  timeZoneInfo: /usr/share/zoneinfo

# network interfaces
net:							#网络配置相关参数
  port: 27017					#mongodb的端口,默认是27017
  bindIp: 127.0.0.1,192.168.158.130  # Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.

#security:				#访问认证相关的参数,这里未启用auth认证
#operationProfiling:
#replication:			#副本集相关的参数,这里部署的是单实例mongodb,未部署副本集mongodb
#sharding:				#分片集群相关的参数,这里部署的是单实例mongodb,未部署分片集群mongodb
## Enterprise-Only Options
#auditLog:
[root@node2 ~]# 
#更多配置参数请查看官网:https://www.mongodb.com/docs/manual/reference/configuration-options/

mongodb库、集合、文档

mongodb有一个特点,如果某个库下没有数据,没有集合的话,该库就等于不存在,mongodb只要创建一个库,在库下写入数据,该库才会生成。如下所示,登录的默认登录到test库,但show database却看不到就是这个意思。
mongosh命令行工具下可以使用tab键补全命令。

#使用mongosh登录mongodb
mongosh mongodb://192.168.185.130:27017
#登录url后不指定库则默认会进入test库,但test库是空的,所以show看不到
#查看全部的数据库
test> show databases
admin    40.00 KiB		#系统预留库,mongodb的系统管理库
config  108.00 KiB		#配置信息库,保存如分片等信息
local    72.00 KiB		#本地预留库,存储关键日志

#按table键可以补全
test> show 
show databases               show dbs                     show collections             show tables                  show profile                 show users                   show roles
show log                     show logs                    show startupWarnings         show automationNotices       show nonGenuineMongoDBCheck

库基本操作

test> show dbs;			#显示全部的数据库
test> show databases;	#显示全部的数据库
test> use unit;			#切换数据库,如果库不存在并且存在往库里面创建集合等操作则会创建库
test> use unit;			#切换数据库
unit> db				#查询当前数据库,其实可以通过前缀符也可以看到当前数据库
unit> db.dropDatabase()	#切换到指定库里面,然后删除数据库,删除的是当前unit数据库(慎用)

集合基本操作

unit> db.createCollection("students");		#创建集合,名称叫students
unit> db.createCollection("blog.user")		#创建集合,名称叫blog.user
unit> db.createCollection("blog.article")	#创建集合,名称叫blog.article
unit> show collections;						#查看当前库的全部集合
unit> db.students.drop()					#删除集合,db是关键字,students是集合名称,所以删除的是students集合

文档的增删改查CURD

插入文档
#插入文档主要使用两个方法
insertOne方法与insertMany方法,如果集合不存在则默认自动创建集合.
#往students集合中插入1个文档,会自动创建唯一主键ID,db是关键字,students是集合名称,insertOne是插入方法
db.students.insertOne({name: "sue",age: 26,status: "pending"})		
#往students集合中插入多个文档,会自动创建唯一主键ID,db是关键字,students是集合名称,insertMany是插入方法
db.students.insertMany([
   { item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
   { item: "mat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
   { item: "mousepad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } }
])
查询文档

官网:https://www.mongodb.com/docs/manual/tutorial/query-documents/
查询集合中的文档,主要使用find()方法。

#查询文档语法格式
#其中db是关键字,然后是集合名称,然后是find()方法
db.collection_name.find(  { <field1>: { <operator1>: <value1> }, ... }  ).otherfuntion
#先插入一些示例文档
db.inventory.insertMany( [
   { item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
   { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" },
   { item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
   { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
   { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }
]);
#查询集合全部文档,即find()方法不加任何条件
db.inventory.find({})	
db.inventory.find()	
等价于mysql的:
SELECT * FROM inventory

#查询前n个文档
db.inventory.find().limit(n);
#指定相等条件,即查询指定的key等于某个values
语法:{ <field1>: <value1>, ... }
db.inventory.find( { "item": "planner" } )
db.inventory.find( { item: 'planner' } )
db.inventory.find( { "size.uom": "in" } )	#使用点引用子key,因为size是嵌套的json数据
等价于mysql的
SELECT * FROM inventory WHERE item="planner"
比较运算符
```bash
$eq 	等于
$ne 	不等于
$lt		小于
$lte	小于等于
$gt 	大于
$gte	大于等于
$in		匹配数组中的值,类似于mysql中的in运算符
$nin	不匹配数组中的值,类似于mysql中的not in运算符

# $eq 等于运算符,查询status等于A的文档
db.inventory.find({status: {$eq: 'A'}})		#等于可以不使用运算符直接写,如下
db.inventory.find({status: 'A'})			#等价于上面一行
等价于mysql的:
SELECT * FROM inventory WHERE status ="A"

# $ne 不等于运算符,查询status不等于A的文档
db.inventory.find({status: {$ne: 'A'}})
等价于mysql的:
SELECT * FROM inventory WHERE status !="A"

# $lt 小于运算符,查询qty小于50的文档
db.inventory.find({qty: {$lt: 50}})
# $lte 小于运算符,查询qty小于等于50的文档
db.inventory.find({qty: {$lte: 50}})
等价于mysql的:
SELECT * FROM inventory WHERE qty < 50
SELECT * FROM inventory WHERE qty <= 50

# $gt 大于运算符,查询qty大于50的文档
db.inventory.find({qty: {$gt: 50}})
# $gte 大于运算符,查询qty大于等于50的文档
db.inventory.find({qty: {$gte: 50}})
等价于mysql的:
SELECT * FROM inventory WHERE qty > 50
SELECT * FROM inventory WHERE qty >= 50

# $in 匹配数组中的值,运算符要求是一个数组,数组使用[]符号,查询status值是B或C的文档
db.inventory.find({status: {$in: ['B','C']}})
# $nin 不匹配数组中的值,运算符要求是一个数组,数组使用[]符号,查询status值是B或C的文档
db.inventory.find({status: {$nin: ['B','C']}})
等价于mysql的:
SELECT * FROM inventory WHERE status in ('B','C')
SELECT * FROM inventory WHERE status not in ('B','C')
逻辑运算符
$and	逻辑与,基于一个或多个表达式执行逻辑AND操作,当且仅当所有表达式都为真时,结果才为真。
$or		逻辑或,逻辑OR操作,只要其中一个表达式为真,结果就为真。
$not	取反, 对结果取反

# $and 逻辑与不用显示的表示,直接使用隐式的即可,
查询status等于A和qty小于30的文档
db.inventory.find({ status: "A", qty: { $lt: 30 } })
等价于mysql的:
SELECT * FROM inventory WHERE status="A" and qty<30
这样也能显示的表示逻辑与,但是不建议:db.inventory.find( { $and: [ { status: "A" }, { qty: { $lt: 30 } } ] } )

# $or 逻辑或,满足其中一个条件即可
# or格式
{ 
   $or: [ 
       {条件1}, 
       {条件2},
       {..n} 
   ] 
} 
#查询status等于A或qty小于30的文档
db.inventory.find( { 
$or: [ { status: "A" }, { qty: { $lt: 30 } } ] 
} 
)
等价于mysql的:
SELECT * FROM inventory WHERE status="A" or qty<30

# and和or联合使用,查询status等于A并且(qty小于30或item以p开头)的文档
db.inventory.find( {
     status: "A",
     $or: [ { qty: { $lt: 30 } }, { item: /^p/ } ]
} )
等价于mysql的:
SELECT * FROM inventory WHERE status = "A" AND ( qty < 30 OR item LIKE "p%")

# $not操作符, 对结果取反
#含义为查询qty值不大于50的文档
db.inventory.find( { qty: { $not: { $gt: 50 } } } )
嵌套字段
#嵌套字段直接使用点引用即可,注意这时的字段key要使用引号扩起来
db.inventory.find( { "size.uom": "sz" } )
db.inventory.find({"size.uom": {$in: ['cd','cm']}})
db.inventory.find( { "size.h": { $lt: 15 }, "size.uom": "in", status: "D" } )
#下面两条查询语句等价
db.inventory.find( { "size.h": 14, "size.w": 21, "size.uom": "cm" }  )
db.inventory.find( { size: { h: 14, w: 21, uom: "cm" } } )
匹配数组
#如下,插入示例数据
db.inventory.insertMany([
   { item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] },
   { item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] },
   { item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] },
   { item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] },
   { item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] }
]);
#上面数据对于tags字段,该字段是数组类型
#查询tags数组元素是["red", "blank"]的文档,这种匹配要求数组元素值、个数、顺序完全一致才会匹配
db.inventory.find( {tags: ["red", "blank"]})

#使用$all 表示查询包含元素“red”和“blank”的数组,而不考虑数组中的顺序,主要包含即可
db.inventory.find( { tags: { $all: ["red", "blank"] } } )

#查询数组字段是否至少包含一个具有指定值的元素,请使用过滤器{ <field>: <value> } 其中<value> 是元素值
#tags是一个数组,查询tags包含字符串"red"作为其元素之一的文档
db.inventory.find( { tags: "red" } )

#使用查询操作符指定数组字段中元素的条件,至少1个元素满足条件即可匹配
#格式 { <array field>: { <operator1>: <value1>, ... } }
db.inventory.find( { dim_cm: { $gt: 25 } } )

#多条件指定查询数组元素,至少1个元素满足条件即可匹配
#下面的示例含义是,集合中数组字段dim_cm中单个元素同时满足大于15并且小于20,或者一个元素满足大于15,另外一个元素小于20。
db.inventory.find( { dim_cm: { $gt: 15, $lt: 20 } } )
下面这些都能匹配的上,如下:
dim_cm:[ 14, 21 ]匹配的上,因为21大于15,另外一个元素14小于20、
dim_cm:[ 10, 15.25 ]匹配的上,因为15.25满足大于15并且小于20
dim_cm:[ 22.85, 14 ]匹配的上,因为22.85满足大于15,另外一个元素14小于20
dim_cm:[ 22.85, 21, 19 ]匹配的上,因为22.85满足大于15,另外一个元素19小于20

#使用$elemMatch操作符为数组元素指定多个条件,使至少一个数组元素满足所有指定条件
#下面的示例查询di_cm数组中至少包含一个大于22且小于30的元素的文档
db.inventory.find( { dim_cm: { $elemMatch: { $gt: 22, $lt: 30 } } } )

#根据数组索引位置查询元素,数组元素从0开始
#使用点表示法查询时,字段和嵌套字段必须在引号内
#下面的例子查询数组dim_cm中第二个元素大于25的所有文档
db.inventory.find( { "dim_cm.1": { $gt: 25 } } )
#查询数组dim_cm中第一个元素为14的所有文档
db.inventory.find( { "dim_cm.0": 14  } )

#使用$size操作符,按数组元素个数查询文档
#例如,下面的代码选择dim_cm数组中有3个元素的文档
db.inventory.find( { "dim_cm": {$size: 3}  } )
匹配嵌套数组
#插入文档,主要是嵌套数组
db.inventory.insertMany( [
   { item: "journal", instock: [ { warehouse: "A", qty: 5 }, { warehouse: "C", qty: 15 } ] },
   { item: "notebook", instock: [ { warehouse: "C", qty: 5 } ] },
   { item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 15 } ] },
   { item: "planner", instock: [ { warehouse: "A", qty: 40 }, { warehouse: "B", qty: 5 } ] },
   { item: "postcard", instock: [ { warehouse: "B", qty: 15 }, { warehouse: "C", qty: 35 } ] }
]);
#下面的例子选择所有的文档,其中一个元素在stock数组中匹配指定的文档:
db.inventory.find( { "instock": { warehouse: "A", qty: 5 } } )
#整个嵌套文档上的相等匹配要求精确匹配指定的文档,包括字段顺序。例如,下面的查询不匹配库存集合中的任何文档
db.inventory.find( { "instock": { qty: 5, warehouse: "A" } } )

#指定文档数组中嵌入字段的查询条件
#如果您不知道嵌套在数组中的文档的索引位置,请将数组字段的名称与嵌套文档中的字段名称连接起来
#下面的示例选择所有文档,其中至少有一个嵌入文档,其中包含字段qty的值小于或等于20
db.inventory.find( { 'instock.qty': { $lte: 20 } } )

#使用数组索引查询嵌入文档中的字段,索引从0开始
#查询instock数组的第1个元素的qty字段小于等于20的文档
db.inventory.find( { 'instock.0.qty': { $lte: 20 } } )

#使用$elemMatch操作符在嵌入文档数组上指定多个标准,使至少一个嵌入文档满足所有指定的标准
#下面的例子,查询文档,stock数组中至少有一个元素,其中包含字段qty等于5和字段warehouse等于A
db.inventory.find( { "instock": { $elemMatch: { qty: 5, warehouse: "A" } } } )
instock: [ { warehouse: 'A', qty: 5 }, { warehouse: 'C', qty: 15 } ] 满足条件,这说明嵌套元素里的字段顺序不论。

#下面的示例查询在stock数组中至少有一个包含字段qty大于10小于等于20的嵌入文档的文档:
db.inventory.find( { "instock": { $elemMatch: { qty: { $gt: 10, $lte: 20 } } } } )

#找到在 instock 数组中【至少有一个嵌入文档包含 qty > 10,以及至少有一个嵌入文档(但不一定是同一个嵌入文档)包含 qty ≤20 】的文档
db.inventory.find( { "instock.qty": { $gt: 10,  $lte: 20 } } )
下面这些都会匹配:
instock: [ { warehouse: 'A', qty: 40 }, { warehouse: 'B', qty: 5 } ]
instock: [ { warehouse: 'B', qty: 15 }, { warehouse: 'C', qty: 35 } ]

#以下示例查询的文档中,instock数组至少有一个包含字段qty等于5的嵌入文档,并且至少有一个包含字段warehouse等于A的嵌入文档(但不一定是相同的嵌入文档):
db.inventory.find( { "instock.qty": 5, "instock.warehouse": "A" } )
下面这些都满足:
instock: [ { warehouse: 'A', qty: 5 }, { warehouse: 'C', qty: 15 } ]
instock: [ { warehouse: 'A', qty: 40 }, { warehouse: 'B', qty: 5 } ]
投影返回指定字段
# 投影就是手电筒从上问下照射的含义
#默认情况下,MongoDB中的查询返回匹配文档中的所有字段。
#为了限制MongoDB发送给应用程序的数据量,您可以包含一个投影文档来指定或限制要返回的字段。
#插入测试数据-文档
db.inventory.insertMany( [
  { item: "journal", status: "A", size: { h: 14, w: 21, uom: "cm" }, instock: [ { warehouse: "A", qty: 5 } ] },
  { item: "notebook", status: "A",  size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "C", qty: 5 } ] },
  { item: "paper", status: "D", size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "A", qty: 60 } ] },
  { item: "planner", status: "D", size: { h: 22.85, w: 30, uom: "cm" }, instock: [ { warehouse: "A", qty: 40 } ] },
  { item: "postcard", status: "A", size: { h: 10, w: 15.25, uom: "cm" }, instock: [ { warehouse: "B", qty: 15 }, { warehouse: "C", qty: 35 } ] }
]);
#默认返回文档的全部字段
db.inventory.find( { status: "A" } )
等价于mysql:
SELECT * from inventory WHERE status = "A"

#只返回指定字段和_id字段
#注意_id字段默认就是1,如果不需要显示id,需要显示的设置_id: 0
#通过设置<field>:1 显示的标明要投影哪些字段
#下面的操作返回与查询匹配的所有文档。在结果集中,只返回匹配文档中的项、状态和(默认情况下)_id字段。
db.inventory.find( { status: "A" }, { item: 1, status: 1 } )
等价于mysql:
SELECT _id, item, status from inventory WHERE status = "A"
#不显示id,抑制id,因为_id字段默认就是1,如果不需要显示id,需要显示的设置_id: 0
db.inventory.find( { status: "A" }, { item: 1, status: 1, _id: 0 } )
等价于mysql:
SELECT item, status from inventory WHERE status = "A"

#返回除排除字段外的所有字段
#您可以使用投影来排除特定字段,而不是列出要在匹配文档中返回的字段
#下面的示例返回匹配文档中除了status和stock字段以外的所有字段
db.inventory.find( { status: "A" }, { status: 0, instock: 0 } )
#下面的示例返回全部文档中除了status和stock字段以外的所有字段
db.inventory.find( {}, { status: 0, instock: 0 } )

#注意注意
#投影的字段要么都是1要么都是0,不能有些是1有些是0,但是_id除外,类比mysql的select字段,不存在既选又不选的说法
db.inventory.find( { status: "A" }, { status: 0, instock: 0,_id:1 } )	#正常查询
db.inventory.find( { status: "A" }, { status: 1, instock: 1,_id:0 } )	#正常查询
db.inventory.find( { status: "A" }, { status: 0, instock: 0,size:1 } )	#报错,投影的字段要么都是1要么都是0,不能有些是1有些是0

#返回嵌入文档中的特定字段
#可以返回嵌入文档中的特定字段。使用点表示法引用嵌入字段,并在投影文档中设置为1。
#显示item、status、size中uom字段
db.inventory.find(
   { status: "A" },
   { item: 1, status: 1, "size.uom": 1 }	#可以这么写:{ item: 1, status: 1, size: { uom: 1 } }.
)
#除了不显示size.uom字段,其他字段都显示
db.inventory.find(
   { status: "A" },	
   { "size.uom": 0 }						#可以这么写{ size: { uom: 0 } }
)

#数组的嵌入
#返回数组中的项目特定数组元素
db.inventory.find( { status: "A" }, { item: 1, status: 1, "instock.qty": 1 } )
# $slice: -1映射数组的最后一个元素
db.inventory.find( { status: "A" }, { item: 1, status: 1, instock: { $slice: -1 } } )
null值匹配或没有的字段
#插入文档
db.inventory.insertMany([
   { _id: 1, item: null },	#这行文档item字段为null	
   { _id: 2 }				#这行文档没有item字段
])
#查询item为null的文档,{item: null}查询匹配的文档要么包含值为null的item字段,要么不包含item字段
#所以查询结果会返回上面两条文档
db.inventory.find( { item: null } )

#使用类型检查来查询
# { item : { $type: 10 } } 仅匹配文档中item字段值是null,null的BSON类型是10
#所以结果就是返回上面的第一文档
db.inventory.find( { item : { $type: 10 } } )

#exists 匹配
#{ item : { $exists: false } } 匹配不包含item字段的文档
#返回结果就是返回上面的第二文档
db.inventory.find( { item : { $exists: false } } )
更新文档
#更新文档主要有三个方法
db.collection.updateOne(<filter>, <update>, <options>)
db.collection.updateMany(<filter>, <update>, <options>)
db.collection.replaceOne(<filter>, <update>, <options>)
db.collection.findAndModify().
语法格式:
{
   <operator1>: { <field1>: <value1>, ... },
   <operator2>: { <field2>: <value2>, ... },
   ...
}
#插入一下测试数据文档
db.inventory.insertMany( [
   { item: "canvas", qty: 100, size: { h: 28, w: 35.5, uom: "cm" }, status: "A" },
   { item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
   { item: "mat", qty: 85, size: { h: 27.9, w: 35.5, uom: "cm" }, status: "A" },
   { item: "mousepad", qty: 25, size: { h: 19, w: 22.85, uom: "cm" }, status: "P" },
   { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "P" },
   { item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
   { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
   { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" },
   { item: "sketchbook", qty: 80, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
   { item: "sketch pad", qty: 95, size: { h: 22.85, w: 30.5, uom: "cm" }, status: "A" }
] );

#更新一个文档
#查找item等于"paper"的并更新size.uom字段值为"cm"以及status字段值为"P"
#使用$currentDate操作符将lastModified字段的值更新为当前日期,如果lastModified字段不存在,$currentDate将创建该字段
db.inventory.updateOne(
   { item: "paper" },
   {
     $set: { "size.uom": "cm", status: "P" },
     $currentDate: { lastModified: true }
   }
)
更新后的文档(原来是没有lastModified字段的):
unit> db.inventory.find({item:"paper"})
[
  {
    _id: ObjectId('65961d3ad6b89310d94a149d'),
    item: 'paper',
    qty: 100,
    size: { h: 8.5, w: 11, uom: 'cm' },
    status: 'P',
    lastModified: ISODate('2024-01-04T03:26:05.556Z')
  }
]
unit> 

#更新多个文档
#使用$set操作符更新uom字段为"in",status字段的值为"P"
#使用$currentDate操作符将lastModified字段的值更新为当前日期,如果lastModified字段不存在,$currentDate将创建该字段
db.inventory.updateMany(
   { "qty": { $lt: 50 } },
   {
     $set: { "size.uom": "in", status: "P" },
     $currentDate: { lastModified: true }
   }
)

#替代文档
#要替换文档中除了_id字段以外的全部内容,将一个全新的文档作为第二个参数传递给db.collection.replaceOne()方法
#替换文档可以具有与原始文档不同的字段,在替换文档中,您可以省略_id字段,因为_id字段是不可变的;但是,如果包含_id字段,它必须与当前值相同.
#下面示例替换指定id的文档
db.inventory.replaceOne(
   { _id: ObjectId('65961d3ad6b89310d94a1499') },
   { item: "journal", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 40 } ] }
)

#更多更新参照、更新操作符请查看官网
删除文档
#删除文档使用两个方法
db.collection.deleteMany()	#删除一个或多个文档或全部文档
db.collection.deleteOne()	#删除单个文档,即使过滤器匹配到多个文档仍然只是删除第一个文档

#删除集合全部文档
db.inventory.deleteMany({})
#删除status等于A的文档
db.inventory.deleteMany({ status : "A" })
#删除qty大于等于50的文档
db.inventory.deleteMany({qty:{$gte:50}})

#删除status等于P的文档,由于是deleteOne方法,所以即使匹配到多个文档仍然只是删除第一个文档
db.inventory.deleteOne( { status: "P" } )

mongodb的访问控制

mongodb数据库应该要设置用户账号密码来保证安全。mongodb内置了一系列的role角色来限定用户所具备的权限。结合用户和角色管理,mongodb就具备安全的访问控制。

理解mongodb的用户、库的关系

mongodb的用户是基于数据库(database)和集合(collections)来进行授权和访问控制的,在mongodb中,用户是在特定数据库中创建的,并且每个用户都可以被授予对该数据库中特定集合的不同级别的权限。
用户在MongoDB中被分配了角色(Role),角色定义了用户在数据库中的权限和操作。MongoDB提供了一些内置角色,如读取(read)和写入(write)角色,也允许用户自定义角色以满足特定需求。
当用户被创建时,需要为其指定所属的数据库,并且可以为该用户分配适当的角色。这样,用户将只能在其所属数据库中的指定集合上执行授权的操作。

admin库的作用

在MongoDB中,admin是一个特殊的系统数据库,它在数据库管理和权限控制方面具有特殊的作用。下面是admin库的主要作用:
1、用户管理:admin库是管理MongoDB用户和角色的主要地方。它存储了用户凭据、角色定义和权限分配。通过admin库,可以创建、修改和删除用户,分配角色和权限,以及执行与用户身份验证和授权相关的操作;
2、权限控制:admin库是MongoDB的权限控制中心。通过admin/库,可以定义和管理数据库用户的访问权限,管理员可以使用admin库中的角色和权限定义来限制用户对数据库的操作,以保护敏感数据和确保系统安全。
3、集群管理:admin库也用于管理MongoDB集群和副本集。通过admin库,可以执行各种集群管理任务,如添加和删除分片、启动和停止副本集成员、配置副本集等。
4、系统状态和监控:admin库存储了与MongoDB服务器状态和监控相关的信息。通过admin库,可以获取服务器的运行状态、连接信息、日志记录等。这对于系统管理和故障排除非常有用。
总之,admin库是MongoDB的系统级数据库,用于管理用户、权限、集群和监控等关键任务。它提供了对数据库管理和安全的细粒度控制,并为管理员提供了管理和监视MongoDB实例的中心位置。

mongodb内置角色

#mongodb内置了很多角色,可以参考官网
https://www.mongodb.com/docs/manual/reference/built-in-roles/#mongodb-authrole-readWriteAnyDatabase
Database user和database administration 这两类角色在每个库都内置有,其他全部的内置角色都在admin库。

登录admin库,创建管理员用户

创建用户可以在任何数据库下创建,下面例子我们模拟mongodb刚安装完成,还没有开启访问认证功能,所以我们要首先登录mongodb,然后在admin库里面创建一个具有管理任何数据库,任何数据库读写的角色的管理员权限用户;上面我们说过,Database user和database administration 这两个角色在每个库都内置有,其他全部的内置角色都在admin库,因为具有管理任何数据库,任何数据库读写的角色是在admin库里的,所以要在admin库里面创建管理员权限用户。

下面按照官网说明,创建一个拥有userAdminAnyDatabase和readWriteAnyDatabase的管理员用户,注意该用户的权限并不是超级管理员权限,但权限已经足够高了。
官方文档:https://www.mongodb.com/docs/manual/tutorial/configure-scram-client-authentication/#create-the-user-administrator

#先不配置访问认证,无账号密码登录mongodb
mongosh mongodb://192.168.158.130:27017/
#创建管理员用户,创建完成退出
use admin		#切换到admin库下创建管理员用户
db.createUser(
  {
    user: "admin",			#管理员用户名
    pwd: passwordPrompt(),  #提示输入密码,也可以直接指定字符串密码,如"admin123"
    roles: [
      { role: "userAdminAnyDatabase", db: "admin" },
      { role: "readWriteAnyDatabase", db: "admin" }
    ]
  }
)
#解释说明
user: "admin",指定管理员用户名;
pwd: passwordPrompt(), 使用passwordPrompt()方法可提示输入密码,也可以直接指定字符串密码,如"admin123"
userAdminAnyDatabase角色允许用户有这些权限:创建用户、授予或撤销用户的角色、创建或修改自定义角色;
readWriteAnyDatabase角色允许对任何数据库具有读写权限;
db: "admin",表示该用户的身份验证数据库,一般写当前数据库即可.

注意: 创建用户的数据库(在本例中为admin库)是用户的身份验证数据库,尽管用户需要对该数据库进行身份验证,但该用户可以在其他数据库中拥有角色,主要用户所关联的角色拥有足够的权限,换句话说,用户的身份验证数据库不会限制用户的特权。

在MongoDB中,虽然创建用户时需要指定一个默认的数据库(上面的例子中是admin数据库),但用户是否可以在任何数据库中执行操作,取决于该用户所关联的角色是否有在该数据库上具有相应的权限。

当创建新用户时,我们可以选择将其与特定的数据库关联起来,作为用户登录的身份验证数据库。例如,在上面的例子中,我们将新用户"admin""admin"数据库关联起来,并赋予了该用户在"admin"数据库上执行用户管理操作和读取/写入任何数据库数据的权限。然而,这并不意味着用户只能在与其关联的数据库中进行操作,一旦用户被创建并赋予了适当的角色和权限,它可以在任何其他数据库中执行操作,只要它具有所需的角色和权限。
因此,尽管创建用户时可以指定默认的数据库,但用户的操作范围并不仅限于该数据库。

修改配置文件,启用账号密码认证

上面创建了管理员账号,关闭mongodb实例,修改配置:
1、如果是使用yum等方式安装的mongodb,默认的配置文件是/etc/mongod.conf,修改配置文件,添加或启动参数如下:

vim /etc/mongod.conf
security:
    authorization: enabled

2、如果是直接mongod命令启动的mongodb,启动时加上–auth参数即可,如下:
mongod --dbpath /var/lib/mongo --logpath /var/log/mongodb/mongod.log --port 27017 --bind_ip 0.0.0.0 --auth --fork
3、如果是docker启动的mongodb,启动时加上–auth参数即可,如下:

docker run --name mongo -d mongodb/mongodb-community-server:latest --auth
docker run --name mongo -p 27017:27017 -v /var/lib/mongo:/data/db -d mongodb/mongodb-community-server:latest --auth

4、使用管理员账号密码登录mongodb

1、命令行指定账号密码登录及用户的身份验证数据库(建议使用这种)
mongosh -u "admin" -p "admin123" --authenticationDatabase admin  mongodb://192.168.158.130:27017/
2、登录后再认证
mongosh mongodb://192.168.158.130:27017/	#先无账号密码登录
use admin			#登录后需要先切换到admin数据库,因为admin用户是在admin库创建的,不切换操作其他任何命令都显示认证报错
db.auth("admin", "admin123")	#亦可以db.auth("admin", passwordPrompt()),用户认证通过就可以自由操作数据库了
use unit						#因为admin用户是管理员角色,对任何数据库都要权限,所以正常切换到其他数据库

创建超级管理员

想要创建一个超级管理员,赋予root角色即可:

#先登录数据库,--authenticationDatabase参数直接指定用户存放的数据库,也称用户的身份验证数据库,即在哪个库下创建的用户就写对应的库名
mongosh -u "admin" -p "admin123" --authenticationDatabase admin mongodb://192.168.158.130:27017/ 
use admin		#我们把root用户放到admin库下,因为root角色在admin库下
db.createUser(
  {
    user: "root",
    pwd: passwordPrompt(),
    roles: [
      { role: "root", db: "admin" }
    ]
  }
)
#创建成功,可以查看当前全部用户
db.getUsers()
#退出,使用root超级管理员登录
mongosh -u "root" -p "root" --authenticationDatabase admin mongodb://192.168.158.130:27017/ 

创建数据库普通用户

#参考官方文档的角色,单独数据库的管理员角色和数据库的平台用户角色
https://www.mongodb.com/docs/manual/reference/built-in-roles/#database-user-roles
https://www.mongodb.com/docs/manual/reference/built-in-roles/#database-administration-roles

#为unit数据库创建数据库管理员(这里创建的是unit数据库管理员角色)
#创建用户时并不一定需要在admin数据库下进行,上面我们创建的用户都是管理员角色所以放在admin下而已,是因为那些角色都在admin库中
#首先得使用具有创建用户权限的用户登录,然后才能创建用户,这里我们使用上面创建的admin管理员用户先登录
mongosh -u "admin" -p "admin123" --authenticationDatabase admin mongodb://192.168.158.130:27017/ 
use unit	#切换到unit,我们要在这个库下创建对unit库具有管理员权限用户
db.createUser(
  {
    user: "unit",
    pwd: "unit123",
    roles: [
      { role: "dbOwner", db: "unit" }	
    ]
  }
)
#dbOwner角色表示数据库管理员,如果只想给普通用户具有读写权限那么可以给readWrite角色
#使用unit账号登录
mongosh -u "unit" -p "unit123" --authenticationDatabase unit mongodb://192.168.158.130:27017/ 
test> show databases;	#只能看到unit数据库了
unit  200.00 KiB
test> use unit
switched to db unit
unit> show collections	#正常
blog.article
blog.user
inventory
students
teacher
unit> 	#这样unit用户就可以给开发使用了

删除用户

#先使用具有删除用户的管理员角色登录,然后删除即可
db.dropUser("unit")

mongodb的高可用架构

1、Replica Set 副本集,也可称复制集,最小架构是1主2从,主节点负责读写,从节点只读,当主节点崩溃会自动从从节点选择一个成为主节点;
2、shard分片集群,最常见的架构是:1个mongos,1个config server,config server由3个节点组成副本集,3个shard分片,每个shard分片由3个节点组成副本集。

Replica Set 副本集模式

Replica Set 即可以称之为副本集,也可以称之为复制集,意思一样。
MongoDB中的副本集是一组维护相同数据集的mongod进程。副本集提供冗余和高可用性,是所有生产部署的基础。
Primary主节点做读写,Secondary用于同步主节点的数据,只读。副本集的成员个数应该总是奇数。这将确保选举顺利进行。所以最小副本集是3个mongodb节点。
官方文档:https://www.mongodb.com/docs/manual/replication/https://www.mongodb.com/docs/manual/tutorial/deploy-replica-set/

#docker部署3节点副本集,--replSet "mongodb-rs"参数是指定副本集的名称
docker run --name mongo-1 -p 27017:27017 -v /opt/mongo-1:/data/db -d mongodb/mongodb-community-server:latest --replSet "mongodb-rs"
docker run --name mongo-2 -p 27018:27017 -v /opt/mongo-2:/data/db -d mongodb/mongodb-community-server:latest  --replSet "mongodb-rs"
docker run --name mongo-3 -p 27019:27017 -v /opt/mongo-3:/data/db -d mongodb/mongodb-community-server:latest  --replSet "mongodb-rs"
#登录其中一个节点,初始化副本集,初始化完成之后,前缀符号都变了
docker exec -it mongo-1 bash
mongosh
rs.initiate( {
   _id : "mongodb-rs",			#这是副本集的名称,与上面docker启动时的名称保持一致
   members: [					#节点IP和端口
      { _id: 0, host: "192.168.158.130:27017" },
      { _id: 1, host: "192.168.158.130:27018" },
      { _id: 2, host: "192.168.158.130:27019" }
   ]
})
#查看副本集的配置
rs.conf()
#查看副本集的状态
rs.status()

#测试,在主库创建库写入文档,主库读写文档正常
mongodb-rs [direct: primary] test> show dbs
admin    80.00 KiB
config  212.00 KiB
local   404.00 KiB
mongodb-rs [direct: primary] test> use unit
mongodb-rs [direct: primary] unit> db.students.insertMany([
     { item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
     { item: "mat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
     { item: "mousepad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } }
  ])
mongodb-rs [direct: primary] unit> db.students.find()	#正常查询

#在从库测试,从库已经同步了主库的数据,但是此时不能读
mongodb-rs [direct: secondary] test> show dbs
admin    80.00 KiB
config  244.00 KiB
local   420.00 KiB
unit     40.00 KiB										#库已经同步了
mongodb-rs [direct: secondary] test> use unit
mongodb-rs [direct: secondary] unit> show collections	#集合可以看到
students
mongodb-rs [direct: secondary] unit> db.students.find()	#查询报错
MongoServerError: not primary and secondaryOk=false - consider using db.getMongo().setReadPref() or readPreference in the connection string

mongodb-rs [direct: secondary] unit> db.getMongo().setReadPref({"mode":"secondaryPreferred"})	#开启从库读功能
mongodb-rs [direct: secondary] unit> db.students.find()		#从库正常读取文档
#从库不能写数据
  mongodb-rs [direct: secondary] unit> db.students.insertMany([
...      { item: "fdfd", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
...      { item: "eeeee", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
...      { item: "mousephhhhhhhhhhhhhad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } }
...   ])
Uncaught:
MongoBulkWriteError: not primary		#从库写数据报错了,只能主库写数据
Result: BulkWriteResult {
  insertedCount: 0,
  matchedCount: 0,
  modifiedCount: 0,
  deletedCount: 0,
  upsertedCount: 0,
  upsertedIds: {},
  insertedIds: {
    '0': ObjectId('6597b7e70c0e47e1f523447d'),
    '1': ObjectId('6597b7e70c0e47e1f523447e'),
    '2': ObjectId('6597b7e70c0e47e1f523447f')
  }
}
Write Errors: []
mongodb-rs [direct: secondary] unit> 

#主从切换测试
#停掉主库
docker stop mongo-1
#测试发现有个从库变成主库了,既然是主库那么可以写数据
db.students.insertMany([
     { item: "fdfd", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
     { item: "eeeee", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
     { item: "mousephhhhhhhhhhhhhad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } }
  ])


mongodb-rs [direct: secondary] unit>	#原来是从库的,现在变成主库了,正常读写
mongodb-rs [direct: primary] unit> db.students.insertMany([
...      { item: "fdfd", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
...      { item: "eeeee", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
...      { item: "mousephhhhhhhhhhhhhad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } }
...   ])
{
  acknowledged: true,
  insertedIds: {
    '0': ObjectId('6597b8330c0e47e1f5234480'),
    '1': ObjectId('6597b8330c0e47e1f5234481'),
    '2': ObjectId('6597b8330c0e47e1f5234482')
  }
}
mongodb-rs [direct: primary] unit> db.students.find()

#测试再启动停掉的节点,节点就会变成从库
#至此,3节点的副本集演示完毕.
#使用yum部署mongodb副本集并开启auth
#官方文档
https://www.mongodb.com/docs/manual/tutorial/deploy-replica-set-with-keyfile-access-control/
#先在3台服务器上部署mongodb,这里忽略,参考前面的部署
#配置主机名
cat >> /etc/hosts<<'EOF'
192.168.158.128 mongodb-1
192.168.158.129 mongodb-2
192.168.158.130 mongodb-3
EOF
#官方提示
为避免IP地址变更导致配置更新,建议使用DNS主机名代替IP地址。在配置副本集成员或分片集群成员时,使用DNS主机名而不是IP地址尤为重要。使用主机名而不是IP地址来配置跨拆分网络水平的集群。从MongoDB 5.0开始,只配置了IP地址的节点将无法启动验证并且不会启动。
#副本集使用创建keyfile
openssl rand -base64 756 > /etc/mongodbRs-keyfile
chmod 400 /etc/mongodbRs-keyfile
chown mongod.mongod /etc/mongodbRs-keyfile
scp /etc/mongodbRs-keyfile root@mongodb-2:/etc/mongodbRs-keyfile
chown mongod.mongod /etc/mongodbRs-keyfile
scp /etc/mongodbRs-keyfile root@mongodb-3:/etc/mongodbRs-keyfile
chown mongod.mongod /etc/mongodbRs-keyfile

#修改配置文件,下面是第一个节点的配置文件,其他节点对应的修改即可
vim /etc/mongod.conf
[root@mongodb-1 ~]# grep -Ev "#|$^" /etc/mongod.conf 
systemLog:						#保持默认即可
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log
storage:						#保持默认即可
  dbPath: /var/lib/mongo
processManagement:				#保持默认即可
  timeZoneInfo: /usr/share/zoneinfo
net:							#保持默认即可
  port: 27017
  bindIp: 127.0.0.1,192.168.158.128,mongodb-1,localhost		#这里一定要添加主机名
security:					
  keyFile: /etc/mongodbRs-keyfile 	#改成keyfile路径
replication:
  replSetName: mongodb-rs		#设置副本集的名称,3个节点副本集名称都要一样
[root@mongodb-1 ~]# 

#启动mongodb
systemctl start mongod.service
systemctl status mongod.service && systemctl enable mongod.service
#登录其中一台初始化
mongosh mongodb://192.168.158.128:27017/
rs.initiate( {
   _id : "mongodb-rs",	#副本集的名称,与配置文件里面的保持一致
   members: [			#这里使用IP端口表示mongodb的示例好像也是正常,没有用主机名
      { _id: 0, host: "192.168.158.128:27017" },
      { _id: 1, host: "192.168.158.129:27017" },
      { _id: 2, host: "192.168.158.130:27017" }
   ]
})
#查看副本集的状态
rs.status()

#在主节点上创建用户,使用db.createUser()方法添加用户,用户在管理数据库上应该至少拥有userAdminAnyDatabase角色
use admin
db.createUser(
  {
    user: "admin",
    pwd: "admin123",
    roles: [
      { role: "root", db: "admin" }
    ]
  }
)
#退出重新登录主库
mongosh -u admin -p admin123 --authenticationDatabase admin  mongodb://192.168.158.128:27017/
mongodb-rs [direct: primary] test> show dbs;
admin   172.00 KiB
config  116.00 KiB
local   404.00 KiB
mongodb-rs [direct: primary] test> use unit
switched to db unit
mongodb-rs [direct: primary] unit> db.teacher.insertOne({name: "Xiaoming",age:50})
{
  acknowledged: true,
  insertedId: ObjectId('6597d1b0687bd5fd7aa3707a')
}
mongodb-rs [direct: primary] unit> db.teacher.find()
[
  {
    _id: ObjectId('6597d1b0687bd5fd7aa3707a'),
    name: 'Xiaoming',
    age: 50
  }
]

#登录从库,从库开启读
mongosh -u admin -p admin123 --authenticationDatabase admin  mongodb://192.168.158.129:27017/
db.getMongo().setReadPref({"mode":"secondaryPreferred"})	#开启从库读功能

mongosh -u admin -p admin123 --authenticationDatabase admin  mongodb://192.168.158.130:27017/
db.getMongo().setReadPref({"mode":"secondaryPreferred"})	#开启从库读功能

#至此,3节点的副本已经部署完成,并且启用了账号密码访问认证功能

mongodump工具-备份mongodb

官方文档:https://www.mongodb.com/docs/database-tools/mongodump/#versioning

#查看命令帮助
mongodump  --help

#1、单机备份
#创建备份文件存放目录
mkdir /data/mongodb-backup -p
#格式mongodump <options> <connection-string>,其中<connection-string>可以使用下面3种:
mongodump --uri="mongodb://mongodb0.example.com:27017" [additional options]
mongodump --host="mongodb0.example.com:27017"  [additional options]
mongodump --host="mongodb0.example.com" --port=27017 [additional options]
#默认会备份全部有文档的库.--gzip表示压缩
mongodump -u "admin" -p "admin123" --authenticationDatabase admin --uri=mongodb://192.168.158.130:27017/ -o /data/mongodb-backup/$(date "+%Y%m%d%H%M%S") --gzip
或者
mongodump --host=192.168.158.130  --port=27017 -u "admin" -p "admin123" --authenticationDatabase admin  -o /data/mongodb-backup/$(date "+%Y%m%d%H%M%S")  
或者
mongodump --host=192.168.158.130:27017 -u "admin" -p "admin123" --authenticationDatabase admin  -o /data/mongodb-backup/$(date "+%Y%m%d%H%M%S")     
#-d是备份指定的库
mongodump -u "admin" -p "admin123" --authenticationDatabase admin --uri=mongodb://192.168.158.130:27017/ -d unit -o /data/mongodb-backup/$(date "+%Y%m%d%H%M%S")
#-d是备份指定的库,-c指定备份的集合,好像只能指定单个集合
mongodump -u "admin" -p "admin123" --authenticationDatabase admin --uri=mongodb://192.168.158.130:27017/ -d unit -c teachers -o /data/mongodb-backup/$(date "+%Y%m%d%H%M%S")
#2、副本集备份
#格式
mongodump --uri="mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017,mongodb2.example.com:27017/?replicaSet=myReplicaSetName" [additional options]
mongodump --host="myReplicaSetName/mongodb0.example.com:27017,mongodb1.example.com:27017,mongodb2.example.com" [additional options]
默认情况下,mongodb从副本集的主节点读取数据进行备份,当然你也可以指定读优先级来覆盖此默认情况:
mongodump --uri="mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017,mongodb2.example.com:27017/?replicaSet=myReplicaSetName&readPreference=secondary" [additional options]
mongodump --uri="mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017,mongodb2.example.com:27017/?replicaSet=myReplicaSetName&readPreference=secondary&readPreferenceTags=region:east" [additional options]
mongodump --host="myReplicaSetName/mongodb0.example.com:27017,mongodb1.example.com:27017,mongodb2.example.com:27017" --readPreference=secondary [additional options]
mongodump --host="myReplicaSetName/mongodb0.example.com:27017,mongodb1.example.com:27017,mongodb2.example.com:27017" --readPreference='{mode: "secondary", tagSets: [ { "region": "east" } ]}' [additional options]
#副本集中每个节点都可以设置节点标签,设置读取优先级时可根据节点标签来指定优先级,关于如何设置节点标签,可参考官网相关内容。

#备份例子,其中mongodb-rs是副本集的名称
mkdir /data/mongodb-backup -p
mongodump --host="mongodb-rs/192.168.158.128:27017,192.168.158.129:27017,192.168.158.130:27017" -u admin -p admin123 --authenticationDatabase admin   -o /data/mongodb-backup/$(date "+%Y%m%d%H%M%S")

# 分片集群的备份
#链接的mongos的IP和端口
mongodump --uri="mongodb://mongos0.example.com:27017" [additional options]
mongodump --host="mongos0.example.com:27017" [additional options]
默认情况下,mongodb从shard副本集的主节点读取数据(sh.status()命令可以查看哪个是主分片)。要覆盖默认值,您可以指定读优先级:
mongodump --uri="mongodb://mongos0.example.com:27017/?readPreference=secondary" [additional options]
mongodump --uri="mongodb://mongos0.example.com:27017/?readPreference=secondary&readPreferenceTags=region:east" [additional options]
mongodump --host="mongos0.example.com:27017" --readPreference=secondary [additional options]
mongodump --host="mongos0.example.com:27017" --readPreference='{mode: "secondary", tagSets: [ { "region": "east" } ]}' [additional options]

#备份示例,链接的是mongos的IP端口
mkdir /data/mongodb-backup -p
mongodump --host="10.100.60.150:27017" --authenticationDatabase admin -u root -p root123   -o /data/mongodb-backup/$(date "+%Y%m%d%H%M%S")
mongodump --host="10.100.60.150:27017" --authenticationDatabase admin -u root -p root123 --readPreference=secondary  -o /data/mongodb-backup/$(date "+%Y%m%d%H%M%S")
#查备份的数据
cd /data/mongodb-backup/20240106141006
[root@mongodb-1 20240106141006]# ll
total 0
drwxr-xr-x 2 root root 128 Jan  6 14:10 admin
drwxr-xr-x 2 root root  55 Jan  6 14:10 unit
[root@mongodb-1 20240106141006]# cd unit/
[root@mongodb-1 unit]# ll		#
teacher.bson					#备份的数据是bson类型的,二进制形式的无法正常查看
teacher.metadata.json			#元数据.json类型
#使用mongodb-database-tools安装包的bsondump命令解析查看bson数据
bsondump --type=json --outFile=teacher.json --bsonFile=teacher.bson
cat teacher.json 
{"_id":{"$oid":"6597d1b0687bd5fd7aa3707a"},"name":"Xiaoming","age":{"$numberInt":"50"}}

分片集群的三大组件

mongodb的分片集群主要包含以下3大组件:

1、shard: Each shard contains a subset of the sharded data. As of MongoDB 3.6, shards must be deployed as a replica set.
     shard:每个shard包含一个分片数据的子集。从MongoDB 3.6开始,分片必须作为副本集部署。
2、mongos: The mongos acts as a query router, providing an interface between client applications and the sharded cluster. Starting in MongoDB 4.4, mongos can support hedged reads to minimize latencies.
    mongos: mongos作为查询路由器,提供客户端应用程序和分片集群之间的接口。从MongoDB 4.4开始,mongos可以支持对冲读取以最小化延迟。
3、config servers: Config servers store metadata and configuration settings for the cluster. As of MongoDB 3.4, config servers must be deployed as a replica set (CSRS).
    配置服务器:配置服务器存储集群的元数据和配置设置。从MongoDB 3.4开始,配置服务器必须部署为副本集(CSRS)。

简单总结就是,分片集群包含:shard(分片)、mongos(路由器)、config servers(配置服务器),其中,分片部署为副本集,即最少个3个节点的副本集,mongos可以有1个或多个实例,config servers也要部署为3节点的副本集。

分片集群的架构

在生产集群中,确保数据是冗余的,系统是高可用的。对于生产分片集群部署,请考虑以下几点:
将Config Servers部署为3个成员的副本集:
将每个分片部署为3个成员的副本集,分片至少需要两个分片来分发分片后的数据;
部署一个或多个mongos路由器
图-分片集群的架构
在这里插入图片描述
测试开发可以使用单个实例:
在这里插入图片描述

部署mongodb分片集群

https://www.mongodb.com/docs/manual/tutorial/deploy-shard-cluster/
按照上面的图-分片集群的架构,如果需要部署一个包含1个mongos、3个shard、1个config server的集群,那么最少需要13台服务器,这里使用3台服务器来搭建这样一个伪集群。

#参考前面的《Replica Set 副本集模式》中的《使用yum部署mongodb副本集并开启auth》章节以及《linux安装mongodb》
#这里部署的分片集群仅给出基本的配置过程且不开启auth
#我们通过mongod --help命令可以看出,通过指定对应的参数就可以表示该mongodb是什么角色,以及默认的shard、config server端口
Sharding options:
  --configsvr                           Declare this is a config db of a 
                                        cluster; default port 27019; default 
                                        dir /data/configdb
  --shardsvr                            Declare this is a shard db of a 
                                        cluster; default port 27018

#准备3台干净服务器并写入主机名
#从主机名也可以看的出角色分配,即shard-1{1..3}为一组副本集,其他同理
cat >> /etc/hosts<<'EOF'
192.168.158.128 shard-11 shard-21 shard-31 config-server-11 mongodb-1 mongos 
192.168.158.129 shard-12 shard-22 shard-32 config-server-12 mongodb-2
192.168.158.130 shard-13 shard-23 shard-33 config-server-12 mongodb-3
EOF
#安装mongodb
sudo yum install -y mongodb-org
#先复制配置文件,3台服务器都这样复制
cp /etc/mongod.conf /etc/mongod-shard-1.conf 
cp /etc/mongod.conf /etc/mongod-shard-2.conf 
cp /etc/mongod.conf /etc/mongod-shard-3.conf
cp /etc/mongod.conf /etc/mongod-config-server.conf

#创建Config Server副本集
#编辑配置服务器的配置文件
vim /etc/mongod-config-server.conf
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb-config-server/mongod-config-server.log	#指定了日志存放路径
storage:
  dbPath: /var/lib/mongo-config-server							#指定数据存放目录
processManagement:
  timeZoneInfo: /usr/share/zoneinfo
net:
  port: 27019													#端口默认是27019
  bindIp: 127.0.0.1,192.168.158.128,config-server-11,localhost	#指定绑定的IP
replication:
  replSetName: config-server									#副本集的名称
sharding:
  clusterRole: configsvr										#指定集群角色是配置服务器

#3台服务器都创建目录并授权
mkdir /var/lib/mongo-config-server  /var/log/mongodb-config-server/ ; 
chown  mongod.mongod -R /var/lib/mongo-config-server  /var/log/mongodb-config-server/
#将配置服务器配置文件复制到其他两台主机上
scp /etc/mongod-config-server.conf  root@mongodb-2:/etc/
scp /etc/mongod-config-server.conf  root@mongodb-3:/etc/
#注意修改其他两台服务器上的配置文件,改成对应的本机的IP和主机名
vim mongod-config-server.conf
#修改service服务文件,使用yum安装的mongodb默认会自动创建了service服务文件,但是要修改一下其中的配置文件
cp /usr/lib/systemd/system/mongod.service /usr/lib/systemd/system/mongod-config-server.service
vim /usr/lib/systemd/system/mongod-config-server.service
grep Environment  /usr/lib/systemd/system/mongod-config-server.service
Environment="OPTIONS=-f /etc/mongod-config-server.conf"		#改这一行即可,指定配置文件 
scp /usr/lib/systemd/system/mongod-config-server.service root@mongodb-2:/usr/lib/systemd/system/
scp /usr/lib/systemd/system/mongod-config-server.service root@mongodb-3:/usr/lib/systemd/system/
#启动,3台都启动
systemctl  daemon-reload 
systemctl start mongod-config-server.service
lsof  -i:27019
#登录其中一台mongodb初始化集群
mongosh mongodb://192.168.158.128:27019/
rs.initiate( {
   _id : "config-server",
   configsvr: true,			#指定是配置服务器
   members: [
      { _id: 0, host: "config-server-11:27019" },
      { _id: 1, host: "config-server-12:27019" },
      { _id: 2, host: "config-server-13:27019" }
   ]
})
#查看副本集的状态
rs.status()
#自此,config-server 3节点搭建完成

#创建Shard Replica Sets,先创建分片1
vim /etc/mongod-shard-1.conf
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongo-shard-1/mongo-shard-1.log
storage:
  dbPath: /var/lib/mongo-shard-1
processManagement:
  timeZoneInfo: /usr/share/zoneinfo
net:
  port: 27018					#端口,分片服务器默认端口是27018
  bindIp: 127.0.0.1,192.168.158.128,shard-11,localhost	#绑定的IP地址
replication:
  replSetName: shard-1			#分片名称
sharding:
  clusterRole: shardsvr			#定义是分片服务器

mkdir /var/lib/mongo-shard-1  /var/log/mongo-shard-1/ ; 
chown  mongod.mongod -R /var/lib/mongo-shard-1  /var/log/mongo-shard-1/ ; 
cp /usr/lib/systemd/system/mongod.service /usr/lib/systemd/system/mongod-shard-1.service
vim /usr/lib/systemd/system/mongod-shard-1.service
Environment="OPTIONS=-f /etc/mongod-shard-1.conf"
systemctl  daemon-reload
systemctl  start mongod-shard-1.service 
scp /etc/mongod-shard-1.conf root@mongodb-2:/etc/
vim /etc/mongod-shard-1.conf	#注意修改其中的IP地址和主机名为本机IP和主机名
scp /etc/mongod-shard-1.conf root@mongodb-2:/etc/
vim /etc/mongod-shard-1.conf	#注意修改其中的IP地址和主机名为本机IP和主机名
scp /usr/lib/systemd/system/mongod-shard-1.service root@mongodb-2:/usr/lib/systemd/system/mongod-shard-1.service
scp /usr/lib/systemd/system/mongod-shard-1.service root@mongodb-3:/usr/lib/systemd/system/mongod-shard-1.service
systemctl  daemon-reload
systemctl  start mongod-shard-1.service 	#启动两台服务器

mongosh  mongodb://192.168.158.128:27018
rs.initiate(
  {
    _id : "shard-1",
    members: [
      { _id : 0, host : "shard-11:27018" },
      { _id : 1, host : "shard-12:27018" },
      { _id : 2, host : "shard-13:27018" }
    ]
  }
)
rs.status()

#同理,创建分片2和3,这里不在演示,注意由于我的是都在相同服务器上,所以创建分片2和3的端口得改一下即可。
最后,服务器上的端口情况如下:
[root@mongodb-1 ~]# netstat -lntup| grep mon | grep '192.168.158.128'
tcp        0      0 192.168.158.128:27018   0.0.0.0:*               LISTEN      128167/mongod    	#shard-1    
tcp        0      0 192.168.158.128:27019   0.0.0.0:*               LISTEN      117996/mongod    	#config-server   
tcp        0      0 192.168.158.128:27028   0.0.0.0:*               LISTEN      22133/mongod   		#shard-2     
tcp        0      0 192.168.158.128:27038   0.0.0.0:*               LISTEN      30355/mongod     	#shard-2    
[root@mongodb-1 ~]# 至此,1个config-server和3个shard已经搭建完成。

#创建mongos
mongos --help
cp /etc/mongod.conf  /etc/mongos.conf
mkdir -p  /var/log/mongos/ ; chown mongod.mongod -R /var/log/mongos/
vim /etc/mongos.conf
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongos/mongos.log
processManagement:
  timeZoneInfo: /usr/share/zoneinfo
net:
  port: 27017	#端口
  bindIp: 127.0.0.1,192.168.158.128,mongos,localhost
sharding:		#格式 分片服务器的副本集的名称/分片服务器节点1,分片服务器节点1,分片服务器节点1
  configDB: config-server/config-server-11:27019,config-server-12:27019,config-server-13:27019
  
cat >> /usr/lib/systemd/system/mongos.service<<'EOF'
[Unit]
Description=MongoDB Mongos Service
After=network.target

[Service]
User=mongod
Group=mongod
ExecStart=/usr/bin/mongos --config /etc/mongos.conf
Restart=always
RestartSec=3
KillMode=process
TimeoutStopSec=10

[Install]
WantedBy=multi-user.target

EOF
systemctl daemon-reload
systemctl  start mongos.service
lsof -i:27017
#至此,mongos.service部署完成

#mongosh连接分片集群,这里连接的是mongos的主机(或IP)端口
mongosh --host mongos --port 27017
#为集群添加分片,使用addShard方法
#前面mongos配置文件我们只是配置了mongos所连接的config-server,还没有为mongos添加分片,现在为集群添加前面我们部署好的3个分片
#格式 sh.addShard( "<replSetName>/s1-mongo1.example.net:27018,s1-mongo2.example.net:27018,s1-mongo3.example.net:27018")
sh.addShard( "shard-1/shard-11:27018,shard-12:27018,shard-13:27018")
sh.addShard( "shard-2/shard-21:27028,shard-22:27028,shard-23:27028")
sh.addShard( "shard-3/shard-31:27038,shard-32:27038,shard-33:27038")
#查看分片集群状态
sh.status()
#创建unit库
test> use unit;			#切换数据库,如果库不存在并且存在往库里面创建集合等操作则会创建库
#开启库的分片功能
sh.enableSharding("unit")
#创建集合
db.createCollection("teacher")
#分片集合初始化,表示对集合的哪个字段执行哪种分片策略,这里是id字段执行哈希分片
#格式sh.shardCollection("<database>.<collection>", { <shard key field> : "hashed" } )
sh.shardCollection("unit.teacher",{_id: "hashed"})
#插入数据测试是否分片正常
db.teacher.insertMany([
   { item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
   { item: "mat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
   { item: "mousepad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } },
   { item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
   { item: "yat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
   { item: "gtyrnal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
   { item: "drf", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
   { item: "werrnal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
   { item: "ljkh", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
   { item: "hat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
])
#查看数据分布,文档已经均衡的分布在shard1、shard2、shard3上了
db.teacher.getShardDistribution()

#至此,分片集群已经搭建完成

k8s搭建mongodb分片集群

官方给的k8s安装mongodb是使用operator安装的,而且是企业版不是社区版。这里使用bitnami仓库的chart包来部署mongodb分片集群。
首先有一个完整的k8s集群且有默认存储类。默认已经搭建完成。

#添加常用的helm仓库
helm repo add mongodb https://mongodb.github.io/helm-charts		#这个是mongodb官方的helm仓库
helm repo add  bitnami  https://charts.bitnami.com/bitnami                    
helm repo add stable http://mirror.azure.cn/kubernetes/charts              
helm repo add aliyun  https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
helm repo update
[root@master mongodb-sharded]# helm  search repo mongodb | grep -vi DEPRECATED
NAME                               	CHART VERSION	APP VERSION	DESCRIPTION                                       
aliyun/mongodb                     	0.4.27       	3.7.1      	NoSQL document-oriented database that stores JS...
aliyun/mongodb-replicaset          	2.3.1        	3.6        	NoSQL document-oriented database that stores JS...
bitnami/mongodb                    	14.4.10      	7.0.5      	MongoDB(R) is a relational open source NoSQL da...
bitnami/mongodb-sharded            	7.2.1        	7.0.5      	MongoDB(R) is an open source NoSQL database tha...
mongodb/mongodb-atlas-operator     	2.0.1        	2.0.1      	MongoDB Atlas Operator - a Helm chart for insta...
mongodb/mongodb-atlas-operator-crds	2.0.1        	2.0.1      	MongoDB Atlas Operator CRDs - Helm chart for in...
mongodb/atlas-cluster              	0.1.9        	0.8.1      	A Helm chart to manage Atlas resources with Atl...
mongodb/atlas-deployment           	1.0.1        	1.0.0      	A Helm chart to manage Atlas resources with Atl...
mongodb/community-operator         	0.9.0        	0.9.0      	MongoDB Kubernetes Community Operator             
mongodb/community-operator-crds    	0.9.0        	0.9.0      	MongoDB Kubernetes Community Operator - CRDs      
mongodb/enterprise-database        	1.13.0       	           	MongoDB Kubernetes Enterprise Database.           
mongodb/enterprise-operator        	1.24.0       	           	MongoDB Kubernetes Enterprise Operator            
mongodb/sample-app                 	0.1.0        	0.1.0      	A sample application to install in your Kuberne...
#我们下载bitnami的bitnami/mongodb-sharded chart包,这里不使用mongodb官方的operator
helm pull bitnami/mongodb-sharded
tar xf mongodb-sharded-7.2.1.tgz
cd mongodb-sharded
#修改配置文件,下面讲解主要修改的参数,其他参数保持默认即可
vim values.yaml
global.storageClass: "nfs-storageclass"			#指定存储类
auth.enabled: true								#开启认证,默认开启,保持默认即可
auth.rootUser: root								#mongodb的超级管理员root用户
rootPassword: "root123"							#指定root用户密码,需自定义
shards: 3										#自定义分片数量,默认是2,建议最少是两个,因为两个shard才存在分片的意义
common.containerPorts.mongodb: 27017			#容器里mongodb端口,不管是config server还是shard,容器里都是这个端口
configsvr.replicaCount: 3						#config server的pod副本数,默认只有1个config server.
mongos.replicaCount: 1							#mongos的数量,可以定义多个,默认1个,默认mongos不需要持久化数据
shardsvr.dataNode.replicaCount: 3				#shard的节点数,即3个pod组成一个shard分片副本集
#从templates目录下的config-server、shard、mongos目录下的模板我们可以得知,
#默认只会创建一个config server,mongos的数量也是默认1个,shard分片的数量可以在values.yaml定义,每个shard分片包含的pod节点数也可在values.yaml定义,在上面的定义中,我们最终创建的mongodb分片集群将会是包含:1个mongos,1个config server,config server由3个pod组成副本集,3个shard分片,每个shard分片有3个pod组成副本集.

#安装mongodb分片集群
helm  install mongodb . -n default
#查看pod,1个mongos,1个config server,config server由3个pod组成副本集,3个shard分片,每个shard分片有3个pod组成副本集
[root@master ~]# kubectl  get pod 
NAME                                              READY   STATUS    RESTARTS        AGE
mongodb-mongodb-sharded-configsvr-0               1/1     Running   0               13m		#config server的第1个pod节点
mongodb-mongodb-sharded-configsvr-1               1/1     Running   0   			11m		#config server的第2个pod节点
mongodb-mongodb-sharded-configsvr-2               1/1     Running   0               10m		#config server的第3个pod节点
mongodb-mongodb-sharded-mongos-7b958d479f-jz6hd   1/1     Running   0               13m		#mongos
mongodb-mongodb-sharded-shard0-data-0             1/1     Running   0			    13m		#shard0的第1个pod节点
mongodb-mongodb-sharded-shard0-data-1             1/1     Running   0               6m1s	#shard0的第2个pod节点
mongodb-mongodb-sharded-shard0-data-2             1/1     Running   0               2m15s	#shard0的第3个pod节点
mongodb-mongodb-sharded-shard1-data-0             1/1     Running   0			    13m		#shard1的第1个pod节点
mongodb-mongodb-sharded-shard1-data-1             1/1     Running   0				12m		#shard1的第2个pod节点
mongodb-mongodb-sharded-shard1-data-2             1/1     Running   0    			11m		#shard1的第3个pod节点
mongodb-mongodb-sharded-shard2-data-0             1/1     Running   0               13m		#shard2的第1个pod节点
mongodb-mongodb-sharded-shard2-data-1             1/1     Running   0               10m		#shard2的第2个pod节点
mongodb-mongodb-sharded-shard2-data-2             1/1     Running   0               6m21s	#shard2的第3个pod节点
[root@master ~]# kubectl  get sts
NAME                                  READY   AGE
mongodb-mongodb-sharded-configsvr     3/3     22m
mongodb-mongodb-sharded-shard0-data   3/3     22m
mongodb-mongodb-sharded-shard1-data   3/3     22m
mongodb-mongodb-sharded-shard2-data   3/3     22m
# mongodb创建了两个service
# mongodb-mongodb-sharded的标签选择器是对应于mongos的,所以该svc用于内部其他应用链接mongodb
# mongodb-mongodb-sharded-headless是config server和shard内部的pod组成副本集使用的
[root@master ~]# kubectl  get svc
NAME                               TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
mongodb-mongodb-sharded            ClusterIP   10.100.60.150    <none>        27017/TCP        21m
mongodb-mongodb-sharded-headless   ClusterIP   None             <none>        27017/TCP        21m
[root@master ~]# kubectl  get sts -oyaml| grep serviceName		#可以看到config server和shard都使用了相同的无头服务
    serviceName: mongodb-mongodb-sharded-headless
    serviceName: mongodb-mongodb-sharded-headless
    serviceName: mongodb-mongodb-sharded-headless
    serviceName: mongodb-mongodb-sharded-headless
[root@master ~]# 

#登录config server的第1个pod节点
kubectl  exec -it mongodb-mongodb-sharded-configsvr-0 -- bash
mongosh mongodb://127.0.0.1:27017 --authenticationDatabase admin -u root -p root123
rs.status()		#查看是3个members表示正常
#登录shard0的第1个pod节点
kubectl  exec -it  mongodb-mongodb-sharded-shard0-data-0 -- bash
mongosh mongodb://127.0.0.1:27017 --authenticationDatabase admin -u root -p root123
rs.status()		#查看是3个members表示正常
#登录shard1的第1个pod节点
kubectl  exec -it  mongodb-mongodb-sharded-shard1-data-0 -- bash
mongosh mongodb://127.0.0.1:27017 --authenticationDatabase admin -u root -p root123
rs.status()		#查看是3个members表示正常
#登录shard2的第1个pod节点
kubectl  exec -it  mongodb-mongodb-sharded-shard2-data-0 -- bash
mongosh mongodb://127.0.0.1:27017 --authenticationDatabase admin -u root -p root123
rs.status()		#查看是3个members表示正常

#插入文档,链接的mongodb-mongodb-sharded,该svc对应的pod是mongos
[root@mongodb-1 ~]# kubectl  get svc mongodb-mongodb-sharded
NAME                      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)     AGE
mongodb-mongodb-sharded   ClusterIP   10.100.60.150   <none>        27017/TCP   43m
#链接到分片集群
mongosh mongodb://10.100.60.150:27017 --authenticationDatabase admin -u root -p root123
#查看分片集群状态
sh.status()
#创建/切换unit库
use unit;
#开启库的分片功能
sh.enableSharding("unit")
#创建集合
db.createCollection("teacher")
#分片集合初始化,表示对集合的哪个字段执行哪种分片策略,这里是id字段执行哈希分片
#格式sh.shardCollection("<database>.<collection>", { <shard key field> : "hashed" } )
sh.shardCollection("unit.teacher",{_id: "hashed"})
#插入数据测试是否分片正常
db.teacher.insertMany([
   { item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
   { item: "mat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
   { item: "mousepad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } },
   { item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
   { item: "yat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
   { item: "gtyrnal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
   { item: "drf", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
   { item: "werrnal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
   { item: "ljkh", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
   { item: "hat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
])
#查看数据分布,文档已经均衡的分布在shard1、shard2、shard3上了
db.teacher.getShardDistribution()

#至此,k8s搭建的mongodb分片集群已经搭建完成

分片集群-总结

分片集群其实就是把文档均分在每个shard上,而每个shard又是由3个副本集构成,副本集直接相互同步数据。
这样,当你单独登录某个shard查询文档,此时查看到的文档肯定是不全的,仅仅是部分文档,主要登录mongos时查询的文档才是最全的,所以分片集群都是使用mongos的IP和端口进行登录链接mongodb。
在使用mongodump命令备份分片集群时,链接的是mongos的IP端口,所以备份的肯定是全部的文档,不在乎你是从主还是secondary shard读取的数据。

  • 20
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值