mongodb是一种存储文档的非关系数据库
1. 初见mongodb
mongodb中拥有很多集合,拥有相似内容的文档被归档于同一个集合之下;同一个集合中的文档可以拥有完全不同的字段;每个文档中包含多个字段和对应的值;针对omongdb而言没有提前规划和设计字段的说法,是非关系型数据库的优势。
1.1 启动mongodb
1 2 3 4 5
| docker run -d --name test-mongo library/mongo
docker exec -ti test-mongo mongo
|
2. 数据库与集合操作
2.1 选择和创建数据库
1 2 3 4 5 6 7 8 9 10 11
| > show dbs;
> use test;
> show collections;
> db
|
保留的数据库名:
- admin:从权限角度来看,这是root数据库。要是一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特殊的数据库命令也只能在这个库运行。
- lloca:这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合,此数据库在副本集情况下数据不会被复制。
- config:当前mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。
2.2 数据库的删除
1 2 3 4 5
| > use test; switched to db test
> db.dropDatabase() { "ok" : 1 }
|
2.3 集合操作
1 2 3 4 5
| db.createCollection("")
db.集合.drop()
|
3. 文档基本CRUD
常见字段讲解:
- 文档主键(_id):每一篇文档的主键皆不相同具有唯一性;除数组以外,其余数据类型都可以作为文档主键。
- 对象主键:默认的文档主键,可快速生成的12字节ID;前四字节就是创建时间,但需注意客户端时间正确,且同一秒钟创建文档,无法完全区分创建顺序。
3.1 创建文档
1 2 3 4 5 6 7
|
{ _id: "account1", name: "alice",balance: 100}
db.accounts.insertOne({_id: "account1",name: "alice",balance: 100})
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
> db.accounts.insert([{_id: "account5",name: "alic5",balance: 105},{_id: "account6",name: "alic6",balance: 106}]) BulkWriteResult({ "writeErrors" : [ ], "writeConcernErrors" : [ ], "nInserted" : 2, "nUpserted" : 0, "nMatched" : 0, "nModified" : 0, "nRemoved" : 0, "upserted" : [ ] })
|
注意:
- 插入的文档没有_id,会自动生成主键值
- mongodb中的数字默认为double类型,如果要存储整型,必须使用函数NumberInt,否则取出来可能有问题。
- 插入当前日期使用new Date()
- 在批量插入时,如果某条数据插入失败将会终止插入,且已经插入的成功的数据不会回滚。
3.2 查询文档
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| db['collection'].find() db.collection.find()
db.collection.find({name:"alic6"}) > db.accounts.find({name:"alic6"}) { "_id" : "account6", "name" : "alic6", "balance" : 106 } { "_id" : ObjectId("63f9c8d91c3cf79dd6ce2814"), "name" : "alic6", "balance" : 106 }
db.colletion.findOne({}) > db.accounts.findOne({name:"alic6"}) { "_id" : "account6", "name" : "alic6", "balance" : 106 }
|
1 2 3 4 5 6 7 8 9 10 11 12
| db.collection.find({条件json},{字段:1/0}).
> db.accounts.find({name:"alic6"}) { "_id" : "account6", "name" : "alic6", "balance" : 106 } { "_id" : ObjectId("63f9c8d91c3cf79dd6ce2814"), "name" : "alic6", "balance" : 106 } > db.accounts.find({name:"alic6"},{balance:1}) { "_id" : "account6", "balance" : 106 } { "_id" : ObjectId("63f9c8d91c3cf79dd6ce2814"), "balance" : 106 } > db.accounts.find({name:"alic6"},{balance:1,_id:0}). { "balance" : 106 } { "balance" : 106 }
|
3.3 更新文档
1 2 3 4 5 6 7 8 9 10 11
| db.collection.update({条件jsn},{更新JSON})
> db.accounts.find({_id:"account5"}). { "_id" : "account5", "name" : "alic5", "balance" : 105 }
> db.accounts.update({_id:"account5"},{banlance:NumberInt(1001)}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.accounts.find({_id:"account5"}) { "_id" : "account5", "banlance" : 1001 }
|
1 2 3 4 5 6 7 8 9 10 11
| > db.accounts.find({_id:"account6"}) { "_id" : "account6", "name" : "alic6", "balance" : 106 } > db.accounts.update({_id:"account6"},{$set:{banlance:NumberInt(1001)}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.accounts.find({_id:"account6"}) { "_id" : "account6", "name" : "alic6", "balance" : 106, "banlance" : 1001 } > db.accounts.update({_id:"account6"},{$set:{banlance:NumberInt(1002)}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.accounts.find({_id:"account6"}) { "_id" : "account6", "name" : "alic6", "balance" : 106, "banlance" : 1002 }
|
1 2 3 4 5 6 7 8 9
| > db.accounts.find() { "_id" : "account6", "name" : "alic6", "balance" : 106, "banlance" : 1002 } { "_id" : ObjectId("63f9c8d91c3cf79dd6ce2814"), "name" : "alic6", "balance" : 106 } > db.accounts.update({name:"alic6"},{$set:{balance:NumberInt(0000)}},{multi:true}). WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 }) > db.accounts.find() { "_id" : "account6", "name" : "alic6", "balance" : 0, "banlance" : 1002 } { "_id" : ObjectId("63f9c8d91c3cf79dd6ce2814"), "name" : "alic6", "balance" : 0 }
|
3.4 删除文档
1 2 3 4 5 6 7 8
| db.collection.remove({条件JSON})
db.colletcion.remove({})
db.collection.remove({_id:"1"})
|
3.5 分页查询
1 2 3 4 5
| db.collection.count({条件JSON},{其他选项})
db.collection.count()
|
1 2 3 4 5 6 7 8 9 10 11 12
|
> db.accounts.find({}).limit(1) { "_id" : "account5", "banlance" : 1001 }
> db.accounts.find({}).limit(1).skip(1) { "_id" : "account6", "name" : "alic6", "balance" : 0, "banlance" : 1002 }
db.accounts.find({}).limit(2) db.accounts.find({}).limit(2).skip(2)
|
sort()方法对数据进行排序,sort()方法可以通过参数指定排序的字段,并使用1和-1来指定排序的方式,其中1为升序排序,而-1位降序排序。
1 2 3 4 5
| db.collection.find().sort({key:1,key2:-})
db.accounts.find({}).sort({name:-1})
|
注意:执行顺序sort - > skip - > limit,和书写顺序无关。
3.6 其他查询
1 2 3 4 5 6 7 8
| db.collection.find(字段:/正则表达式/)
db.collection.find(name:/^001/)
db.collection.find(name:/001/)
|
1 2 3 4 5 6
| db.collection.find({"field":{$gt:value}}). $lt 小于、$gte 大于等于、$lte 小于等于、$ne 不等于
db.comment.find({likenum:{$gt:NumberInt(700)}})
|
1 2 3 4 5 6
| db.collection.find({userid:{$in;["1003","1004"]}}).
db.collection.find({$and:[{likenum:{$get:NumberInt(700)}},{likenum:{$lt:NumberInt(2000}}]})
|
4. 索引
索引支持在mongodb中高效地执行查询,如果没有索引,mongodb必须执行全集合扫描,即扫描集合中的每一个文档,以选择与查询语句匹配的文档。这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可能需要花费几十秒甚至几分钟,这种对于生产来说是非常致命的。
如果查询存在适当的索引,mongodb可以使用该索引限制必须查询的文档数。
索引是特殊的数据结构,它以易于遍历的形式存储集合数据集的一小部分。索引存储特定字段或者一组字段的值,按字段值排序。索引项的排序支持有效的相等匹配和基于范围的查询操作。以外mongodb还可以使用索引中的排序返回排序结果。mongodb索引使用B树数据结构,所以索引必须是支持排序的
4.1 索引的类型
mongodb支持在文档的单个字段上创建用户定义的升序或者降序索引,称为单字段索引。对于单字段索引的排序操作,索引键的排序顺序并不重要,因为mongodb可以在任何方向上遍历索引。
mongodb还支持多个字段的用户定义索引,即复合索引。复合索引中列出的字段顺序具有重要意义。例如:如果复合索引由{userid: 1, score: -1}组成,则索引首先需要按userid进行正序排序,然后在每一个userid的值内,在按score倒序排序。
- 其他索引
- 地理空间索引 为了支持对地理空间坐标数据的有效查询,mongodb提供了两种特殊的索引:返回结果使用平面几何的二维索引和返回结果使用球面几何的二维球面索引。
- 文本索引 mongodb提供了一种文本索引类型,支持在集合中搜索字符串内容。
- 哈希索引 为了支持基于散列的分片,mongodb提供了散列索引类型,它对字段的散列进行索引。
4.2 索引的管理
1 2 3 4 5
| db.collection.getIndexes()
> db.accounts.getIndexes(). [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_" } ]
|
1 2 3 4 5 6 7 8
| db.collection.createIndex(keys,options)
db.accounts.createIndex({name:1})
db.accounts.createIndex({name:-1,balance:1})
|
1 2 3 4 5 6 7 8 9
| db.colletcion.dropIndex(index)
> db.accounts.dropIndex({name:-1}) { "nIndexesWas" : 4, "ok" : 1 }
db.accounts.dropIndexes()
|
5. 索引的使用
5.1 执行计划
分析查询性能通常使用执行计划(解释计划、explain plan)来查询查询的情况,如查询的时间,是否基于所有查询等。一般想知道建立的索引是否有效,效果如何,都需要通过执行计划来查看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| > db.accounts.find({name:"alic5"}).explain() { ... ... "winningPlan" : { "stage" : "COLLSCAN", "filter" : { "name" : { "$eq" : "alic5" } }, "direction" : "forward" }, "rejectedPlans" : [ ] }, ... ...
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| > db.accounts.createIndex({name:1})
> db.accounts.find({name:"alic5"}).explain() { ... ... "winningPlan" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "name" : 1 }, "indexName" : "name_1", "isMultiKey" : false, "multiKeyPaths" : { "name" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "name" : [ "[\"alic5\", \"alic5\"]" ] } } }, "rejectedPlans" : [ ] }, ... ... }
|
5.2 涵盖的查询
当查询条件和查询投影仅包含索引字段时,mongodb直接从索引返回结果,而不扫描任何文档或者将文档带入内存。这些覆盖的查询时非常有效的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| > db.accounts.find({name:"alic5"},{name:1,_id:0}).explain() { ... .... "winningPlan" : { "stage" : "PROJECTION_COVERED", "transformBy" : { "name" : 1, "_id" : 0 }, "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "name" : 1 }, "indexName" : "name_1", "isMultiKey" : false, "multiKeyPaths" : { "name" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "name" : [ "[\"alic5\", \"alic5\"]" ] } } }, "rejectedPlans" : [ ] }, ... .... }
|