MongoDB 基础知识
一、简介
MongoDB 是一个基于分布式 文件存储的NoSQL数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。
MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
关系型数据库(SQLite、Oracle、mysql)特点
- 关系模型指的是二维表格模型
- 通用的SQL语言是的操作关系型数据库非常方便
- 固定的表结构
MongoDB 特点
- 模式自由 :可以把不同结构的文档存储在同一个数据库里
- 面向集合的存储:适合存储 JSON风格文件的形式,
- 完整的索引支持:对任何属性可索引,
- 复制和高可用性:支持服务器之间的数据复制,支持主-从模式及服务器之间的相互复制。复制的主要目的是提供冗余及自动故障转移。
- 自动分片:支持云级别的伸缩性:自动分片功能支持水平的数据库集群,可动态添加额外的机器。
- 丰富的查询:支持丰富的查询表达方式,查询指令使用JSON形式的标记,可轻易查询文档中的内嵌的对象及数组。
- 快速就地更新:查询优化器会分析查询表达式,并生成一个高效的查询计划。
- 高效的传统存储方式:支持二进制数据及大型对象(如照片或图片)
二、MongoDB 的基本操作
2.1 创建数据库
语法
MongoDB创建数据库的语法格式如下:
use DATABASE_NAME
如果数据库不存在,则指向数据库,但不创建(等待实际数据入库后创建),否则切换到指定的数据库。
示例
创建数据库
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
> use test
switched to db test
> db
test
>
查看当前数据库名称
>db
查看当前db中所有的collection
>show collections
刚创建的数据库 test 并不在数据库的列表中,要显示它,我们需要向 test 数据库插入一些数据
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
> db.message.insert({"name":"simon","msg":"hello"})
2018-12-01T13:38:28.225+0800 E QUERY [js] SyntaxError: illegal character @(shell):1:18
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
2.2 删除数据库
语法
MongoDB 删除数据库的语法格式如下:
db.dropDatabase()
删除当前数据库,默认为 test,你可以使用 db 命令查看当前数据库名
示例
查看所有数据库
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
mytest 0.000GB
> db
mytest
> show collections
message
删除当前数据库 mytest
> db.dropDatabase()
{ "dropped" : "mytest", "ok" : 1 }
检查数据库列表
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
2.3 创建集合
语法
基本的 createCollection() 命令语法如下:
db.createCollection(name, options)
在命令中, name 是要创建的集合的名称. Options 是一个文档,用于指定配置的集合
参数 | 类型 | 描述 |
---|---|---|
Name | String | 要创建的集合名称 |
Options | Document | (可选)指定有关内存大小和索引选项 |
选项参数是可选的,所以只需要到指定的集合名称。以下是可以使用的选项列表:
字段 | 类型 | 描述 |
---|---|---|
capped | Boolean | (可选)如果为true,则启用封顶集合。封顶集合是固定大小的集合,当它达到其最大大小,会自动覆盖最早的条目。如果指定true,则需要也指定size字段。 |
autoIndexID | Boolean | (可选)如果为true,自动创建索引_id字段, 默认值是false。 |
size | number | (可选)指定集合最大可使用字节。如果封顶如果是 true,那么你还需要指定这个字段。 |
max | number | (可选)指定封顶集合允许在文件的最大数量。Size限制优先于此限制。如果一个封顶集合达到大小size限制,未达到文件的最大数量,MongoDB删除旧的文件。如果您更喜欢使用max,确保为上限的集合所需的大小限制,足以包含文档的最大数量。 |
当插入文档,MongoDB 第一检查大小字段封顶集合,然后它会检查最大的字段。
示例
createCollection() 方法不使用选项的示例如下:
> use mytest
switched to db mytest
> db.createCollection("mycollection")
{ "ok" : 1 }
> show collections
mycollection
createCollection() 方法使用选项的示例如下:
> db.createCollection("log", { capped : true, size : 5242880, max : 5000 } )
{ "ok" : 1 }
> show collections
log
mycollection
可以不需要创建集合。当插入一些文档自动创建的集合。
2.4 删除集合
语法
drop() 命令的基本语法如下
db.COLLECTION_NAME.drop()
示例
现在删除集合名称为 mycollection
> show collections
log
mycollection
> db.mycollection.drop()
true
> show collections
log
drop() 方法将返回 true,如果选择成功收集被丢弃,否则将返回 false
2.5 插入文档
要插入数据到 MongoDB 集合,需要使用 MongoDB 的 insert() 或 save() 方法
语法
insert() 命令的基本语法如下:
db.COLLECTION_NAME.insert(document)
save() 命令的基本语法如下:
db.COLLECTION_NAME.save(document)
示例
执行下面的命令插入文档
db.mycol.insert(
[
{
title: 'MongoDB Overview',
description: 'MongoDB is no sql database',
by: 'tutorials itcast',
url: 'http://www.itcast.cn',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
},
{
title: 'MySQL Overview',
description: 'MySQL is sql database',
by: 'tutorials itcast',
url: 'http://www.itcast.cn',
tags: ['MySQL', 'database', 'SQL'],
likes: 40
}
]
)
插入文档中,如果我们不指定_id参数,然后MongoDB 本文档分配一个唯一的ObjectId。
> db.mycol.find().pretty()
{
"_id" : ObjectId("5c022988c6e6fc84a60212c1"),
"title" : "MongoDB Overview",
"description" : "MongoDB is no sql database",
"by" : "tutorials itcast",
"url" : "http://www.itcast.cn",
"tags" : [
"mongodb",
"database",
"NoSQL"
],
"likes" : 100
}
{
"_id" : ObjectId("5c0229c7c6e6fc84a60212c2"),
"title" : "MongoDB Overview",
"description" : "MongoDB is no sql database",
"by" : "tutorials itcast",
"url" : "http://www.itcast.cn",
"tags" : [
"mongodb",
"database",
"NoSQL"
],
"likes" : 100
}
_id
是12个字节的十六进制数,唯一一个集合中的每个文档。 12个字节被划分如下:
_id: ObjectId(4 bytes timestamp, 3 bytes machine id, 2 bytes process id, 3 bytes incrementer)
2.6 查询文档
语法
基本的find()方法语法如下:
db.COLLECTION_NAME.find()
示例
> db.mycol.find().pretty()
{
"_id" : ObjectId("5c022988c6e6fc84a60212c1"),
"title" : "MongoDB Overview",
"description" : "MongoDB is no sql database",
"by" : "tutorials itcast",
"url" : "http://www.itcast.cn",
"tags" : [
"mongodb",
"database",
"NoSQL"
],
"likes" : 100
}
{
"_id" : ObjectId("5c0229c7c6e6fc84a60212c2"),
"title" : "MongoDB Overview",
"description" : "MongoDB is no sql database",
"by" : "tutorials itcast",
"url" : "http://www.itcast.cn",
"tags" : [
"mongodb",
"database",
"NoSQL"
],
"likes" : 100
}
结果显示在一个格式化的方式,可以使用 pretty() 方法。
操作 | 语法 | 例子 |
---|---|---|
Equality | `{<key>:<value>}`` | db.mycol.find({"by":"tutorials itcast"}).pretty() |
Less Than | {<key>:{$lt:<value>}} |
db.mycol.find({"likes":{$lt:50}}).pretty() |
Less Than Equals | {<key>:{$lte:<value>}} |
db.mycol.find({"likes":{$lte:50}}).pretty() |
Greater Than | {<key>:{$gt:<value>}}
|
db.mycol.find({"likes":{$gt:50}}).pretty() |
Greater Than Equals | {<key>:{$gte:<value>}} |
db.mycol.find({"likes":{$gte:50}}).pretty() |
Not Equals | {<key>:{$ne:<value>}} |
db.mycol.find({"likes":{$ne:50}}).pretty() |
AND 在MongoDB中用法:
语法
在 find() 方法,如果通过多个键分离',',那么 MongoDB 处理 AND 条件。AND 基本语法如下所示:
db.mycol.find({key1:value1, key2:value2}).pretty()
示例
> db.mycol.find({"by":"tutorials itcast","title": "MongoDB Overview"}).pretty()
{
"_id" : ObjectId("5799c3eb235910620b89c674"),
"title" : "MongoDB Overview",
"description" : "MongoDB is no sql database",
"by" : "tutorials itcast",
"url" : "http://www.itcast.cn",
"tags" : [
"mongodb",
"database",
"NoSQL"
],
"likes" : 100
}
>
对于上面给出的例子相当于where子句 where by='tutorials itcast' AND title='MongoDB Overview',可以通过任意数量的键值对在 find 子句。
OR 在MongoDB的用法
语法
OR条件的基础上要查询文件,需要使用$or关键字。OR 基本语法如下所示:
>db.mycol.find(
{
$or: [
{key1: value1}, {key2:value2}
]
}
).pretty()
示例
下面给出的例子将显示所有的教程,标题是“MongoDB Overview'或'MySQL Overview'
>db.mycol.find({
$or: [
{
"title": "MySQL Overview"
},
{
"title": "MongoDB Overview"
}
]
}).pretty()
{
"_id" : ObjectId("57cefded600d5bb1e2acdb70"),
"title" : "MongoDB Overview",
"description" : "MongoDB is no sql database",
"by" : "tutorials itcast",
"url" : "http://www.itcast.cn",
"tags" : [
"mongodb",
"database",
"NoSQL"
],
"likes" : 100
}
{
"_id" : ObjectId("57cefdf7600d5bb1e2acdb71"),
"title" : "MySQL Overview",
"description" : "MySQL is sql database",
"by" : "tutorials itcast",
"url" : "http://www.itcast.cn",
"tags" : [
"MySQL",
"database",
"SQL"
],
"likes" : 40
}
>
AND 和 OR 一起使用
示例
面给出的例子将显示 喜爱数高于50,其标题是MongoDB Overview或者是itcast所写 。等效于 SQL where子句 为 where likes>10 AND (by = 'itcast' OR title = 'MongoDB Overview')
db.mycol.find({
"likes": {
$gt: 50
},
$or: [
{
"by": "tutorials itcast"
},
{
"title": "MongoDB Overview"
}
]
}).pretty()
{
"_id" : ObjectId("57cefded600d5bb1e2acdb70"),
"title" : "MongoDB Overview",
"description" : "MongoDB is no sql database",
"by" : "tutorials itcast",
"url" : "http://www.itcast.cn",
"tags" : [
"mongodb",
"database",
"NoSQL"
],
"likes" : 100
}
>
2.7 更新文档
MongoDB 使用 update() 和 save() 方法来更新集合中的文档。接下来让我们详细来看下两个函数的应用及其区别。
2.7.1 update() 更新文档
语法
update() 方法的基本语法如下:
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)
参数说明:
- query : update的查询条件,类似sql update查询内where后面的。
- update : update的对象和一些更新的操作符(如inc...)等,也可以理解为sql update查询内set后面的
- upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
- multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
- writeConcern :可选,抛出异常的级别。
示例
下面的例子将设置标题MongoDB Overview的文件,更新其标题是New MongoDB Tutorial
> db.mycol.update({
'title': 'MongoDB Overview'
},
{
$set: {
'title': 'New MongoDB Tutorial'
}
})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.mycol.find().pretty()
{
"_id" : ObjectId("5799c3eb235910620b89c674"),
"title" : "New MongoDB Tutorial",
"description" : "MongoDB is no sql database",
"by" : "tutorials itcast",
"url" : "http://www.itcast.cn",
"tags" : [
"mongodb",
"database",
"NoSQL"
],
"likes" : 100
}
{
"_id" : ObjectId("5799c3f3235910620b89c675"),
"title" : "MySQL Overview",
"description" : "MySQL is sql database",
"by" : "tutorials itcast",
"url" : "http://www.itcast.cn",
"tags" : [
"MySQL",
"database",
"SQL"
],
"likes" : 40
}
MongoDB默认将只更新单一的文件,来更新多个你需要设置参数置multi为true
>db.mycol.update({'by':'tutorials itcast'},{$set:{'by':'itcast'}},{multi:true})
2.7.2 save() 更新文档
save() 方法覆盖原有的文档 或者 插入新的文档
语法
MongoDB 的 save() 方法的基本语法如下:
db.collection.save(
<document>,
{
writeConcern: <document>
}
)
参数说明:
document : 要存储的文档数据。
writeConcern :可选,抛出异常的级别。
例子
下面的例子将取代文件具有_id为 '5799c3eb235910620b89c674'
>db.mycol.save(
{
"_id" : ObjectId("5799c3eb235910620b89c674"),
"title":"itcast New Topic",
"by":"itcast"
}
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.mycol.find().pretty()
{
"_id" : ObjectId("5799c3eb235910620b89c674"),
"title" : "itcast New Topic",
"by" : "itcast"
}
{
"_id" : ObjectId("5799c3f3235910620b89c675"),
"title" : "MySQL Overview",
"description" : "MySQL is sql database",
"by" : "tutorials itcast",
"url" : "http://www.itcast.cn",
"tags" : [
"MySQL",
"database",
"SQL"
],
"likes" : 40
}}
>
2.8 删除文档
语法
MongoDB 删除文档的函数是remove()
db.collection.remove(
<query>,
{
justOne: <boolean>,
writeConcern: <document>
}
)
参数说明:
- justOne : (可选)如果设为 true 或 1,则只删除一个文档。默认false
- writeConcern :(可选)抛出异常的级别。
例子
考虑以下数据myLimit集合:
> db.myLimit.insert(
[
{ "_id" : 0 },
{ "_id" : 1 },
{ "_id" : 2 },
{ "_id" : 3 },
{ "_id" : 4 }
]
)
> db.myLimit.find()
{ "_id" : 0 }
{ "_id" : 1 }
{ "_id" : 2 }
{ "_id" : 3 }
{ "_id" : 4 }
下面的例子将删除所有的文档,其_id
是 3
> db.myLimit.remove({"_id" : 3})
WriteResult({ "nRemoved" : 1 })
> db.myLimit.find()
{ "_id" : 0 }
{ "_id" : 1 }
{ "_id" : 2 }
{ "_id" : 4 }
如果有多个记录且要删除的只有第一条记录,那么设置remove()方法中justOne参数设置1或者是true
>db.myLimit.remove({"_id" : {$gt:0}},1)
WriteResult({ "nRemoved" : 1 })
如果不指定删除条件,然后MongoDB将从集合中删除整个文档。这相当于SQL的truncate命令。
> db.myLimit.remove({})
WriteResult({ "nRemoved" : 3 })
2.8 索引
索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文档并选取那些符合查询条件的记录,花费的时间会很多
语法
ensureIndex()方法基本语法格式如下所示:
db.COLLECTION_NAME.ensureIndex({KEY:1})
语法中 Key 值为你要创建的索引字段,1为指定按升序创建索引,-1即为降序。
MongoDB 提供了一个 explain 命令让我们获知系统如何处理查询请求。利用 explain 命令,可以很好地观察系统如何使用索引来加快检索,同时可以针对性优化索引。
示例
1. 插入测试数据
db.inventory.insert([
{ "_id" : 1, "item" : "f1", type: "food", quantity: 500 },
{ "_id" : 2, "item" : "f2", type: "food", quantity: 100 },
{ "_id" : 3, "item" : "p1", type: "paper", quantity: 200 },
{ "_id" : 4, "item" : "p2", type: "paper", quantity: 150 },
{ "_id" : 5, "item" : "f3", type: "food", quantity: 300 },
{ "_id" : 6, "item" : "t1", type: "toys", quantity: 500 },
{ "_id" : 7, "item" : "a1", type: "apparel", quantity: 250 },
{ "_id" : 8, "item" : "a2", type: "apparel", quantity: 400 },
{ "_id" : 9, "item" : "t2", type: "toys", quantity: 50 },
{ "_id" : 10, "item" : "f4", type: "food", quantity: 75 }])
2. 无索引查询
db.inventory.find(
{
quantity: {
$gte: 100,
$lte: 200
}
}
).explain("executionStats")
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "mytest.inventory",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"quantity" : {
"$lte" : 200
}
},
{
"quantity" : {
"$gte" : 100
}
}
]
},
"winningPlan" : {
"stage" : "COLLSCAN",
"filter" : {
"$and" : [
{
"quantity" : {
"$lte" : 200
}
},
{
"quantity" : {
"$gte" : 100
}
}
]
},
"direction" : "forward"
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 3,
"executionTimeMillis" : 48,
"totalKeysExamined" : 0,
"totalDocsExamined" : 10,
"executionStages" : {
"stage" : "COLLSCAN",
"filter" : {
"$and" : [
{
"quantity" : {
"$lte" : 200
}
},
{
"quantity" : {
"$gte" : 100
}
}
]
},
"nReturned" : 3,
"executionTimeMillisEstimate" : 28,
"works" : 12,
"advanced" : 3,
"needTime" : 8,
"needYield" : 0,
"saveState" : 1,
"restoreState" : 1,
"isEOF" : 1,
"invalidates" : 0,
"direction" : "forward",
"docsExamined" : 10
}
},
"serverInfo" : {
"host" : "DESKTOP-GVTQ3ND",
"port" : 27017,
"version" : "4.0.4",
"gitVersion" : "f288a3bdf201007f3693c58e140056adf8b04839"
},
"ok" : 1
}
- COLLSCAN:如果你仔细一看,知道是CollectionScan,就是所谓的“集合扫描”,看到集合扫描 相当于 sql数据库中的table scan
- nReturned:就是所谓的numReturned,就是说最后返回的num个数,从图中可以看到,就是最终返回了三条
- docsExamined:就是documentsExamined,检查了10个documents。
从上面三个信息中得出,docsExamined 10条数据,最终才返回nReturned 3条,说明做了7条数据scan的无用功,那么这个时候问题就来了!
3. 创建索引
在quality字段之上添加索引,如下:
db.inventory.createIndex({ quantity: 1})
db.inventory.find(
{ quantity: { $gte: 100, $lte: 200 } }
).explain("executionStats")
结果如下:
db.inventory.find( { quantity: { $gte: 100, $lte: 200 } } ).explain("executionStats")
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "mytest.inventory",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"quantity" : {
"$lte" : 200
}
},
{
"quantity" : {
"$gte" : 100
}
}
]
},
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"quantity" : 1
},
"indexName" : "quantity_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"quantity" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"quantity" : [
"[100.0, 200.0]"
]
}
}
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 3,
"executionTimeMillis" : 0,
"totalKeysExamined" : 3,
"totalDocsExamined" : 3,
"executionStages" : {
"stage" : "FETCH",
"nReturned" : 3,
"executionTimeMillisEstimate" : 0,
"works" : 4,
"advanced" : 3,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 3,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 3,
"executionTimeMillisEstimate" : 0,
"works" : 4,
"advanced" : 3,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"quantity" : 1
},
"indexName" : "quantity_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"quantity" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"quantity" : [
"[100.0, 200.0]"
]
},
"keysExamined" : 3,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
},
"serverInfo" : {
"host" : "DESKTOP-GVTQ3ND",
"port" : 27017,
"version" : "4.0.4",
"gitVersion" : "f288a3bdf201007f3693c58e140056adf8b04839"
},
"ok" : 1
}
当我们执行完createindex之后,再次explain,4个重要的parameters参数就出来了:
- IXSCAN:这个时候再也不是所谓的COLLSCAN了,而是IndexScan,这就说明我们已经命中索引了
- nReturned, totalDocsExamined, totalKeysExamined:从中可以看到三个参数都是3,这就说明我们的mongodb查看了3个key,3个document,返回3个文档,这个就是所谓的高性能所在。
4. 查看索引
db.inventory.getIndexes()
三、数据类型
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:正则表达式类型。用于存储正则表达式。