目录
一、MongoDB概述
1.MongoDB定义
MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。
MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
MongoDB服务端可运行在Linux、Windows平台,支持32位和64位应用,默认端口为27017。
推荐运行在64位平台,因为MongoDB在32位模式运行时支持的最大文件尺寸为2GB。
2.MongoDB主要特点
2.1文档
MongoDB
中的记录是一个文档,它是由字段和值对组成的数据结构。
多个键及其关联的值有序地放在一起就构成了文档。
MongoDB
文档类似于JSON
对象。字段的值可以包括其他文档,数组和文档数组。
{"greeting":"hello,world"}
这个文档只有一个键"greeting"
,对应的值为"hello,world"
。多数情况下,文档比这个更复杂,它包含多个键/值对。
例如:{"greeting":"hello,world","foo": 3}
文档中的键/值对是有序的,下面的文档与上面的文档是完全不同的两个文档。{"foo": 3 ,"greeting":"hello,world"}
文档中的值不仅可以是双引号中的字符串,也可以是其他的数据类型,例如,整型、布尔型等,也可以是另外一个文档,即文档可以嵌套。文档中的键类型只能是字符串。
使用文档的优点:
- 文档(即对象)对应于许多编程语言中的本机数据类型
- 嵌入式文档和数组减少了对昂贵连接的需求
- 动态模式支持流畅的多态性
2.2集合
集合就是一组文档,类似于关系数据库中的表。
集合是无模式的,集合中的文档可以是各式各样的。例如,{"hello,word":"Mike"}和{"foo": 3},它们的键不同,值的类型也不同,但是它们可以存放在同一个集合中,也就是不同模式的文档都可以放在同一个集合中。
既然集合中可以存放任何类型的文档,那么为什么还需要使用多个集合?
这是因为所有文档都放在同一个集合中,无论对于开发者还是管理员,都很难对集合进行管理,而且这种情形下,对集合的查询等操作效率都不高。所以在实际使用中,往往将文档分类存放在不同的集合中。
例如,对于网站的日志记录,可以根据日志的级别进行存储,Info级别日志存放在Info 集合中,Debug 级别日志存放在Debug 集合中,这样既方便了管理,也提供了查询性能。
但是需要注意的是,这种对文档进行划分来分别存储并不是MongoDB 的强制要求,用户可以灵活选择。
可以使用"."按照命名空间将集合划分为子集合。
例如,对于一个博客系统,可能包括blog.user 和blog.article 两个子集合,这样划分只是让组织结构更好一些,blog 集合和blog.user、blog.article 没有任何关系。虽然子集合没有任何特殊的地方,但是使用子集合组织数据结构清晰,这也是MongoDB 推荐的方法。
2.3数据库
MongoDB 中多个文档组成集合,多个集合组成数据库。
一个MongoDB 实例可以承载多个数据库。它们之间可以看作相互独立,每个数据库都有独立的权限控制。在磁盘上,不同的数据库存放在不同的文件中。
MongoDB 中存在以下系统数据库。
- Admin 数据库:一个权限数据库,如果创建用户的时候将该用户添加到admin 数据库中,那么该用户就自动继承了所有数据库的权限。
- Local 数据库:这个数据库永远不会被复制,可以用来存储本地单台服务器的任意集合。
- Config 数据库:当MongoDB 使用分片模式时,config 数据库在内部使用,用于保存分片的信息。
2.4数据模型
一个MongoDB 实例可以包含一组数据库,一个DataBase 可以包含一组Collection(集合),一个集合可以包含一组Document(文档)。
一个Document包含一组field(字段),每一个字段都是一个key/value pair
- key: 必须为字符串类型
- value:可以包含如下类型
- 基本类型,例如,string,int,float,timestamp,binary 等类型
- 一个document
- 数组类型
二、安装MongoDB
1.Windows安装MongoDB
1.1下载MongoDB
MongoDB提供了可用于32位系统和64位系统的预编译二进制包(新版本没有了32位系统的安装文件),你可以进入MongoDB官网下载安装,MongoDB的预编译二进制包的下载地址为:https://www.mongodb.com/download-center/community,打开之后会看到如下图,直接点击Download下载即可,也可以在Version中选择你想要的版本:
1.2安装MongoDB
双击打开文件进行安装,在安装过程中,可以通过点击 "Custom
(自定义)" 按钮来设置你的安装目录。
这里我选择安装在E:\MongoDB
这个目录下(安装目录会影响我们后面的配置)
这里选择直接next
这里安装 "Install MongoDB Compass"
不勾选,否则可能要很长时间都一直在执行安装,MongoDB Compass
是一个图形界面管理工具,这里不安装也是没有问题的,可以自己去下载一个图形界面管理工具,比如Robo3T
。
之后稍微等待一会就安装好了。
1.3配置MongoDB
MongoDB
的安装过程是很简单的,但是配置就比较麻烦了,可能会遇到各种各样的问题,需要你有足够的耐心和仔细。
首先要在MongoDB
的data
文件夹里新建一个db
文件夹和一个log
文件夹:
然后在log文件夹下新建一个mongo.log:
然后将E:\MongoDB\bin
添加到环境变量path
中,此时打开cmd
窗口运行一下mongo
命令,出现如下情况:
这是为什么呢?这是因为我们还没有启动MongoDB
服务,自然也就连接不上服务了。那要怎么启动呢?在cmd
窗口中运行如下命令:
mongod --dbpath E:\MongoDB\data\db
需要注意的是:如果你没有提前创建db
文件夹,是无法启动成功的。运行成功之后,我们打开浏览器,输入127.0.0.1:27017
,看到如下图,就说明MongoDB
服务已经成功启动了。
但是如果每次都要这么启动服务的话也太麻烦了吧,这里你可以选择设置成开机自启动,也可以选择用命令net start mongodb
来手动启动,这里我选择使用后者,具体方法如下。
还是打开cmd
窗口,不过这次是以管理员身份运行,然后输入如下命令:
mongod --dbpath "E:\MongoDB\data\db" --logpath "E:\MongoDB\data\log\mongo.log" -install -serviceName "MongoDB"
如果没有报错的话就说明成功添加到服务里了,可以使用win+R
然后输入services.msc
命令进行查看:
默认是自动运行的,这里我选择把它改成手动的。然后在cmd
窗口中运行net start mongodb
:
怎么解决呢?两个步骤:
-
运行
sc delete mongodb
删除服务; -
再运行一次配置服务的命令:
mongod --dbpath "E:\MongoDB\data\db" --logpath "E:\MongoDB\data\log\mongo.log" -install -serviceName "MongoDB"
然后再运行net start mongodb
,服务启动成功:
1.3.1可能遇到的问题
- mongod不是内部或外部命令
- 出现这种问题说明你没有把bin目录添加到环境变量之中,重新添加一下即可解决。
- 服务名无效
- 首先是看你输入的服务名称是否有误,然后再查看本地服务中有没有MongoDB服务,如果没有服务,则运行命令添加服务即可。
- 发生服务特定错误:100
- 删除db文件夹下的mongod.lock和storage.bson两个文件,若删除完之后仍然出现这种问题,用sc delete mongodb删除服务,再配置一下服务就能解决了。
1.4安装一盒可视化工具
官网下载
RoBo 3T(Robomongo is now Robo 3T)
下载地址:
https://robomongo.org/download
打开后,有一个填信息的页面,name
、email
,暂时不用管,直接finish
。
启动MongoDB
服务。
点击弹出框中的create
,创建新连接,可以修改连接名name
,连接IP
(下图IP
为本地IP
),端口(默认)
连接成功后,右击localhost
,选择create Database
,创建数据库
创建数据库firstTest
,然后右击firstTest
,选择open Shell
,开始进行shell
命令来创建数据库中的集合和文档。
2.Linux安装MongoDB
2.1下载MongoDB
官方下载地址:
https://www.mongodb.com/download-center/community
bash
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.2.1.tgz
2.2解压安装
bash
tar -zxvf mongodb-linux-x86_64-rhel70-4.2.1.tgz
mkdir -p /usr/local/mongo
mv mongodb-linux-x86_64-rhel70-4.2.1/* /usr/local/mongo/
#创建目录/usr/local/mongo,并将解压完的mongodb目录移动到/usr/local/mongo下
mkdir -p /usr/local/mongo/data/db #数据库目录
mkdir -p /usr/local/mongo/logs #日志目录
mkdir -p /usr/local/mongo/conf #配置文件目录
mkdir -p /usr/local/mongo/pids #进程描述文件目录
#切到/usr/local/mongo目录下,创建目录
2.3增加配置
bash
vim /usr/local/mongo/conf/mongo.conf
#在conf目录,增加配置文件mongo.conf
#数据保存路径
dbpath=/usr/local/mongo/data/db/
#日志保存路径
logpath=/usr/local/mongo/logs/mongo.log
#进程描述文件
pidfilepath=/usr/local/mongo/pids/mongo.pid
#日志追加写入
logappend=true
bind_ip_all=true
#mongo默认端口
port=27017
#操作日志容量
oplogSize=10000
#开启子进程
fork=true
2.4通过配置文件启动
bash
/usr/local/mongo/bin/mongod -f /usr/local/mongo/conf/mongo.conf
2.5启动客户端
bash
/usr/local/mongo/bin/mongo --host 127.0.0.1 --port 27017
至此,安装完成
三、MongoDB基本操作及增删改查
1.基本操作
1.1登录数据库
bash
/usr/local/bin/mongo/mongo
1.2查看数据库
bash
show databases
1.3选择数据库
bash
use 数据库名
如果切换到一个没有的数据库,例如
use admin2
,那么会隐式创建这个数据库。(后期当该数据库有数据时,系统自动创建)
1.4查看集合
sql
show collections
1.5创建集合
sql
db.createCollection('集合名')
1.6删除集合
sql
`db.集合名.drop()`
1.7删除数据库
通过use
语法选择数据
通过db.dropDataBase()
删除数据库
2.增删改查
2.1增
sql
db.集合名.insert(JSON数据)
如果集合存在,那么直接插入数据。如果集合不存在,那么会隐式创建。
示例:在test2
数据库的c1
集合中插入数据(姓名叫webopenfather
年龄18
岁)
sql
use test2 db.c1.insert({name:"wyb",age:18})
- 数据库和集合不存在都隐式创建
- 对象的键统一不加引号(方便看),但是查看集合数据时系统会自动加
mongodb
会给每条数据增加一个全球唯一的_id
键
_id键的组成
自己增加_id,只需要给插入的JSON数据增加_id键即可覆盖(但实际不推荐)
sql
db.c1.insert({_id:1, name:"web", age:18})
2.1.1一次性插入多条数据
传递数据,数组中写一个个JSON
数据即可
sql
db.c1.insert([ {name:"cxk", age:3}, {name:"wyb", age:4}, {name:"zs", age:5} ])
2.1.2快速插入10条数据
由于mongodb
底层使用JS
引擎实现的,所以支持部分js
语法。因此:可以写for
循环
sql
for (var i=1; i<=10; i++) { db.c2.insert({anme: "a"+i, age: i}) }
2.2删
sql
db.集合名.remove(条件[,是否删除一条])
- 是否删除一条
true
:是(删除的数据为第一条)false
:否
sql
db.c1.remove({name:"cxk"})
2.3改
sql
db.集合名.update(条件,新数据[是否新增,是否修改多条,])
- 新数据此数据需要使用修改器,如果不使用,那么会将新数据替换原来的数据。1db.集合名.update(条件,{修改器:{键:值}}[是否新增,是否修改多条,]) 修改器作用inc递增rename重命名列set修改列值unset删除列
- 是否新增,指条件匹配不到数据则插入(true是插入,false否不插入默认) db.c3.update({uname:"zs30"},{$set:{age:30}},true)
- 是否修改多条,指将匹配成功的数据都修改(
true
是,false
否默认)db.c3.update({uname:"zs2"},{$set:{age:30}},false,true)
2.4查询文档
sql
db.集合名.find(条件[,查询的列])
条件 | 写法 |
---|---|
查询所有的数据 | {}或者不写 |
查询age=5的数据 | {age:6} |
查询条件为age=6且性别为男的数据 | {age:6,sex:"男"} |
查询的列(可选参数) | 写法 |
---|---|
查询全部列(字段) | 不写 |
只显示age列(字段) | {age:1} |
除了age列(字段)以外的其他所有字段 | {age:0} |
2.5其他语法
sql
db.集合名.find({
键:{运算符:值}
})
运算符 | 作用 |
---|---|
$gt | 大于 |
$gte | 大于等于 |
$lt | 小于 |
$lte | 小于等于 |
$ne | 不等于 |
$in | in |
$nin | not in |
实例语法 | 含义 |
---|---|
db.c1.find({age:{$gt:5}}) | 查询年龄大于5 的数据 |
db.c2.find({age:{$in:[5,8,10]}}) | 查询年龄是5 岁、8 岁、10 岁的数据 |
2.6总结
高级开发攻城狮统称:所有数据库都需要增删改查CURD
标识
sql
增 Create
db.集合名.insert(JSON数据)
删 Remove/Delete
db.集合名.remove(条件 [,是否删除一条true是false否默认])
也就是默认删除多条
改 Update
db.集合名.update(条件, 新数据 [,是否新增,是否修改多条])
升级语法db.集合名.update(条件,{修改器:{键:值}})
查 Read
db.集合名.find(条件 [,查询的列])
四、MongoDB存储数据类型
MongoDB中每条记录称作一个文档,这个文档和我们平时用的JSON有点像,但也不完全一样。JSON是一种轻量级的数据交换格式。简洁和清晰的层次结构使得JSON成为理想的数据交换语言,JSON易于阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率,但是JSON也有它的局限性,比如它只有null、布尔、数字、字符串、数组和对象这几种数据类型,没有日期类型,只有一种数字类型,无法区分浮点数和整数,也没法表示正则表达式或者函数。由于这些局限性,BSON闪亮登场啦,BSON是一种类JSON的二进制形式的存储格式,简称Binary JSON,它和JSON一样,支持内嵌的文档对象和数组对象,但是BSON有JSON没有的一些数据类型,如Date和BinData类型,MongoDB使用BSON做为文档数据存储和网络传输格式。
1.数字
shell
默认使用64
位浮点型数值,如下:
sql
db.sang_collec.insert({x:3.1415926})
db.sang_collec.insert({x:3})
对于整型值,我们可以使用NumberInt
或者NumberLong
表示,如下:
sql
db.sang_collec.insert({x:NumberInt(10)})
db.sang_collec.insert({x:NumberLong(12)})
2.字符串
字符串也可以直接存储,如下:
sql
db.sang_collec.insert({x:"hello MongoDB!"})
3.正则表达式
正则表达式主要用在查询里边,查询时我们可以使用正则表达式,语法和JavaScript
中正则表达式的语法相同,比如查询所有key
为x
,value
以hello
开始的文档且不区分大小写:
sql
db.sang_collec.find({x:/^(hello)(.[a-zA-Z0-9])+/i})
4.数组
数组一样也是被支持的,如下:
sql
db.sang_collec.insert({x:[1,2,3,4,new Date()]})
数组中的数据类型可以是多种多样的。
5.日期
MongoDB
支持Date
类型的数据,可以直接new
一个Date
对象,如下:
sql
db.sang_collec.insert({x:new Date()})
6.内嵌文档
一个文档也可以作为另一个文档的value
,这个其实很好理解,如下:
sql
db.sang_collect.insert({name:"三国演义",author:{name:"罗贯中",age:99}});
五、MongoDB中的索引
1.索引创建
默认情况下,集合中的_id
字段就是索引,我们可以通过getIndexes()
方法来查看一个集合中的索引:
sql
db.sang_collect.getIndexes()
sql
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "sang.sang_collect"
}
]
我们看到这里只有一个索引,就是
_id
。
现在我的集合中有10000
个文档,我想要查询x
为1
的文档,我的查询操作如下:
sql
db.sang_collect.find({x:1})
这种查询默认情况下会做全表扫描,我们可以用上篇文章介绍的explain()
来查看一下查询计划,如下:
sql
db.sang_collect.find({x:1}).explain("executionStats")
结果如下:
sql
{
"queryPlanner" : {
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1,
"executionTimeMillis" : 15,
"totalKeysExamined" : 0,
"totalDocsExamined" : 10000,
"executionStages" : {
"stage" : "COLLSCAN",
"filter" : {
"x" : {
"$eq" : 1.0
}
},
"nReturned" : 1,
"executionTimeMillisEstimate" : 29,
"works" : 10002,
"advanced" : 1,
"needTime" : 10000,
"needYield" : 0,
"saveState" : 78,
"restoreState" : 78,
"isEOF" : 1,
"invalidates" : 0,
"direction" : "forward",
"docsExamined" : 10000
}
},
"serverInfo" : {
},
"ok" : 1.0
}
结果比较长,我摘取了关键的一部分。我们可以看到查询方式是全表扫描,一共扫描了10000
个文档才查出来我要的结果。实际上我要的文档就排第二个,但是系统不知道这个集合中一共有多少个x
为1
的文档,所以会把全表扫描完,这种方式当然很低效,但是如果我加上 limit
,如下:
sql
db.sang_collect.find({x:1}).limit(1)
此时再看查询计划发现只扫描了两个文档就有结果了,但是如果我要查询x
为9999
的记录,那还是得把全表扫描一遍,此时,我们就可以给该字段建立索引,索引建立方式如下:
sql
db.sang_collect.ensureIndex({x:1})
1表示升序,-1表示降序。当我们给x字段建立索引之后,再根据x字段去查询,速度就非常快了,我们看下面这个查询操作的执行计划:
sql
db.sang_collect.find({x:9999}).explain("executionStats")
这个查询计划过长我就不贴出来了,我们可以重点关注查询要耗费的时间大幅度下降。
此时调用getIndexes()
方法可以看到我们刚刚创建的索引,如下:
sql
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "sang.sang_collect"
},
{
"v" : 2,
"key" : {
"x" : 1.0
},
"name" : "x_1",
"ns" : "sang.sang_collect"
}
]
我们看到每个索引都有一个名字,默认的索引名字为字段名_排序值,当然我们也可以在创建索引时自定义索引名字,如下:
sql
db.sang_collect.ensureIndex({x:1},{name:"myfirstindex"})
此时创建好的索引如下:
sql
{
"v" : 2,
"key" : {
"x" : 1.0
},
"name" : "myfirstindex",
"ns" : "sang.sang_collect"
}
当然索引在创建的过程中还有许多其他可选参数,如下:
sql
db.sang_collect.ensureIndex({x:1},{name:"myfirstindex",dropDups:true,background:true,unique:true,sparse:true,v:1,weights:99999})
1.name表示索引的名称
2.dropDups表示创建唯一性索引时如果出现重复,则将重复的删除,只保留第一个
3.background是否在后台创建索引,在后台创建索引不影响数据库当前的操作,默认为false
4.unique是否创建唯一索引,默认false
5.sparse对文档中不存在的字段是否不起用索引,默认false
6.v表示索引的版本号,默认为2
7.weights表示索引的权重
此时创建好的索引如下:
sql
{
"v" : 1,
"unique" : true,
"key" : {
"x" : 1.0
},
"name" : "myfirstindex",
"ns" : "sang.sang_collect",
"background" : true,
"sparse" : true,
"weights" : 99999.0
}
2.查看索引
getIndexes()
可以用来查看索引,我们还可以通过totalIndexSize()
来查看索引的大小,如下:
sql
db.sang_collect.totalIndexSize()
3.删除索引
我们可以按名称删除索引,如下:
sql
db.sang_collect.dropIndex("xIndex")
表示删除一个名为xIndex
的索引,当然我们也可以删除所有索引,如下:
sql
db.sang_collect.dropIndexes()
4.总结
索引是个好东西,可以有效的提高查询速度,但是索引会降低插入、更新和删除的速度,因为这些操作不仅要更新文档,还要更新索引,MongoDB
限制每个集合上最多有64
个索引,我们在创建索引时要仔细斟酌索引的字段。