一篇文章上手MongoDB 4.X

本篇文章主要汇总MongoDB的一些核心知识点,以及在Python和PHP中的使用,持续更新,力争清晰全面,便于作为手册查询。

一,概述

1. MongoDB是什么 WHAT

MongoDB 是一个由C++语言编写的,基于分布式文件存储的NoSQL数据库。

它是非关系型数据库中功能最丰富,最像关系数据库的。

MongoDB 提出的是文档、集合的概念,他将数据存储为一个文档,并使用BSON(类JSON对象)作为其数据模型结构,由键值key => value对组成。

其结构是面向对象的,而不是二维表。字段值可以包含其他文档,数组及文档数组。

2. 为什么要用MongoDB WHY

  • 文档数据类型

    文档是一组属性名和属性的集合。相较于关系数据库复杂的规范化,面向文档的数据模型很容易以聚合的形式来表示数据。

    SQL类型的数据库可以通过主键或者外键的约束,保证数据的完整性与唯一性,而MongoDB的文档没有固定的Schema,可以让数据的存储数据结构更灵活。

  • 即时查询能力

    MongoDB保留了关系型数据库即时查询的能力,保留了索引(底层是基于B tree),并对查询进行优化,无需预先定义系统接受的查询类型。

  • 复制能力

    MongoDB采用副本集(replica set)的拓扑结构提供复制功能,能将数据分布到多台机器上实现冗余,在服务器或网络故障时,可以提供自动故障转移、扩展读能力。
      副本集由一个主节点(Primary node)和一个或多个从节点(secondary node)构成。当主节点down掉后,集群会选择一个从节点自动将它提升为主节点,先前的主节点恢复后变成一个从节点。主节点既能够读也能够写,而从节点是只读的。

  • 速度与持久性

    通过开启Journaling日志记录,控制速度和持久化之间的平衡。

    MongoDB的驱动实现一个写入语义 fire and forget ,即通过驱动调用写入时,可以立即得到返回得到成功的结果,这样让写入的速度更加快。其次提供了Journaling日志的概念,实际上像mysql的bin-log日志,当需要插入的时候会先往日志里面写入记录,再完成实际的数据操作,这样如果出现停电,进程突然中断的情况,可以保障数据不会错误,可以通过修复功能读取Journaling日志进行修复。

  • 水平扩展能力

    MongoDB使用分片技术对数据进行水平扩展,通过集群将数据分布到多台机器,而不是只提升单个节点的性能。MongoDB能自动分片、自动转移分片里面的数据块,让每一个服务器里面存储的数据都是一样大小,降低故障带来的影响。

二,MongoDB shell HOW

1. 安装

32bit的mongodb最大只能存放2G的数据,64bit就没有限制

下载MongoDB

https://www.mongodb.com/download-center/community

下载最新版安装包

 curl -O https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.2.1.tgz

解压并指定目录

 tar -zxvf mongodb-linux-x86_64-rhel70-4.2.1.tgz 
 mv mongodb-linux-x86_64-rhel70-4.2.1 /usr/local/mongodb

创建数据目录

 cd /usr/local/mongodb/
 mkdir -p ./data/db
 mkdir ./logs

创建配置文件

  vim mongodb.conf
dbpath = /usr/local/mongodb/data/db #数据文件存放目录
logpath = /usr/local/mongodb/logs/mongodb.log #日志文件存放目录
port = 27017  # 端口
fork = true  # 以守护程序的方式启用,即在后台运行
auth=true # 测试阶段可以关闭权限校验
bind_ip=0.0.0.0

环境变量配置 /etc/profile, 添加这条语句:

export PATH=$PATH:/usr/local/mongodb/bin

启用配置

source /etc/profile

启动MongoDB

mongod -f /usr/local/mongodb/mongodb.conf
about to fork child process, waiting until server is ready for connections.
forked process: 4207
child process started successfully, parent exiting

查看服务状态

ps aux |grep mongodb
root      4207  3.9  7.8 1547080 79192 ?       Sl   08:50   0:03 mongod -f /usr/local/mongodb/mongodb.conf
root      4246  0.0  0.0 112708   976 pts/1    R+   08:51   0:00 grep --color=auto mongodb

关闭连接

  mongod -f /usr/local/mongodb/mongodb.conf --shutdown  
2019-11-24T08:50:17.366-0500 I  CONTROL  [main] log file "/usr/local/mongodb/logs/mongodb.log" exists; moved to "/usr/local/mongodb/logs/mongodb.log.2019-11-24T13-50-17".
killing process with pid: 4049

自此MongoDB 4.2安装成功

2. MongoDB基本操作

使用终端连接

mongo

这个shell就是mongodb的客户端,同时也是一个js的编译器

MongoDB shell version v4.2.1
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("e1ac6d21-6554-45fc-8f5e-0f27b05c8402") }
MongoDB server version: 4.2.1
Server has startup warnings: 
2019-11-24T08:50:44.275-0500 I  CONTROL  [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2019-11-24T08:50:44.275-0500 I  CONTROL  [initandlisten] 
2019-11-24T08:50:44.275-0500 I  CONTROL  [initandlisten] 
2019-11-24T08:50:44.275-0500 I  CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2019-11-24T08:50:44.275-0500 I  CONTROL  [initandlisten] **        We suggest setting it to 'never'
2019-11-24T08:50:44.275-0500 I  CONTROL  [initandlisten] 
2019-11-24T08:50:44.275-0500 I  CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2019-11-24T08:50:44.275-0500 I  CONTROL  [initandlisten] **        We suggest setting it to 'never'
2019-11-24T08:50:44.275-0500 I  CONTROL  [initandlisten] 
2019-11-24T08:50:44.275-0500 I  CONTROL  [initandlisten] ** WARNING: soft rlimits too low. rlimits set to 4096 processes, 65535 files. Number of processes should be at least 32767.5 : 0.5 times number of files.
2019-11-24T08:50:44.275-0500 I  CONTROL  [initandlisten] 
---
Enable MongoDB's free cloud-based monitoring service, which will then receive and display
metrics about your deployment (disk utilization, CPU, operation statistics, etc).

The monitoring data will be available on a MongoDB website with a unique URL accessible to you
and anyone you share the URL with. MongoDB may use this information to make product
improvements and to suggest MongoDB products and deployment options to you.

To enable free monitoring, run the following command: db.enableFreeMonitoring()
To permanently disable this reminder, run the following command: db.disableFreeMonitoring()
---
  • 查看 mongodb 的版本
db.version()
  • 查看当前数据库
# 查看当前数据库名称
db

# 查看当前状态信息
db.stats()

默认的数据库为test,如果你没有创建新的数据库,集合将存放在test数据库中

> db.stats()
{
        "db" : "test",
        "collections" : 0,
        "views" : 0,
        "objects" : 0,
        "avgObjSize" : 0,
        "dataSize" : 0,
        "storageSize" : 0,
        "numExtents" : 0,
        "indexes" : 0,
        "indexSize" : 0,
        "scaleFactor" : 1,
        "fileSize" : 0,
        "fsUsedSize" : 0,
        "fsTotalSize" : 0,
        "ok" : 1
}
  • 列出所有在物理上存在的数据库
show dbs
  • 切换数据库
use demo

如果数据库不存在,则指向数据库,但不创建,直到插入数据或创建集合时数据库才被创建

  • 终端退出连接
exit

  • 查看当前数据库的集合
show collections
  • 集合创建
db.createCollection(name, options)

name是要创建的集合的名称,options是一个文档,用于指定集合的配置

> show collections
> db.createCollection('project')
{ "ok" : 1 }
  • 删除集合
db.name.drop()
> show collections
project
> db.project.drop()
true
> show collections

基本数据操作

  • 插入文档
# 通用插入
db.集合名称.insert(document)

#单条插入
db.集合名称.insertOne(document)

# 多条插入
db.集合名称.insertMany(documents)

插入文档时,如果不指定_id参数,MongoDB会为文档分配一个唯一的ObjectId

> db.project.insert([{name:'阿里巴巴',founder:'马云'},{name:'京东',founder: '刘强东'}])
BulkWriteResult({
        "writeErrors" : [ ],
        "writeConcernErrors" : [ ],
        "nInserted" : 2,
        "nUpserted" : 0,
        "nMatched" : 0,
        "nModified" : 0,
        "nRemoved" : 0,
        "upserted" : [ ]
})
  • 查询文档
# 查询多条
db.集合名称.find({条件}) 

# 查询一条
db.集合名称.findOne({条件})

# 方法pretty():将结果格式化
db.集合名称.find({条件}).pretty()
> db.project.find()
{ "_id" : ObjectId("5dda8de90de81f9845d393fe"), "name" : "阿里巴巴", "founder" : "孙正义" }
{ "_id" : ObjectId("5dda8de90de81f9845d393ff"), "name" : "京东", "founder" : "刘强东" }

ROBO 查询指定字段

db.getCollection('demo').find({},{_id:0, rongzi:1}).count()
  • 更新文档
db.集合名称.update(
   {query},
   {update},
   {multi: }
)

参数query:查询条件,类似sql语句update中where部分
参数update:更新操作符,类似sql语句update中set部分
参数multi:可选,默认false,表示只更新找到的第一条记录,true表示全部更新

> db.project.update({name:'阿里巴巴'}, {$set:{founder:'马云'}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.project.find()
{ "_id" : ObjectId("5dda8de90de81f9845d393fe"), "name" : "阿里巴巴", "founder" : "马云" }
{ "_id" : ObjectId("5dda8de90de81f9845d393ff"), "name" : "京东", "founder" : "刘强东" }
  • 删除文档
db.集合名称.remove(
   {query},
   {justOne: }
)

参数query:可选,删除的文档的条件,不加条件则全部删除
参数justOne:可选,如果设为true或1,则只删除匹配的一条,默认false,表示删除多条

> db.project.remove({name:'京东'})
WriteResult({ "nRemoved" : 1 })

> db.project.find()
{ "_id" : ObjectId("5dda8de90de81f9845d393fe"), "name" : "阿里巴巴", "founder" : "马云" }
  • skip与Limit
# 获取指定条数的文档
db.集合名称.find({条件可选}).limit(NO.)

# 跳过指定条数的文档,NO.为2则表示查询从第3条开始
db.集合名称.find({条件可选}).skip(NO.)

方法limit()和skip()可以一起使用,不分先后顺序

> db.createCollection('people')
{ "ok" : 1 }

> for(i=0;i<10;i++){db.people.insert({_id:i,name:'people' + i})}
WriteResult({ "nInserted" : 1 })

> db.people.find({_id:{$gte:1}}).skip(2).limit(5)
{ "_id" : 3, "name" : "people3" }
{ "_id" : 4, "name" : "people4" }
{ "_id" : 5, "name" : "people5" }
{ "_id" : 6, "name" : "people6" }
{ "_id" : 7, "name" : "people7" }
> 
  • 投影(字段)
db.集合名称.find({},{字段名称:1,...})

参数为字段与值,值为1表示显示,值为0不显示
对于需要显示的字段,设置为1即可,不设置即为不显示(_id默认显示)

> db.project.find({},{name:1})
{ "_id" : ObjectId("5dda8de90de81f9845d393fe"), "name" : "阿里巴巴" }
  • 排序 sort()
db.集合名称.find().sort({字段:1,...})

参数1为升序排列,参数-1为降序排列

> db.people.find({_id:{$gt:0}}).limit(3).sort({_id:1})
{ "_id" : 1, "name" : "people1" }
{ "_id" : 2, "name" : "people2" }
{ "_id" : 3, "name" : "people3" }

> db.people.find({_id:{$gt:0}}).limit(3).sort({_id:-1})
{ "_id" : 9, "name" : "people9" }
{ "_id" : 8, "name" : "people8" }
{ "_id" : 7, "name" : "people7" }
  • 统计个数 count()
db.集合名称.find({条件}).count()
db.集合名称.count({条件})
> db.people.find().count()
10

> db.people.count()
10
  • 去重 distinct()
db.集合名称.distinct('去重字段',{条件})
  • 聚合 aggregate
db.集合名称.aggregate([{管道:{表达式}}])

管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的输入,在mongodb中,管道具有同样的作用

常用管道

管道名 作用
$group 将集合中的文档分组,可用于统计结果
$match 过滤数据,只输出符合条件的文档
$project 修改输入文档的结构,如重命名、增加、删除字段、创建计算结果
$sort 将输入文档排序后输出
$limit 限制聚合管道返回的文档数
$skip 跳过指定数量的文档,并返回余下的文档
$unwind 将数组类型的字段进行拆分

索引

  • 创建大量数据:
for (i = 0; i < 100000; i++) {
    db.表.insert({name:'test'+i, age:i})
}
  • 数据查找性能分析(使用explain()命令进行查看性能)
> db.explain.find({name:'test666'}).explain('executionStats')
{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "demo.explain",
                "indexFilterSet" : false,
                "parsedQuery" : {
                        "name" : {
                                "$eq" : "test666"
                        }
                },
                "winningPlan" : {
                        "stage" : "COLLSCAN",
                        "filter" : {
                                "name" : {
                                        "$eq" : "test666"
                                }
                        },
                        "direction" : "forward"
                },
                "rejectedPlans" : [ ]
        },
        "executionStats" : {
                "executionSuccess" : true,
                "nReturned" : 0,
                "executionTimeMillis" : 0,
                "totalKeysExamined" : 0,
                "totalDocsExamined" : 10,
                "executionStages" : {
                        "stage" : "COLLSCAN",
                        "filter" : {
                                "name" : {
                                        "$eq" : "test666"
                                }
                        },
                        "nReturned" : 0,
                        "executionTimeMillisEstimate" : 0,
                        "works" : 12,
                        "advanced" : 0,
                        "needTime" : 11,
                        "needYield" : 0,
                        "saveState" : 0,
                        "restoreState" : 0,
                        "isEOF" : 1,
                        "direction" : "forward",
                        "docsExamined" : 10
                }
        },
        "serverInfo" : {
                "host" : "10.0.3.15",
                "port" : 27017,
                "version" : "4.2.1",
                "gitVersion" : "edf6d45851c0b9ee15548f0f847df141764a317e"
        },
        "ok" : 1
}

三,MongoDB在php中的使用

  1. 安装MongoDB扩展
composer require topthink/think-mongo=2.0.*
  1. 配置相关参数
// 数据库类型
'type'           => '\think\mongo\Connection',
// 设置查询类
'query'          => '\think\mongo\Query',
// 服务器地址
'hostname'       => '127.0.0.1',
// 强制把_id转换为id进行操作,保持和Mysql一致的主键命名习惯
'pk_convert_id'  => true,
// 集合名
'database'       => 'demo',
// 用户名
'username'       => '',
// 密码
'password'       => '',
// 端口
'hostport'       => '',
  1. thinkPHP支持的原生查询

query (query)

collection:表示当前查询的集合
query:是一个\MongoDB\Driver\Query对象

$filter = [
    'author' => 'bjori',
    'views' => [
        '$gte' => 100,
    ],
];

$options = [
    /* Only return the following fields in the matching documents */
    'projection' => [
        'title' => 1,
        'article' => 1,
    ],
    /* Return the documents in descending order of views */
    'sort' => [
        'views' => -1
    ],
);

$query = new MongoDB\Driver\Query($filter, $options);
Db::query('demo.user', $query);

execute (bulk)

collection:表示当前查询的集合
bulk:是一个\MongoDB\Driver\BulkWrite对象

command (dbName)

command:是一个\MongoDB\Driver\Command对象
dbName:当前操作的数据库名称,留空表示当前数据库

系统还封装了一个cmd方法可以直接执行字符串格式的mongo命令

// 列出当前的集合
$collections = Db::cmd('listCollections');
  1. 查询ORM
// 查询操作
$user = Db::table('user')
    ->where('id','589461c0fc122812b4007411')
    ->find();
dump($user);

针对MongoDb的特殊链式操作方法

方法 描述
skip 设置skip
awaitData 设置awaitData
batchSize 设置batchSize
exhaust 设置exhaust
modifiers 设置modifiers
noCursorTimeout 设置noCursorTimeout
oplogReplay 设置oplogReplay
partial 设置partial
maxTimeMS 设置maxTimeMS
slaveOk 设置slaveOk
tailable 设置tailable
writeConcern 设置writeConcern

不再支持的方法

view、join、alias、group、having、union、lock、strict、sequence、force、bind、partition

安装PHP扩展问题汇总:

  1. brew install mongodb 安装mongodb提示:No available formula with the name "mongodb"

https://blog.csdn.net/weixin_40368256/article/details/100626177

四,schema 设计原则

MongoDB是文档型数据库,是Schema Free的,这种设计的好处如下:

  • 使用BSON结构,可直接将一个json数据存储进MongoDB,非常友好
  • 通过合理的数据模型设计,将多种关联需求通过内嵌、反范式的方式实现,减少了随机IO,读写性能高
  • 数据模型灵活,无需为Online DDL而操心,不同文档可以有不同结构

但这样也有一些弊端,对于PHP这种弱类型语言,如果往一个集合里插入任意类型的数据,可能会导致查询结果的错误。例如:

> db.members.find();
{ "_id" : ObjectId("5b62a5c73eeb75204f15dbe3"), "user_id" : 100, "level" : "vip" }
{ "_id" : ObjectId("5b62a5cb3eeb75204f15dbe4"), "user_id" : 101, "level" : "vip" }
{ "_id" : ObjectId("5b62a5db3eeb75204f15dbe5"), "user_id" : "102", "level" : "gold" }
{ "_id" : ObjectId("5b62a5e53eeb75204f15dbe6"), "user_id" : 102, "level" : "gold" }

集合中有user_id为102和"102"的两个文档,按user_id查询

> db.members.find({user_id:{$eq:102}});
{ "_id" : ObjectId("5b62a5e53eeb75204f15dbe6"), "user_id" : 102, "level" : "gold" }

> db.members.find({user_id:{$eq:"102"}});
{ "_id" : ObjectId("5b62a5db3eeb75204f15dbe5"), "user_id" : "102", "level" : "gold" }

MongoDB从版本3.2开始引入了schema validation,可以为集合指定验证规则,在正确的地方、需要的地方schema free,在适当的地方要有限制。我们为members集合指定验证规则,限定user_id必须为int类型,且不可或缺。

> db.createCollection( "contacts",{ validator: { $or :
[
  { phone: { $type: "string" } },
  { email: { $regex: /@mongodb.com$/ } },
  { status: { $in: [ "Unknown", "Incomplete" ] } }
]
}} )
{ "ok" : 1 }

尝试插入user_id为string类型的数据

> db.members.insert({user_id:"102", level:"gold"});
Document failed validation

对已经建立了的表,可以通过如下方式来做限定:

db.runCommand( {
collMod: "contacts",
validator: { $or: [ { phone: { $type: "string" } }, { email: { $regex: /@mongodb.com$/ } }, { status: { $in: [ "Unknown", "Incomplete" ] } } ] },
validationLevel: "moderate"
} )

我们可以在设置validation的时候指定我们的validationLevel级别

  • 默认级别是strict,对该collection已有和新增document都进行validation验证
  • 可以设置为moderate,仅对已存在的document进行validation限定

validationAction参数来指定,当有不符合validation规则的数据进行update或者insert的时候,mongodb实例如何处理

  • 默认级别为error,不符合validation规则的拒绝insert和update
  • 可以设置为warn,mongodb会在日志中记录,但允许insert和update操作

validation的限制

  • validation不能对admin、local和config库中的collection进行设置;
  • 不能对system.*这类collections进行validation设置;

五,索引与查询优化

mongodb也有索引,视图功能, MongoDB 4.0 引入的事务功能,支持多文档ACID特性。

六,MongoDB副本和分片

MongoDb在用于生产环境的三种模式,master/slaves(主从模式)、replcation副本集和auto shard分片模式。官方推荐使用副本集模式。

副本

副本集是为了解决mongodb的可靠性。

在早期的系统设计中,主从模式是比较流行的。将读写分离,在不同的DB上操作,可以有效降低数据库的压力,还能实现数据的备份,但是在master节点故障时,不能及时的自动切换到slaves节点,需要手动干预。

MongoDB复制是将数据同步在多个服务器的过程,它不但实现了主从模式的读写分离,而且有自己的一套选举机制,能通过自己的算法,选举出当前最优节点作为Primary。

客户端从主节点上读写数据,主节点负责将数据同步复制到副本节点。

主节点和副本节点,以及副本节点之间会有心跳

一旦主节点挂掉,会马上选出一个副本节点作为主节点,不影响数据库的使用。

一般读取从主节点读取,但也可以通过设置从副本节点读取

分片

分片是为了解决mongodb的扩展问题。

利用Mongo的分片,可以将数据自动分解成多个块,存储在不同的节点上,每个被拆分的块都有三个副本集,这样是为了数据备份和恢复,而且数据分片后,可以利用多台廉价的存储和CPU的计算构建一个水平可扩展的系统架构。

水平扩展涉及划分系统数据集并加载多个服务器,添加其他服务器以根据需要增加容量。虽然单个机器的总体速度或容量可能不高,但每台机器处理整个工作负载的子集,可能提供比单台服务器更高的效率。扩展部署容量,只需要根据需求添加额外的服务器,这可能比单个机器的垂直扩展总体成本更低。

MongoDB分片群集包含以下组件:

分片:每个分片包含分片数据的子集。每个分片都可以部署为副本集。
mongos:mongos充当查询路由器,在客户端应用程序和分片集群之间提供接口。
config servers:配置服务器存储群集的元数据和配置设置。从MongoDB 3.4开始,必须将配置服务器部署为副本集(CSRS)

数据库可以混合使用分片和非分片集合。分片集合在集群中的分片上进行分区和分布。非散列集合存储在主分片上。每个数据库都有自己的主分片。必须连接到mongos路由器才能与分片群集中的任何集合进行交互。这包括分片和非分片集合。客户端永远不应连接到单个分片以执行读取或写入操作

七,部署与日常管理

参考文档:

https://docs.mongodb.com/manual/introduction/

你可能感兴趣的:(一篇文章上手MongoDB 4.X)