工作中的项目用到 MongoDB 的地方还是不少的,只是前几个月都比较忙碌,一直没腾出时间好好梳理一下。这两周稍微轻松些了,就决定把 MongoDB 这块知识过一遍,也方便以后参考(实在讨厌各大博客网站的转载机制,劣质无用的文章被转载的到处都是,真正遇到问题时,想要找到一篇具有参考性的文章实在太难)。由于工作中暂无 MongoDB 集群的使用场景,因此本文暂未整理分片集群这块的知识(标记为 TODO
,或许日后会补充)。
# NoSQL 简介
NoSQL,指的是非关系型的数据库。NoSQL 是 Not Only SQL 的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称。NoSQL 通常用于超大规模数据的存储(如谷歌或 Facebook 每天为他们的用户收集万亿比特的数据),这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展。
# NoSQL 优缺点
优点:
高可扩展性
分布式计算
低成本
架构的灵活性,半结构化数据
没有复杂的关系
缺点:
没有标准化
有限的查询功能(到目前为止)
最终一致是不直观的程序
# RDBMS vs NoSQL
关系型数据库(RDBMS)与非关系型数据库(NoSQL)对比:
对比项 | RDBMS | NoSQL |
---|---|---|
存储格式 | 表格式,行和列 | 文档、键值对、图结构 |
存储规范 | 规范性,避免重复 | 鼓励冗余 |
存储扩展 | 纵向扩展 (横向扩展有限) | 横向扩展,分布式 |
查询方式 | 结构化查询 | 非结构化查询语言 SQL |
事务 | 支持事务 | 不支持事务一致性 |
性能 | 读写性能差 | 读写性能高 |
成本 | 成本高 | 简单易部署,开源,成本低 |
存储方式 | 数据主要存储在磁盘中 | 数据主要存储在内存中(部分可以持久化到磁盘) |
建表原则 | 依靠关系模型构建关联 | 数据模型比较简单,用 K/V 的形式来存储数据 |
# MongoDB 简介
MongoDB 是由 C++
语言编写的,是一个基于分布式文件存储的开源非关系型数据库系统。在高负载的情况下,添加更多的节点,可以保证服务器性能。MongoDB 旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
MongoDB 使用 BSON(Binary JSON)对象来存储,与 JSON 格式的键值对(key/value)类似,字段值可以包含其他文档,数组及文档数组。支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系型数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
# MongoDB 主要特点
MongoDB 是一个面向文档存储的数据库,操作起来比较简单和容易。
你可以在 MongoDB 记录中设置任何属性的索引 (如:FirstName="Sameer",Address="8 Gandhi Road")来实现更快的排序。
你可以通过本地或者网络创建数据镜像,这使得 MongoDB 有更强的扩展性。
如果负载的增加(需要更多的存储空间和更强的处理能力) ,它可以分布在计算机网络中的其他节点上这就是所谓的分片。
Mongo 支持丰富的查询表达式。查询指令使用 JSON 形式的标记,可轻易查询文档中内嵌的对象及数组。
MongoDB 使用 update () 命令可以实现替换完成的文档(数据)或者一些指定的数据字段 。
MongoDB 中的 Map/reduce 主要是用来对数据进行批量处理和聚合操作。
Map 和 Reduce。Map 函数调用 emit (key,value) 遍历集合中所有的记录,将 key 与 value 传给 Reduce 函数进行处理。
Map 函数和 Reduce 函数是使用 Javascript 编写的,并可以通过 db.runCommand 或 mapreduce 命令来执行 MapReduce 操作。
GridFS 是 MongoDB 中的一个内置功能,可以用于存放大量小文件。
MongoDB 允许在服务端执行脚本,可以用 Javascript 编写某个函数,直接在服务端执行,也可以把函数的定义存储在服务端,下次直接调用即可。
MongoDB 支持各种编程语言:RUBY,PYTHON,JAVA,C++,PHP,C# 等多种语言。
MongoDB 安装简单。
# MongoDB 数据类型
数据类型 | 描述 |
---|---|
String | 字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。 |
Integer | 整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位。 |
Boolean | 布尔值。用于存储布尔值(真 / 假)。 |
Double | 双精度浮点值。用于存储浮点值。 |
Min/Max keys | 将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比。 |
Arrays | 用于将数组或列表或多个值存储为一个键。 |
Timestamp | 时间戳。记录文档修改或添加的具体时间。 |
Object | 用于内嵌文档。 |
Null | 用于创建空值。 |
Symbol | 符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言。 |
Date | 日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息。 |
Object ID | 对象 ID。用于创建文档的 ID。 |
Binary Data | 二进制数据。用于存储二进制数据。 |
Code | 代码类型。用于在文档中存储 JavaScript 代码。 |
Regular expression | 正则表达式类型。用于存储正则表达式。 |
# MongoDB 支持语言
# 下载与安装
# Windows 环境
官网地址:https://www.mongodb.com
下载地址:https://www.mongodb.com/try/download
双击可执行文件 .msi
,进行安装(基本上下一步即可)。
推荐选择自定义安装:
推荐取消默认的 Install MongoDB Compass
安装。
MongoDB 配置:
环境变量
编辑系统变量
Path
并添加 MongoDB 的 bin 目录位置。例如:A:\MongoDB\Server\5.0\bin
创建数据库文件的存放位置
创建文件夹
[MongoDB]\data\db
。这是因为 MongoDB 服务在启动之前必须创建数据库文件的存放文件夹,否则命令不会自动创建,且服务无法启动成功。
启动 MongoDB 服务
cmd
命令进入安装目录[MongoDB]\data\bin
位置,执行如下命令:A:\MongoDB\Server\5.0\bin> mongod --dbpath A:\MongoDB\Server\5.0\data\db
浏览器访问:http://127.0.0.1:27017,如果出现如下信息,则表示配置成功且服务已启动:
It looks like you are trying to access MongoDB over HTTP on the native driver port.
配置本地 MongoDB 服务
创建文件夹
[MongoDB]\data\log
,用于存放日志文件。创建配置文件
[MongoDB]\mongo.config
:dbpath=A:\MongoDB\Server\5.0\data\db
logpath=A:\MongoDB\Server\5.0\data\log\mongo.log
使用管理员角色执行如下命令:
# 停止已启动的 MongoDB 服务
net stop MongoDB
# 移除 MongoDB 服务
sc delete MongoDB
# 安装 MongoDB 服务
mongod -dbpath "A:\MongoDB\Server\5.0\data\db" -logpath "A:\MongoDB\Server\5.0\data\log\mongo.log" -install -serviceName "MongoDB"
# 启动 MongoDB 服务
net start MongoDB
浏览器访问:http://127.0.0.1:27017
客户端连接:
A:\MongoDB\Server\5.0\bin> mongo.exe
# Linux 环境
卸载删除
# 停止服务
ps -ef | grep mongo
kill -9 xxx
net stop mongodb
sudo yum erase $(rpm -qa | grep mongodb-org) #卸载 MongoDB
sudo rm -r /var/log/mongodb #删除日志文件
sudo rm -r /var/lib/mongo #删除数据文件
安装
配置 yum 源:
cd /etc/yum.repos.d
vi mongodb-org-5.0.repo
# 版本选择可前往 https://repo.mongodb.org 进行查看。
编辑如下内容(版本号视个人需要而定):
[mongodb-org-5.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/7Server/mongodb-org/5.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-5.0.asc
提示:具体版本,可前往源地址进行查看选择:https://repo.mongodb.org
执行安装:
yum install -y mongodb-org
修改配置:
vim /etc/mongod.conf
# network interfaces
net:
port: 27017
bindIp: 0.0.0.0 # Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.
基本命令:
systemctl start mongod.service # 启动
systemctl status mongod.service # 查看状态
systemctl stop mongod.service # 停止
systemctl enable mongod.service # 自启
测试连接:
mongo 127.0.0.1:27017 # 如果是默认 IP 端口,可以不指定
权限控制:
use admin # 其他数据库也类似
switched to db admin
db.createUser({ user:"admin", pwd:"123456", roles:["root"] })
Successfully added user: { "user" : "admin", "roles" : [ "root" ] }
db.auth("admin", "123456")
1
exit
bye
mongo -u root -p 123456 # 账号登录
启用身份验证:
vi /etc/mongod.conf
security:
authorization: enabled # disable or enabled
问题排查
MongoDB 服务启动失败,可以使用
journalctl -xe
命令跟踪更加详细的信息,通常是由于以下几种原因导致的:mongod.conf 配置内容或语法错误。
日志访问权限不足。
锁定
如果服务器本地可以访问 MongoDB,但远程访问失败,请检查:
检查配置文件
/etc/mongod.conf
IP 是否放开、是否允许远程访问,以及访问账号密钥信息。云服务器需注意配置安全组规则。
如已开启防火墙,需添放开对应端口。
命令提示
# 查看与修改
vi /etc/mongod.conf # MongoDB 配置文件
cat /usr/lib/systemd/system/mongod.service # MongoDB 服务
# 权限或 mongodb 锁文件
sudo chown -Rc mongodb. /var/log/mongodb
sudo chown -Rc mongodb. /var/lib/mongo
rm -rf /var/lib/mongo/mongod.lock
rm -rf /tmp/mongodb-27017.sock
# 防火墙
firewall-cmd --state # 查看防火墙状态
systemctl stop firewalld.service # 关闭防火墙
systemctl start firewalld.service # 开启防火墙
firewall-cmd --add-port=27017/tcp --permanent # 防火墙放开指定端口(永久)
firewall-cmd --zone=public --list-ports # 查看防火墙已开放端口
firewall-cmd --reload # 防火墙配置重新载入
配置参考:https://docs.mongodb.com/manual/reference/configuration-options
# MongoDB GUI
Robo 3T / Studio 3T
Navicat
# 术语与概念
SQL 数据库与 MongoDB 数据库术语及概念的对比:
SQL 术语 / 概念 | MongoDB 术语 / 概念 | 解释 / 说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表 / 集合 |
row | document | 数据记录行 / 文档 |
column | field | 数据字段 / 域 |
index | index | 索引 |
table joins | 表连接,MongoDB 不支持 | |
primary key | primary key | 主键,MongoDB 自动将_id 字段设置为主键 |
# 数据库
一个 mongodb 中可以建立多个数据库。MongoDB 的默认数据库为 db
,该数据库存储在 data 目录中,这一点我们在安装配置时已有提及。
MongoDB 的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限,不同的数据库也放置在不同的文件中。
> show dbs # 显示所有数据列表 | |
admin 0.000GB | |
config 0.000GB | |
local 0.000GB | |
> db # 显示当前数据库对象或集合 | |
test | |
> use local # 切换到指定数据库 | |
switched to db local |
MongoDB 默认有三个数据库:
admin:从权限的角度来看,这是 root 数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
local:这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
config: 当 Mongo 用于分片设置时,config 数据库在内部使用,用于保存分片的相关信息。
# 文档
文档是一组键值对(即 BSON),示例:
{"site":"www.chinmoku.cc", "author":"Chinmoku"} |
RDBMS 与 MongoDB 术语对比:
RDBMS | MongoDB |
---|---|
数据库 | 数据库 |
表格 | 集合 |
行 | 文档 |
列 | 字段 |
表联合 | 嵌入文档 |
主键 | 主键(MongoDB 提供了 key 为 _id) |
注意:
文档中的键值对是有序的。
文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
MongoDB 区分类型和大小写。
MongoDB 的文档不能有重复的键。
文档的键是字符串,除少数例外情况,键可以使用任意 UTF-8 字符。
文档键命名规范:
键不能含有空字符。这个字符用来表示键的结尾。
.
和$
有特别的意义,只有在特定环境下才能使用。以下划线开头的键是保留的(不是严格要求的)。
# 集合
集合就是 MongoDB 文档组,类似于 RDBMS 中的表。集合存在于数据库中,集合没有固定的结构。
集合的合法性:
集合名不能是空字符串。
集合名不能含有
\0
空字符,这个字符表示集合名的结尾。集合名不能以
system.
开头,这是为系统集合保留的前缀。用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。
# Capped Collections
Capped collections 就是固定大小的 collection,它是高性能自动的维护对象的插入顺序。它非常适合类似记录日志的功能和标准的 collection 不同,你必须要显式的创建一个 capped collection,指定一个 collection 的大小,单位是字节。collection 的数据存储空间值提前分配的。
Capped collections 可以按照文档的插入顺序保存到集合中,而且这些文档在磁盘上存放位置也是按照插入顺序来保存的,所以当我们更新 Capped collections 中文档的时候,更新后的文档不可以超过之前文档的大小,这样话就可以确保所有文档在磁盘上的位置一直保持不变。
由于 Capped collection 是按照文档的插入顺序而不是使用索引确定插入位置,这样的话可以提高增添数据的效率。MongoDB 的操作日志文件 oplog.rs 就是利用 Capped Collection 来实现的。
要注意的是指定的存储大小包含了数据库的头信息。
db.createCollection("mycoll", {capped:true, size:100000}) |
# 元数据
在 MongoDB 数据库中命名空间 <dbname>.system.*
是包含多种系统信息的特殊集合(Collection),如下:
集合命名空间 | 描述 |
---|---|
dbname.system.namespaces | 列出所有名字空间。 |
dbname.system.indexes | 列出所有索引。 |
dbname.system.profile | 包含数据库概要(profile)信息。 |
dbname.system.users | 列出所有可访问数据库的用户。 |
dbname.local.sources | 包含复制对端(slave)的服务器信息和状态。 |
# ObjectId
ObjectId 类似唯一主键,可以很快的去生成和排序,它包含 12 个字节,其含义如下:
前 4 个字节表示创建 unix 时间戳,格林尼治时间 UTC 时间,比北京时间晚了 8 个小时。
接下来的 3 个字节是机器标识码。
紧接的两个字节由进程 id 组成 PID。
最后三个字节是随机数。
使用方式:
var newObject = ObjectId() | |
newObject.getTimestamp() | |
newObject.str |
# 时间与字符串
BSON 字符串
BSON 字符串都是 UTF-8 编码。
时间戳
BSON 有一个特殊的时间戳类型用于 MongoDB 内部使用,与普通的日期类型不相关。
在单个 mongod 实例中,时间戳值通常是唯一的。
在复制集中,oplog 有一个 ts 字段。这个字段中的值使用 BSON 时间戳表示了操作时间。
BSON 时间戳类型主要用于 MongoDB 内部使用。
日期
表示当前距离 Unix 新纪元(1970 年 1 月 1 日)的毫秒数。日期类型是有符号的,负数表示 1970 年之前的日期,创建及使用方式如下:
var mydate = new Date()
typeof mydate
mydate.toString()
Date()
这样创建的时间是日期类型,可以使用 JS 中的 Date 类型的方法。
# 基本操作
# 数据库操作
创建连接
# 连接实例:
mongodb://root:123456@127.0.0.1/test
更多 MongoDB 连接配置,参考:https://www.runoob.com/mongodb/mongodb-connections.html
创建或切换数据库
# 如果不存在,则创建,如果已存在,则切换
use mytest
# 查看所有数据库(当创建的数据库没有数据时,不会显示)
show dbs
# 插入一条数据
db.col1.insert({"name":"Chinmoku"})
# 再次查询数据库列表
show dbs
删除数据库
use mytest
db.dropDatabase()
# 集合与文档操作
创建集合
# 创建集合
db.createCollection("test")
# 查看所有集合
show collections
# 查看所有集合(方式二)
show tables
# 创建固定集合
db.createCollection("col2",{capped:true,autoIndexId:true,size:6142800,max:10000}) # 集合大小:6142800B,最大文档个数:10000
在 MongoDB 中,其实不需要显式创建集合,在插入文档时,MongoDB 就会自动创建集合。
删除集合
# db.collection.drop (),返回布尔值
db.col2.drop()
文档基本操作
# 插入文档
db.col.insert({title: 'Telephone Book', user: 'Jason',mobile: '138****2573',site: 'https://www.jason.com',label: ['classmate', 'friend'],description: ''})
db.col.find() # 查看文档内容
# 也可以将数据定义为变量,然后插入该变量
jason_document=({title: 'Telephone Book', user: 'Jason',mobile: '138****2573',site: 'https://www.jason.com',label: ['classmate', 'friend'],description: ''})
db.col.insert(jason_document)
# v3.2 版本之后新增了 insertOne 和 insertMany 方法
db.col2.insertOne({"name": "Lily"})
db.col2.insertMany([{"name": "Charles"},{"name": "James"}])
save 方法也可以创建文档。
更新文档
# 参数(1. 筛选条件,2. 更新内容,3.***,4.***)
db.col.update({'title':'Telephone Book'},{$set:{'title':'Telephone Book: Jason'}}) # 查询标题为 Telephone Book 的数据,并更新标题
# 同时更新多条文档内容
db.col.update({'user':'Jason'},{$set:{'title':'Telephone Book: Jason'}},{multi:true})
# save(如果指定了 _id 字段,则更新对应文档,如果未指定,则新增文档)
db.col.save({"_id" : ObjectId("61c9ad7bba2a467651726dba"),title: 'Telephone Book: Bill', user: 'Bill',mobile: '187****0966',site: 'https://www.bill.com',label: ['classmate'],description: ''})
db.col.find() # 查询并格式化显示
使用 save 方法更新文档时,文档内所有字段都会被更新,即 save 中未指定字段会被移除,save 指定但数据库不存在字段会被新增。
删除文档
# 删除指定数据
db.col.remove({'user':'Jason'})
# 删除所有
db.col.remove({})
查询文档
# 查询所有
db.col.find()
# 查询并返回指定的键
db.col.find({},{"title":1,_id:0})
db.col.find()
# 查询并格式化显示
db.col.find().pretty()
# 查询一条
db.col.findOne()
比较查询:
操作 格式 示例 等于 {<key>:<value>}
db.col.find({"title":"John"}) 小于 {<key>:{$lt:<value>}}
db.col.find({"age":{$lt:18}}) 小于等于 {<key>:{$lte:<value>}}
db.col.find({"age":{$lte:18}}) 大于 {<key>:{$gt:<value>}}
db.col.find({"age":{$gt:18}}) 大于等于 {<key>:{$gte:<value>}}
db.col.find({"age":{$gte:18}}) 不等于 {<key>:{$ne:<value>}}
db.col.find({"age":{$ne:18}}) 这些比较查询语句,类似于关系型数据库中的
select * from col where age > 18;
。AND 和 OR:
# AND
db.col.find({key1:value1, key2:value2})
# OR
db.col.find({$or:[{key1: value1},{key2:value2}]})
# AND 和 OR 联合使用
db.col.find({"site": "https://www.jason.com", $or: [{"user": "Jason"},{"age": {$gt:18}}]})
# 进阶操作
# $type
使用示例:
# 查询 title 类型为 string 的数据 | |
db.col.find({"title" : {$type : 2}}) | |
# 等价于 | |
db.col.find({"title" : {$type : 'string'}}) |
# limit 和 skip
# 查询 2 条记录 | |
db.col.find({},{"user":1,_id:0}).limit(2) | |
# 跳过 1 条数,并查询其后续的 2 条记录 | |
db.col.find({},{"user":1,_id:0}).limit(2).skip(1) |
# 排序
# 排序使用 1 和 - 1 来分别表示正序和倒叙 | |
db.col.find({},{"user":1,_id:0}).sort({"age":-1}) |
# 索引
# 根据键名 user 按顺序创建索引,1 为升序,-1 为降序 | |
db.col.createIndex({"user":-1}) | |
# 复合索引 | |
db.col.createIndex({"title":1,"description":-1}) | |
# 查看集合索引 | |
db.col.getIndexes() | |
# 查看集合索引大小 | |
db.col.totalIndexSize() | |
# 删除所有集合索引 | |
db.col.dropIndexes() | |
# 删除集合指定索引 | |
db.col.dropIndex("myindex") |
创建索引还可以接受许多参数,如索引名称、是否唯一、设置过期时间等,详情可自行参考官方文档。
# 聚合
db.mypost.aggregate([{$group : {_id : "$author", post_count : {$sum : 1}}}]) | |
# 类似于如下 SQL | |
select author, count(*) as post_count from mypost group by author |
管道:
在此处的示例中,
$group
是 MongoDB 管道的一种,MongoDB 的聚合管道将 MongoDB 文档在一个管道处理完毕后将结果传递给下一个管道处理,并且管道操作是可以重复的。常用管道操作符:
$project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
match 使用 MongoDB 的标准查询操作。
$limit:用来限制 MongoDB 聚合管道返回的文档数。
$skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
$unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
$group:将集合中的文档分组,可用于统计结果。
$sort:将输入文档排序后输出。
$geoNear:输出接近某一地理位置的有序文档。
表达式:
此处示例中的
$sum
即是表示聚合操作的表达式。常用表达式:
$sum:计算总和。
$avg:计算平均值
$min:获取集合中所有文档对应值得最小值。
$max:获取集合中所有文档对应值得最大值。
$push:将值加入一个数组中,不会判断是否有重复的值。
$addToSet:将值加入一个数组中,会判断是否有重复的值,若相同的值在数组中已经存在了,则不加入。
$first:根据资源文档的排序获取第一个文档数据。
$last:根据资源文档的排序获取最后一个文档数据
# 复制(副本集)
MongoDB 复制是将数据同步在多个服务器的过程。复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。复制还允许您从硬件故障和服务中断中恢复数据。
MongoDB 复制原理:
MongoDB 复制至少需要两个节点,其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。
主节点记录在其上的所有操作 oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。
MongoDB 复制结构图如下所示:
副本集具有如下特征:
N 个节点的集群
任何节点可作为主节点
所有写入操作都在主节点上
自动故障转移
自动恢复
副本集设置:
# 启动一个名为 rs0 的 MongoDB 实例 | |
mongod --port 27017 --dbpath "A:\MongoDB\Server\5.0\data" --replSet rs0 | |
# 客户端连接并启动一个新的副本集 | |
rs.initiate() | |
# 查看副本集配置 | |
rs.conf() | |
# 查看副本集状态 | |
rs.status() |
添加副本集成员:
# 副本集添加成员 | |
rs.add("192.168.0.101:27017") | |
# 判断当前运行的 Mongo 服务是否为主节点 | |
db.isMaster() |
MongoDB 中你只能通过主节点将 Mongo 服务添加到副本集中,并且,MongoDB 的副本集与我们常见的主从有所不同,主从在主机宕机后所有服务将停止,而副本集在主机宕机后,副本会接管主节点成为主节点。
# 分片
MongoDB 中存在一种分片技术,它可以通过在多台机器上分割数据的方式,来使得数据库系统能存储和处理更多的数据或提高读写吞吐量。
分片结构集群部署图例:
组件说明:
Shard:用于存储实际的数据块,实际生产环境中一个 shard server 角色可由几台机器组个一个 replica set 承担,防止主机单点故障。
Config Servers:mongod 实例,存储了整个 ClusterMetadata,其中包括 chunk 信息。
Query Routers:前端路由,客户端由此接入,且让整个集群看上去像单一数据库,前端应用可以透明使用。
[❗TODO]{.label .danger} 未完待补充
# 备份与恢复
# 数据备份,语法:mongodump -h <hostname><:port> -d dbname -o <path> | |
# 不指定参数,则连接本地 27017 端口服务,默认备份到目录 [mongodb]/bin/dump/ 中 | |
mongodump | |
# 备份恢复,语法:mongorestore -h <hostname><:port> -d dbname <path> | |
# 不指定参数,则连接本地 27017 端口服务,默认从目录 [mongodb]/bin/dump/ 中获取备份文件 | |
mongorestore |
# 监控
mongostat
是 MongoDB 自带的状态检测工具,在命令行下使用。它会间隔固定时间获取 MongoDB 的当前运行状态,并输出。
使用方式:
mongostat # 无密码监控默认数据库 | |
mongostat -uroot -p123456 --authenticationDatabase=admin |
如提示 command serverStatus requires authentication,那么多半是因为 MongoDB 需要登录权限,详情可通过命令
mongostat --help
查看。
mongotop
也是 MongoDB 下的一个内置工具,mongotop 提供了一个方法,用来跟踪一个 MongoDB 的实例,查看哪些大量的时间花费在读取和写入数据。 mongotop 提供每个集合的水平的统计数据。默认情况下,mongotop 返回值的每一秒。
使用方式:
mongotop # 无密码监控默认数据库 | |
mongotop -uroot -p123456 --authenticationDatabase=admin | |
mongotop -uroot -p123456 --authenticationDatabase=admin --lock # 监控各个数据库锁使用情况 |
# API
# Java API
此处 MongoDB Java API 以 SpringBoot 框架作为示例。
准备工作:
使用 SpringInitializer 创建一个测试项目。
配置依赖包。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
启动文件配置:
spring:
data:
mongodb:
uri: "mongodb://localhost:27017/test"
# 如未设置则不需要指定用户名及密码
# username:
# password:
创建用于测试的实体类:
@Data
public class User {
//id 会被映射为 mongodb 文档的主键
private String id;
private String name;
private int age;
}
基础增删改查:
创建接口:
public interface UserService {
User insert(User user);
User save(User user);
User update(User user);
List<User> findAll();
boolean remove(User user);
}
实现类:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private MongoTemplate mongoTemplate;
@Override
public User insert(User user) {
return this.mongoTemplate.insert(user);
}
@Override
public User save(User user) {
return this.mongoTemplate.save(user);
}
@Override
public User update(User user) {
return this.mongoTemplate.save(user);
}
@Override
public List<User> findAll() {
return this.mongoTemplate.findAll(User.class);
}
@Override
public boolean remove(User user) {
DeleteResult result = this.mongoTemplate.remove(user);
return result.getDeletedCount() > 0;
}
}
提供 API 访问:
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/list")
public List<User> findAll() {
return this.userService.findAll();
}
@PostMapping("/insert")
public User insert(User user) {
return this.userService.insert(user);
}
@PostMapping("/save")
public User save(User user) {
return this.userService.save(user);
}
@PostMapping("/update")
public User update(User user) {
return this.userService.update(user);
}
@PostMapping("/remove")
public boolean remove(User user) {
return this.userService.remove(user);
}
}
测试发送请求:
### 添加用户
POST http://localhost:8080/insert?name=Lily&age=21
Accept: application/json
###
POST http://localhost:8080/insert?name=Mike&age=26
Accept: application/json
###
POST http://localhost:8080/insert?name=Jason&age=27
Accept: application/json
###
POST http://localhost:8080/insert?name=Philips&age=14
Accept: application/json
### 添加
POST http://localhost:8080/save?name=Jill&age=28
Accept: application/json
### 修改
POST http://localhost:8080/save?name=Jill&age=28&id=61caf27055120f6b62234917
Accept: application/json
### 修改
POST http://localhost:8080/update?name=Tom&age=33&id=61caf27055120f6b62234917
Accept: application/json
### 查询列表
GET http://localhost:8080/list
Accept: application/json
扩展查询:
// 单一条件查询示例 | |
@Override | |
public User queryById(String id) { | |
Criteria criteria = Criteria.where("id").is(id); | |
Query query = Query.query(criteria); | |
return this.mongoTemplate.findOne(query, User.class); // 返回一条 | |
} | |
// 符合条件查询示例 | |
@Override | |
public List<User> queryByCondition(int age, String name) { | |
Criteria criteria = new Criteria(); | |
criteria.orOperator(Criteria.where("age").gt(age), Criteria.where("name").is(name)); | |
Query query = Query.query(criteria); | |
// 排序 | |
Sort sort = Sort.by(new Sort.Order(Sort.Direction.DESC, "id")); | |
query.with(sort); | |
return this.mongoTemplate.find(query, User.class); // 返回多条 | |
} | |
// 分页查询 | |
@Override | |
public List<User> queryWithPage(int index, int limit) { | |
Query query = new Query(); | |
query.skip(index).limit(limit);// 从下标为 index 开始查询接下来的 limit 条数据 | |
return this.mongoTemplate.find(query, User.class); | |
} | |
// 模糊查询 | |
@Override | |
public List<User> like(String chars) { | |
Query query = new Query(Criteria.where("name").regex(chars));// 查询 name 中包含指定字符的数据 | |
return this.mongoTemplate.find(query, User.class); | |
} | |
// 查询并删除 | |
@Override | |
public boolean queryToRemove(String id) { | |
Query query = new Query(Criteria.where("id").is(id)); | |
DeleteResult result = this.mongoTemplate.remove(query); | |
return result.getDeletedCount() > 0; | |
} |
更多 Java API 用法,请查询 spring-boot-starter-data-mongodb 文档。
# Python API
安装 pymongo 连接工具:
python3 -m pip3 install pymongo
# 如需更新,执行如下命令:
python3 -m pip3 install --upgrade pymongo
数据库与集合操作:
#!/usr/bin/python3
import pymongo
# 建立连接
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# 创建数据库
mydb = myclient["test"]
# 查询数据库列表
dblist = myclient.list_database_names()
# dblist = myclient.database_names()
if "test" in dblist:
print("数据库已存在!")
# 创建集合
mydb = myclient["test"]
mycol = mydb["col"]
# 查询集合列表
collist = mydb.list_collection_names()
if "col" in collist:
print("集合已存在!")
增删改查
新增:
# 插入一条数据
mydict = { "name": "Chinmoku", "site": "https://www.chinmoku.cc" }
x = mycol.insert_one(mydict)
print(x.inserted_id) # 文档数据 ID
# 插入多条数据
mylist = [
{ "name": "Facebook", "url": "https://www.facebook.com" },
{ "name": "知乎", "url": "https://www.zhihu.com" },
{ "name": "Github", "url": "https://www.github.com" }
]
x = mycol.insert_many(mylist)
print(x.inserted_ids)
# 也可以指定 ID
mylist = [
{ "_id": 1, "name": "百度", "url": "https://www.baidu.com"},
{ "_id": 2, "name": "Google", "url": "https://www.google.com"}
]
x = mycol.insert_many(mylist)
print(x.inserted_ids)
删除:
# 删除单条
myquery = { "name": "百度" }
mycol.delete_one(myquery)
# 删除多条 (删除 http 协议网站)
myquery = { "url": {"$regex": "^http:"} }
x = mycol.delete_many(myquery)
print("已删除文档个数:", x.deleted_count)
# 删除所有文档
x = mycol.delete_many({})
# 删除集合
mycol.drop()
修改:
# 修改单条
myquery = { "id": "1" }
newvalues = { "$set": { "name": "No 1" } }
mycol.update_one(myquery, newvalues)
# 修改多条
myquery = { "url": {"$regex": "^http:"} }
newvalues = { "$set": { "name": "非HTTPS协议网站" } }
x = mycol.update_many(myquery, newvalues)
print("已更新文档个数:", x.modified_count)
查询:
# 查询文档第一条数据
x = mycol.find_one()
# 查询文档所有数据
for x in mycol.find():
print(x)
# 查询指定字段(指定 1,表示需要返回的字段),注意,除_id 外,其他字段不能 0 或 1 必须一致
for x in mycol.find({},{ "_id": 0, "name": 1, "url": 1 }):
print(x)
# 反向返回字段(返回除 url 之外的其他字段)
for x in mycol.find({},{ "url": 0 }):
print(x)
# 根据条件查询
myquery = { "name": "Chinmoku" }
mydoc = mycol.find(myquery)
for x in mydoc:
print(x)
# 使用修饰符查询(比较 name 首字母)
myquery = { "name": { "$gt": "H" } }
mydoc = mycol.find(myquery)
for x in mydoc:
print(x)
# 正则查询(查询 name 以 C 开头的数据)
myquery = { "name": { "$regex": "^C" } }
mydoc = mycol.find(myquery)
for x in mydoc:
print(x)
# 返回指定条数
myresult = mycol.find().limit(3)
for x in myresult:
print(x)
# 参考
https://docs.mongodb.com
https://www.runoob.com/mongodb
https://www.mrhelloworld.com/mongodb
https://www.cnblogs.com/hexrui/p/14885785.html
https://blog.csdn.net/a1120467800/article/details/109954145