MongoDB入门笔记

慕课网学习笔记。
学习视频地址:https://www.imooc.com/learn/295
教程版本为2.x,部分API已经被废弃或处于别名状态,但功能类似,可供参考。
我在学习中使用的为4.0.2,没有遇到问题。


引言

Sql数据库:关系型数据库,如Qracle,Mysql等。但是其设计模式在现在的互联网模式下存在一些弊端。例如,其表结构使得数据库的横向扩充存在限制。
NoSql数据库:Redis,MongoDB

一: MongoDB概念

1. 为什么是选择MongoDB
  • 无数据结构的限制
    没有表结构的概念,每条记录可以有不同的结构。
    业务开发方便。
    不需要像sql数据库那样,事先定义表结构。

  • 完全的索引支持
    redis存储为key-value对,只支持按键值查询。
    hbase为单索引,二级索引需要自己实现。
    MongoDB支持单键索引,数据索引,全文索引等。

  • 方便的冗余和扩展
    复制集保证了数据的安全性
    分片扩展数据规模

分片技术提供了自动数据均衡,对数据库的同一入口。不需要人为的在应用层进行访问的分发,较少了很多人工操作。

  • 良好的支持
    完整的文档。
    齐全的驱动支持。
2. 名词说明
sql中的概念 mongo中的概念 说明
database database 数据库
table collection 数据库表/集合
row document 数据记录行/文档
column field 数据字段/域
index index 索引
table joins 表连接,MongoDB不支持
primary key primary key 主键,MongoDB自动将_id字段设置为主键

二: 搭建MongoDB

1. 目录结构

mongod数据库部署工具
mongo文件为客户端链接工具
mongoimport和mongoexport用于数据的导入导出
mongodump和mongorestore用于数据的二进制导入导出,用于数据的备份。
mongooplog用于数据库的操作记录
mongostat用于查看数据库状态

2. 搭建服务器

参考:https://www.runoob.com/mongodb/mongodb-linux-install.html

通过wget下载tgz包并解压,目录如下:

MongoDB所需要的目录在安装过程不会自动创建,所以你需要手动创建data目录,log目录和conf目录。

mkdir log
mkdir -p data/db
mkdir conf

然后在conf目录下创建mongodb.conf配置文件,添加如下内容:

dbpath = data
logpath = logs/mongod.log
storageEngine=mmapv1
fork = true
bind_ip = 0.0.0.0
fork 指明该服务为后台启动。
bind_ip 0.0.0.0 表明允许所有ip访问

启动时指定配置文件

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

这里补充一个问题,我在执行上述指令的时候出现了报错:
error while loading shared libraries: libcurl.so.4: cannot open shared object file: No such file or directory

然后我查看了一下依赖:

需要安装一下:libcurl4-openssl-dev
然后启动成功:

之后可以从其它主机进行访问:

三: MongoDB的基本使用

主要设计mongoDB的增删查改。

首先通过客户端连接mongoDB

./bin/mongo ip:port
如果为本地直接使用:./bin/mongo

连接成功:

1. 数据库的增删改查
  • 查看已有数据库:
    show dbs
  • 切换数据库:
    use ***
  • 删除当前库:
    db.dropDatabase()

如果使用use时,所指定的db并不存在,那么将自动生成相应的DB

2. 数据的增删改查
  • 插入数据
    通过use example 创建了db:example
    通过db.表名.insert({json})创建了记录。


  • 通过js语法进行操作:

  • 在对应表中查询数据
    db.表名.find(条件)
  • 对find结果进行设置:
    count()统计了总数
    skip()跳过了表中的前x项
    limit()设置返回的条目数
    sort()设置根据哪个字段排序
  • 更新:
--全更新
> db.example_collection.update({x:1},{x:999})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.example_collection.find({x:999})
{ "_id" : ObjectId("5bbd91ce0980ab8f024199ac"), "x" : 999 }

> db.example_collection.insert({x:123,y:223,z:323})
WriteResult({ "nInserted" : 1 })
--部分更新,需要使用$set:
> db.example_collection.update({x:123},{$set:{z:999}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.example_collection.find({x:123})
{ "_id" : ObjectId("5bbd95e00980ab8f02419a11"), "x" : 123, "y" : 223, "z" : 999 }

当我们更新一条不存在的内容时,可以通过在update后面加true令其自动创建。

mongodb的update每次只对一条数据生效,当需要批量更新时,语法如下:

  • 删除
    删除默认是删除所有匹配到的数据,与更新不同。
3. 数据表的增删改查
  • 增加
    当使用db.表名.insert()时,会根据表名自动创建表。

  • 删表:
    db.表名.drop()

四: 不同类型索引的创建与使用

用索引来优化大规模数据时的查询效率。

1. 索引的基本操作
  • 查看集合的索引情况:
> db.example_collection.getIndexes()
[
    {
        "v" : 2,
        "key" : {
            "_id" : 1
        },
        "name" : "_id_",
        "ns" : "example.example_collection"
    }
]
  • 创建索引
    1为正向索引,-1为负向索引。
    需要说明的是:索引一般在使用数据库前就创建完毕,若是数据库中已有大量数据,这时创建索引可能会出现错误。
> db.example_collection.ensureIndex({x:1})
{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 2,
    "ok" : 1
}

2. 索引的种类与使用
  • _id索引
    绝大多数集合默认建立的索引,每当插入数据的时候,MongoDB会自动的生成一条唯一的_id字段。

  • 单键索引
    指定某个字段作为索引。其值为单一的值

> db.example_collection.ensureIndex({x:1})
{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 2,
    "ok" : 1
}

  • 多键索引
    指定某个字段作为索引。其值有多个记录,例如数组。

对于该条数据,mongoDB认为上文创建了一个多键索引。

  • 复合索引
    当查询条件不止一个时,就需要建立复合索引。
  • 过期索引
    是在一段时间后会过期的索引。
    在索引过期后,相应的数据会被删除。
    建立方法如下:

过期索引字段的值必须是时间类型,为ISODate或者ISODate数组。
不能是时间戳。
插入时间类型的方式如下:

 db.example_collection.insert({time:new Date()})

如果索引字段指定的是数组,则根据最早过期的时间进行删除。
过期索引不能是复合索引。

最小时间间隔为60秒,因为服务60s运行一次输出操作。

  • 全文索引
    比较特殊的索引,在后文中详细介绍

  • 地理位置索引
    比较特殊的索引,在后文中详细介绍

3. 全文索引

全文索引,对字符串与字符串数组创建全文可搜索的索引。
例如,我们有三个字段:{author:"",title:"",article:""}
我们希望关键字去匹配author、title和article三者的内容。

建立方法:
单个字段:
db.articles.ensureIndex({key:"text"})

多个字段:
db.articles.ensureIndex({key:"text",key2:"text"})

任意字段:
db.articles.ensureIndex({"$**":"text"})

每个数据集合只允许创建一个数据索引

使用方法:
查找包含coffee字段的数据
> db.article.find({$text:{$search:"coffee"}})

查找包含coffee字段或fee字段的数据
> db.article.find({$text:{$search:"coffee fee"}})

查找包含coffee字段且不包含fee字段的数据
> db.article.find({$text:{$search:"coffee -fee"}})

通过\个特殊字符进行转义,“”包含的内容是必须被匹配上的。
> db.article.find({$text:{$search:" \"america\" coffee"}})
全文匹配的相似度

$meta操作符:{score:{$meta:"textScore"}}
写在查询条件后可以返回反馈结果的相似度,一般与sort一起使用。

db.example_collection.find({$text:{$search:"1"}},{score:{$meta:"textScore"}})

根据score进行排序:
db.example_collection.find({$text:{$search:"1"}},{score:{$meta:"textScore"}}).sort({score:{$meta:"textScore"}})

全文索引的使用限制

每次查询只能使用一个$text查询
$text查询不能出现在$nor查询中
查询包含$text后,不能使用hint指定索引。

4. 地理位置索引

将一些点的位置存储在MongoDB中,创建索引后,可以按照位置来查找其他点。

分为:

  • 2d索引:用于平面查找。
  • 2dsphere索引:用于球面查找。

查找方式:

  • 查找距离某点一定范围内的点。
  • 查找包含在某个区域内的点。
2d索引的使用

创建:

> db.location.ensureIndex({"w":"2d"})
{
    "createdCollectionAutomatically" : true,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 2,
    "ok" : 1
}

位置表示方式:经纬度[j经度,纬度]
取值范围:经度[-180,180],纬度[-90,90]

插入了如下点:

查询方法:

  • $near查询:查找最近的点。
near查询默认返回100个点。:

> db.location.find({w:{$near:[1,1]}})
{ "_id" : ObjectId("5bbde1fd7b9cb8b60db8f0ac"), "w" : [ 1, 1 ] }
{ "_id" : ObjectId("5bbde20a7b9cb8b60db8f0ad"), "w" : [ 1, 2 ] }
{ "_id" : ObjectId("5bbde20e7b9cb8b60db8f0ae"), "w" : [ 3, 2 ] }
{ "_id" : ObjectId("5bbde2747b9cb8b60db8f0b0"), "w" : [ 100, 20 ] }
{ "_id" : ObjectId("5bbde27d7b9cb8b60db8f0b1"), "w" : [ 180, 80 ] }

通过maxDistance进行最大距离的限定。

> db.location.find({w:{$near:[1,1],$maxDistance:10}})
{ "_id" : ObjectId("5bbde1fd7b9cb8b60db8f0ac"), "w" : [ 1, 1 ] }
{ "_id" : ObjectId("5bbde20a7b9cb8b60db8f0ad"), "w" : [ 1, 2 ] }
{ "_id" : ObjectId("5bbde20e7b9cb8b60db8f0ae"), "w" : [ 3, 2 ] }
  • $geoWithin查询:查找某个形状内的点。

例如:

  • $geoNear查询:near查询的进化版,可以使用更多的参数

使用实例:

> db.runCommand({geoNear:"location",near:[1,2],maxDistance:10,num:1})
{
    "results" : [
        {
            "dis" : 0,
            "obj" : {
                "_id" : ObjectId("5bbde20a7b9cb8b60db8f0ad"),
                "w" : [
                    1,
                    2
                ]
            }
        }
    ],
    "stats" : {
        "nscanned" : 3,
        "objectsLoaded" : 1,
        "avgDistance" : 0,
        "maxDistance" : 0,
        "time" : 690
    },
    "ok" : 1
}

可以看到,返回的结果更加丰富,可以用于支持更多的功能。

2dsphere索引

创建:

> db.location.ensureIndex({"w":"2dsphere"})

位置表示方式:描述一个点,一条直线,多边形等形状。
格式为:{type:" ",coordinates:[<coordinates>]}
例如:

db.location.insert( { w: { type: "Point", coordinates: [ 120.31, 30.21] }, title: "location"})

除了支持之前的查询方式外,还支持查询两个多边形的交叉点。

5. 索引属性:
name:

在建立索引的时候可以对索引进行命名:
db.example_collection.ensureIndex({x:1,y:1},{name:"example_x_y"})

使用名称可以更方便的删除索引:

db.example_collection.dropIndex("example_x_y")

unique,唯一性

db.example_collection.ensureIndex({x:1,y:1},{unique:true/false})

设置了unique的索引,表明该索引为唯一索引。不允许在同一集合中,插入两条具有相同唯一索引的字段的数据。

例如设置了x,y为唯一索引。
那么,不能插入x,y值都一样的数据。

sparse,稀疏性

db.example_collection.ensureIndex({x:1,y:1},{sparse:true/false}),默认是稀疏的。
设置稀疏意义在于,在你对于x字段创建了索引后,如果插入了一条没有x字段的数据,那么不会对该条数据建立x索引。
同样,你不能在这个稀疏索引上针对x进行搜索。

五:简单运维

1. 索引构建情况分析

索引的有点:加快索引相关的查询。
索引的缺点:增加磁盘空间消耗,降低写入性能。

评判从四个角度进行:

2. 常见的一些工具指令
  • mongostat:查看mongodb运行状态的程序。
使用方法 :mongostat -h 127.0.0.1:27017

edwin@ubuntu:~/mongodb/mongodb-linux-x86_64-ubuntu1604-4.0.2$ ./bin/mongostat 
insert query update delete getmore command flushes mapped vsize   res faults qrw arw net_in net_out conn                time
    *0    *0     *0     *0       0     1|0       0        1.64G 79.0M      0 0|0 0|0   155b   31.2k    3 Oct 10 19:38:13.316
    *0    *0     *0     *0       0     2|0       0        1.64G 79.0M      0 0|0 0|0   162b   32.6k    3 Oct 10 19:38:14.286
    *0    *0     *0     *0       0     2|0       0        1.64G 79.0M      0 0|0 0|0   158b   31.6k    3 Oct 10 19:38:15.286
    *0    *0     *0     *0       0     1|0       0        1.64G 79.0M      0 0|0 0|0   157b   31.6k    3 Oct 10 19:38:16.286
    *0    *0     *0     *0       0     1|0       0        1.64G 79.0M      0 0|0 0|0   157b   31.5k    3 Oct 10 19:38:17.288
    *0    *0     *0     *0       0     2|0       0        1.64G 79.0M      0 0|0 0|0   158b   31.7k    3 Oct 10 19:38:18.286
    *0    *0     *0     *0       0     1|0       0        1.64G 79.0M      0 0|0 0|0   157b   31.6k    3 Oct 10 19:38:19.286
    *0    *0     *0     *0       0     1|0       0        1.64G 79.0M      0 0|0 0|0   157b   31.6k    3 Oct 10 19:38:20.286
    *0    *0     *0     *0       0     1|0       0        1.64G 79.0M      0 0|0 0|0   157b   31.6k    3 Oct 10 19:38:21.287
    *0    *0     *0     *0       0     2|0       0        1.64G 79.0M      0 0|0 0|0   158b   31.6k    3 Oct 10 19:38:22.285

其中,qrw为读写队列数,会很直观的影响mongodb的性能。

  • profile集合
db.getProfilingLevel()
该指令可以查看Profile级别,级别为0不会记录任何操作,级别为2时,记录所有操作。

> db.getProfilingStatus()
{ "was" : 0, "slowms" : 100, "sampleRate" : 1 }
当level为1时,配合slowms作为阈值,高于其则记录。

>  db.setProfilingLevel(2)
{ "was" : 0, "slowms" : 100, "sampleRate" : 1, "ok" : 1 }
> db.getProfilingLevel()
2
对level进行修改。

直接show tables无法看到该集合,但是对system.profile进行find()操作,可以看到所记录的内容。
字段含义可以查看文档:
https://docs.mongodb.com/manual/reference/database-profiler/

> show tables
example.location
example_collection
location
> db.system.profile.find().limit(1)
{ "op" : "insert", "ns" : "example.location", "command" : { "insert" : "location", "ordered" : true, "lsid" : { "id" : UUID("e535e60c-0998-46e4-8eb9-1443ca5fdfdc") }, "$db" : "example" }, "ninserted" : 1, "keysInserted" : 2, "numYield" : 0, "locks" : { "Global" : { "acquireCount" : { "r" : NumberLong(1), "w" : NumberLong(1) } }, "MMAPV1Journal" : { "acquireCount" : { "w" : NumberLong(2) } }, "Database" : { "acquireCount" : { "w" : NumberLong(1) } }, "Collection" : { "acquireCount" : { "W" : NumberLong(1) } } }, "responseLength" : 45, "protocol" : "op_msg", "millis" : 0, "ts" : ISODate("2018-10-11T03:06:57.484Z"), "client" : "127.0.0.1", "appName" : "MongoDB Shell", "allUsers" : [ ], "user" : "" }
> 

profile在生产环境中建议关闭,以为在操作频繁时,profile的读写会占用mongoDB大量的资源。

  • 日志

日志在配置文件中进行配置,在第二大章中有提到,在配置文件中添加verbose字段:

...
verbose = vvvvv
...

v越多,详细程度越高。

  • explain()

使用在指令之后,会显示出指令的详细信息。

> db.example_collection.find({x:1})
{ "_id" : ObjectId("5bbdb18b41b7cdd7f5ed175b"), "x" : [ 1, 2, 3, 4, 5, 6 ] }
> db.example_collection.find({x:1}).explain()
{
...
}

3. 安全性问题

一些基本的方面包括:
物理隔离与网络隔离
IP白名单
用户密码及权限

一般用户名密码及权限是最常规的方法。

开启权限认证
  • auto开启
    在配置文件中添加:
auth = true

一般先创建用户,再设置该条选项。
指令为createUser

结构为:


roles的内建类型为:
read:读权限
readWrite:读写权限
dbAdmin:db的管理权限,除了读写还可以创建索引。
dbOwner:db所以权限
userAdmin:对其它角色进行管理。

可以对权限进行组合,创建一个新的role。
示例如下:

> db.createUser({user:"edwin",pwd:"1234",roles:[{role:"userAdmin",db:"example"},{role:"read",db:"example"}]})
Successfully added user: {
    "user" : "edwin",
    "roles" : [
        {
            "role" : "userAdmin",
            "db" : "example"
        },
        {
            "role" : "read",
            "db" : "example"
        }
    ]
}

这时再通过直接登陆,在操作时会报错:

进行如下验证:

> use example
switched to db example
> db.auth("edwin","1234")
1
返回1说明成功

可以创建特殊的角色

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容