MongoDB


一、简介

1、使用场景

传统的关系型数据库 (比如 MySQL), 在数据操作的”三高”需求以及对应的 Web 2.0 网站需求面前, 会有”力不从心”的感觉

所谓的三高需求:

高并发, 高性能, 高可用, 简称三高

  • High Performance: 对数据库的高并发读写的要求
  • High Storage: 对海量数据的高效率存储和访问的需求
  • High Scalability && High Available: 对数据的高扩展性和高可用性的需求

而 MongoDB 可以应对三高需求

具体的应用场景:

  • 社交场景, 使用 MongoDB 存储存储用户信息, 以及用户发表的朋友圈信息, 通过地理位置索引实现附近的人, 地点等功能.
  • 游戏场景, 使用 MongoDB 存储游戏用户信息, 用户的装备, 积分等直接以内嵌文档的形式存储, 方便查询, 高效率存储和访问.
  • 物流场景, 使用 MongoDB 存储订单信息, 订单状态在运送过程中会不断更新, 以 MongoDB 内嵌数组的形式来存储, 一次查询就能将订单所有的变更读取出来.
  • 物联网场景, 使用 MongoDB 存储所有接入的智能设备信息, 以及设备汇报的日志信息, 并对这些信息进行多维度的分析.
  • 视频直播, 使用 MongoDB 存储用户信息, 点赞互动信息等.

这些应用场景中, 数据操作方面的共同点有:

  1. 数据量大
  2. 写入操作频繁
  3. 价值较低的数据, 对事务性要求不高

对于这样的数据, 更适合用 MongoDB 来实现数据存储

那么我们什么时候选择 MongoDB 呢?

除了架构选型上, 除了上述三个特点之外, 还要考虑下面这些问题:

  • 应用不需要事务及复杂 JOIN 支持
  • 新应用, 需求会变, 数据模型无法确定, 想快速迭代开发
  • 应用需要 2000 - 3000 以上的读写QPS(更高也可以)
  • 应用需要 TB 甚至 PB 级别数据存储
  • 应用发展迅速, 需要能快速水平扩展
  • 应用要求存储的数据不丢失
  • 应用需要 99.999% 高可用
  • 应用需要大量的地理位置查询, 文本查询

如果上述有1个符合, 可以考虑 MongoDB, 2个及以上的符合, 选择 MongoDB 绝不会后悔.

2、MongoDB

MongoDB是一个开源, 高性能, 无模式的文档型数据库,当初的设计就是用于简化开发和方便扩展,是NoSQL数据库产品中的一种。是最 像关系型数据库(MySQL)的非关系型数据库。

它支持的数据结构非常松散,是一种类似于 JSON 的 格式叫BSON, 所以它既可以存储比较复杂的数据类型, 又相当的灵活。

MongoDB中的记录是一个文档,它是一个由字段和值对(field:value)组成的数据结构。MongoDB文档类似于JSON对象, 即一个文档认 为就是一个对象。字段的数据类型是字符型,它的值除了使用基本的一些类型外,还可以包括其他文档, 普通数组和文档数组。

SQL术语 MongoDB术语 说明
database database 数据库
table(表) collection(集合)
row(记录行) document(文档)
column(字段) field(字段)
tables joins 表连接,MongoDb不支持
嵌入文档 MongoDB通过嵌入文档代替多表连接

MongoDB 数据模型是面向文档的, 所谓文档就是一种类似于 JSON 的结构, 简单理解 MongoDB 这个数据库中存在的是各种各样的 JSON(BSON)。

文档型数据库的最小单位, 通常情况, 我们存储和操作的内容都是文档

MongoDB 也存在索引,同时也有主键,MongoDB 会自动将 _id 字段设置为主键

在 MongoDB 中, 数据库和集合都不需要手动创建, 当我们创建文档时, 如果文档所在的集合或者数据库不存在, 则会自动创建数据库或者集合

mysql数据库是一个完全的基于硬盘去进行数据存取的数据库,redis则是一个完全的内存数据库,而mongodb数据库则是介于这两种数据库库之间的。

这是因为mongodb数据库的数据库文件还是存放在磁盘空间之中的,但是mongodb数据库在进行数据的查询是会通过map映射的方式将数据映射到内存中的某一块空间内。这样既能够加快查询数据的速度,还能用磁盘的大容量去保存数据。

MongoDB采用的是内存和磁盘结合的存储方式,比较常用的数据都保存在内存中,如果内存中找不到数据,再去磁盘中查找,这样就加快了查询的速度。

为了保证数据的安全性,内存中的数据每一分钟都会同步到磁盘中。
此外,为了更加保护数据的安全性,MongoDB在新版本中引入了日志,日志在内存和硬盘中各一份,每10ms同步一次。这样即使断电的时候,内存中的数据没有保存到磁盘,也可以通过日志文件进行恢复,从而保证了数据的安全性。

  • 查询效率:Redis > MongoDB > MySQL

二、安装

1、下载

下载地址:https://www.mongodb.com/try/download/community?tck=docs_server

选择 centos7.0 的 tgz 包下载

上传到服务器,解压

tar -zxvf mongodb-linux-x86_64-rhel70-5.0.8.tgz

重命名文件,方便后续操作

mv mongodb-linux-x86_64-rhel70-5.0.8 mongodb

创建文件夹:data用来存放数据库、logs用来存放日志

cd mongodb
mkdir data logs

2、配置文件

进入 /bin 目录下新建 mongodb.conf

cd bin/
vim mongdb.conf

添加以下内容

#数据库数据存放目录
dbpath=/data/mongodb/data
#数据库日志存放目录
logpath=/data/mongodb/logs/mongodb.log 
#以追加的方式记录日志
logappend=true
#端口号 默认为27017
port=27017 
#以后台方式运行进程
fork=true 
#mongodb所绑定的ip地址,0.0.0.0 允许其它ip访问
bind_ip=0.0.0.0
#启用日志文件,默认启用
journal=true 
#这个选项可以过滤掉一些无用的日志信息,若需要调试使用请设置为false
quiet=true 

############## 开启用户认证,建好数据库用户再添加 #############
auth=true

启动 mongodb ,bin 目录下

./mongod -f /data/mongodb/bin/mongodb.conf --fork

配置环境变量

vim /etc/profile

export MONGODB_HOME=/data/mongodb
export PATH=$PATH:$MONGODB_HOME/bin

source /etc/profile

设置开机启动

vim /etc/rc.d/rc.local

添加如下内容:

/data/mongodb/bin/mongod -f /data/mongodb/bin/mongodb.conf

3、连接

配置好环境变量之后,可直接通过以下命令进入 mongodb

mongo

查看数据库列表

show dbs;

查看版本

db.version()

4、设置账户密码

进入mongodb命令行界面

mongo

查看数据库

show dbs

进入admin数据库

use admin

创建管理员账户

db.createUser(
    {
        user: "admin",
        pwd: "admin",
        roles: [{ role: "root", db: "admin" }]
    }
)

验证是否成功,返回1则代表成功

db.auth("admin", "admin")

切换到要设置的数据库,以 test 为例

use test

为 test 创建用户,用户名和密码请自行设置。

db.createUser(
    {
        user: "test", 
        pwd: "123456", 
        roles: [{ role: "dbOwner", db: "test" }]
    }
)

输入 exit 退出数据库管理。

设置连接需要认证

修改配置文件添加 auth=true

重启

连接mongodb,使用执行关闭命令

use admin
db.shutdownServer()

修改用户密码

db.updateUser("admin",{pwd:"mongoPwd"})

三、基本语法

1、操作数据库

操作 语法
查看所有数据库 show dbs;show databases;
查看当前数据库 db;
切换到某数据库 (若数据库不存在则创建数据库) use <db_name>;
删除当前数据库 db.dropDatabase();

2、操作集合

操作 语法
查看所有集合 show collections;
创建集合 db.createCollection("<collection_name>");
删除集合 db.<collection_name>.drop()

四、操作文档

1、新增

(1)单条新增

db.集合名.insert(JSON数据)

  • 使用 db.<collection_name>.insertOne() 向集合中添加一个文档, 参数一个 json 格式的文档
  • 使用 db.<collection_name>.insertMany() 向集合中添加多个文档, 参数为 json 文档数组

集合存在,则直接插入数据,集合不存在,隐式创建并插入

db.collection.insert({
  <document or array of documents>,
  writeConcern: <document>,
  ordered: <boolean>
})


// 向集合中添加一个文档
db.collection.insertOne(
   { item: "canvas", qty: 100, tags: ["cotton"], size: { h: 28, w: 35.5, uom: "cm" } }
)
// 向集合中添加多个文档
db.collection.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" } }
])

注:当我们向 collection 中插入 document 文档时, 如果没有给文档指定 _id 属性, 那么数据库会为文档自动添加 _id field, 并且值类型是 ObjectId(blablabla), 就是文档的唯一标识, 类似于 relational database 里的 primary key

  • mongo 中的数字, 默认情况下是 double 类型, 如果要存整型, 必须使用函数 NumberInt(整型数字), 否则取出来就有问题了
  • 插入当前日期可以使用 new Date()

(2)批量新增

db.集合名.insert([json数据1,json数据2])

db.test.insert([{name:"zhangsan",age:1},{name:"lisi",age:2}])

// mongodb底层使用JS引擎实现的,所以支持部分js语法,也可以使用for循环
for(var i=1;i<=10;i++){
  db.test.insert({name:"a"+i,age:i})
}

(3)结合try/catch

当一次性插入或者更新n条数据时,mongodb不会因为一条数据的错误而使得整个操作终止并回滚,只会终止接下来的操作,所以可以使用trycatch来进行异常的捕捉处理。测试的时候可以不处理。

try{
  db.c1.insertMany([
      {"_id":1,name:"xzy"},
      {"_id":2,name:"lhl"},
    {"_id":3,name:"yzh"},
    {"_id":4,name:"lwy"}
  ])
}catch(e){
    print(e);
}

2、删除

db.集合名.remove(条件[,是否删除一条])

是否删除一条 true是,false否 默认

//当存在多条符合条件的行时,只删除一条
db.c3.remove({username:"zs30"},true)
//存在多条时,全部删除
db.c3.remove({username:"zs30"},false)

3、修改

基础语法: db.集合名.update(条件,新数据[,是否新增,是否修改多条])

  • 是否新增:指条件匹配不到数据则插入,true是插入,false否不插入默认
  • 是否修改多条:指将匹配成功的数据都修改(true是,false否默认)
  • db.<collection_name>.updateOne(<filter>, <update>, <options>) 方法修改一个匹配 <filter> 条件的文档
  • db.<collection_name>.updateMany(<filter>, <update>, <options>) 方法修改所有匹配 <filter> 条件的文档
  • db.<collection_name>.replaceOne(<filter>, <update>, <options>) 方法替换一个匹配 <filter> 条件的文档
  • db.<collection_name>.update(查询对象, 新对象) 默认情况下会使用新对象替换旧对象

其中 <filter> 参数与查询方法中的条件参数用法一致.

// 默认会修改第一条
db.document.update({ userid: "30", { $set {username: "guest"} } })

// 修改所有符合条件的数据
db.document.update( { userid: "30", { $set {username: "guest"} } }, {multi: true} )

运算符

运算符 作用
$inc 递增
$rename 重命名列
$set 修改列值
$unset 删除列
db.c3.update({username:"zs2"}{$set:{username:"zs222"}}//给zs10 增加2岁
db.c3.update({username:"zs10"},{$inc:{age:2}})
//给zs10 减少2岁
db.c3.update({username:"zs10"},{$inc:{age:-2}})


//复杂修改
db.c4.update({username:"lisi"},{
    $set:{username:"wangwu"},
    $inc:{age:1},
    $rename:{who:"sex"},
    $unset:{other:true}
})

//更新不存在的值,若不存在则不会有操作
db.c3.update({username:"zs30"},{$set:{age:30}})
//在最后加一个true参数,作用是,如果不存在,则插入该条数据,默认为false则不管
db.c3.update({username:"zs30"},{$set:{age:30}},true)

//第四个参数如果为true,当匹配到多条条件符合的元素时,都更改,默认为false,只改一条
db.c3.update({},{$set:{age:20}},false,true)

4、查询

语法: db.集合名.find(条件,[查询的列])

  • db.<collection_name>.find() 接受一个 json 格式的查询条件. 返回的是一个数组

  • db.<collection_name>.findOne() 查询集合中符合条件的第一个文档, 返回的是一个对象

  • 条件

    • 查询所有数据 {}或者不写
    • 查询age=6的数据 {age:6}
    • 查询age=6且性别为男 {age:6,sex:’男’}
  • 查询的列

    • 不写 - 查询全部的列
    • {age:1} 只显示age列,可以显示多个想要的列{user:1,age:1…….}
    • {age:0} 除了age列外都显示 可以不显示多个想要的列{user:0,age:0}
    • 无论怎么写系统自定义_id都会在
// 查询全部
db.test.find()

// 查询 name 为张三的文档
db.test.find({name: "zhangsan"})

// 查询 name 为张三的age字段
db.test.find({name: "zhangsan"},{age: 1})

运算符

db.集合名.find({
    键:{运算符:值}
})
运算符 作用
$gt 大于
$gte 大于等于
$lt 小于
$lte 小于等于
$ne 不等于
$in in
$nin Not in
//年龄小于5的
db.c1.find({age:{$lt:5}})

//年龄等于3、4、5的
db.c1.find({age:{$in:[3,4,5]}})

五、高级查询

1、排序

语法:db.集合名.find().sort(JSON数据)

说明:键-就是要排序的列/字段,值:1升序 -1降序

//对年龄进行降序排序
db.c2.find().sort({age:-1})

2、分页

语法:db.集合名.find().skip(数字).limit(数字)

说明:skip里的数字指跳过指定数量(可选),limit限制查询的数量

db.c2.find().sort({age:-1}).skip(1).limit(2)

3、聚合查询

db.集合名称.aggregate([
    {管道:{表达式}}
    ....
])

常用管道:
$group   将集合中的文档分组,用于统计结果
$match   过滤数据,只要输出符合条件的文档
$sort         聚合数据进一步排序
$skip    跳过指定文档数
$limit   限制集合数据返回文档数
....

常用表达式:
$sum 总和 $sum:1同count表示统计
$avg 平均
$min 最小值
$max 最大值

六、数据类型

数据类型 描述
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 使用的是 B Tree, MySQL 使用的是 B+ Tree

1、索引操作

(1)创建索引

创建索引语法: db.集合名.createIndex(待创建索引的列[,额外选项])

  • 待创建索引的列:{键:1,…,键:-1},说明:1升序 -1降序 列入{age:1}表示创建age索引并按照升序的方式存储

  • 额外选项:设置索引的名称或者唯一索引等等

//创建只对单个列为条件的索引
db.c1.create({name:1})
//创建一个自己取名的索引
db.c1.create({name:1},{name:"xzy"})
//创建条件为多个列的组合索引
db.c1.create({name:1,age:-1},{"hh"})

//创建唯一索引
db.c1.createIndex({name:1},{unique:"name"})

(2)删除索引

全部删除:db.集合名.dropIndexes()

删除指定:db.集合名.dropIndex(索引名)

(3)查看索引

查看索引语法:db.集合名.getIndexes()

2、索引的类型

(1)单字段索引

MongoDB 支持在文档的单个字段上创建用户定义的升序/降序索引, 称为单字段索引 Single Field Index

对于单个字段索引和排序操作, 索引键的排序顺序(即升序或降序)并不重要, 因为 MongoDB 可以在任何方向上遍历索引.

(2)复合索引

MongoDB 还支持多个字段的用户定义索引, 即复合索引 Compound Index

复合索引中列出的字段顺序具有重要意义.例如, 如果复合索引由 { userid: 1, score: -1 } 组成, 则索引首先按 userid 正序排序, 然后 在每个 userid 的值内, 再在按 score 倒序排序.

(3)其他索引

其他索引

  • 地理空间索引 Geospatial Index
  • 文本索引 Text Indexes
  • 哈希索引 Hashed Indexes
地理空间索引(Geospatial Index)

为了支持对地理空间坐标数据的有效查询, MongoDB 提供了两种特殊的索引: 返回结果时使用平面几何的二维索引和返回结果时使用球面几何的二维球面索引.

文本索引(Text Indexes)

MongoDB 提供了一种文本索引类型, 支持在集合中搜索字符串内容.这些文本索引不存储特定于语言的停止词(例如 “the”, “a”, “or”), 而将集合中的词作为词干, 只存储根词.

哈希索引(Hashed Indexes)

为了支持基于散列的分片, MongoDB 提供了散列索引类型, 它对字段值的散列进行索引.这些索引在其范围内的值分布更加随机, 但只支持相等匹配, 不支持基于范围的查询.

3、执行计划

分析查询性能 (Analyze Query Performance) 通常使用执行计划 (解释计划 - Explain Plan) 来查看查询的情况

db.<collection_name>.find( query, options ).explain(options)

输出结果中的 "stage" 表示索引使用情况:

  • "stage" : "COLLSCAN", 表示全集合扫描

  • "stage" : "IXSCAN", 基于索引的扫描

COLLSCAN 全表扫描

IXSCAN索引扫描

FETCH根据索引去检索指定document

八、备份还原

在终端中执行,该命令不是mongo命令
导出:mongodump -h -port -u -p -d -o
导出语法说明
-h host 服务器IP地址(一般不写 默认本机)
-port port 端口(不写默认27017)
-u user 用户
-p pwd 密码
-d database 数据库(不写默认导出全部)
-o open 备份到指定目录下

mongodump -u admin -p 123456 -o /Users/didi/xzy文件/mongo
#注意 最新的mongodb版本4.4中,是没有mongodump工具的,需要通过使用brew命令单独下载
#brew install mongodb-database-tools

单独备份一个指定数据库:
mongodump -u shop2 -p 123456 -d shop -o /Users/didi/xzy文件/mongo
#此时好像不能使用admin作为用户来备份,可能是因为这个不是创建在shop中的用户?

九、Java中使用

1、配置

依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

配置文件

spiring:
    #数据源配置
    data:
        mongodb:
            #主机地址
            host: locahost
            #数据库
            database: test
            #默认端口时27017
            port: 27017
            #也可以使用url连接
            #url:mongodb://localhost:27017/test

或者如下

spring.data.mongodb.uri=mongodb://user:pwd@localhost:27017/test

#多个IP集群可以采用以下配置:

spring.data.mongodb.uri=mongodb://user:pwd@ip1:port1,ip2:port2/database

2、基本操作

https://gitee.com/AoiX/studied/blob/master/documents/MongoDB.md#u%E6%94%B9


  目录