深入浅出MongoDB

MongDB

安装

https://www.mongodb.com/try/download/community

Mongdb vs RDS

  1. Database : Database
  2. Collection : Table
  3. Documents : Row

创建数据库

use dbName;

创建collection

db.createCollection();

CRUD

Insert(Create)

在mongdb中对于一个document的写操作是原子的

  • db.collection.insertOne()

    > db.c1.insertOne({name:"周三",age:18});
    {
            "acknowledged" : true,
            "insertedId" : ObjectId("61e2dff8398c43c5df448f99")
    }
    
  • db.collection.insertMany()

    > db.c1.insertMany([{name:"李四",age:22},{name:"王五",age:30}])
    {
            "acknowledged" : true,
            "insertedIds" : [
                    ObjectId("61e2e432398c43c5df448f9a"),
                    ObjectId("61e2e432398c43c5df448f9b")
            ]
    }
    

Read

1. 默认
db.c1.find()
{ "_id" : ObjectId("61e2dff8398c43c5df448f99"), "name" : "周三", "age" : 18 }
{ "_id" : ObjectId("61e2e432398c43c5df448f9a"), "name" : "李四", "age" : 22 }
{ "_id" : ObjectId("61e2e432398c43c5df448f9b"), "name" : "王五", "age" : 30 }
db.c1.find({age:{$gte:18}}, {name:1})
{ "_id" : ObjectId("61e2dff8398c43c5df448f99"), "name" : "周三" }
{ "_id" : ObjectId("61e2e432398c43c5df448f9a"), "name" : "李四" }
{ "_id" : ObjectId("61e2e432398c43c5df448f9b"), "name" : "王五" }
> db.c1.find({age:{$gt:18}}, {name:1})
{ "_id" : ObjectId("61e2e432398c43c5df448f9a"), "name" : "李四" }
{ "_id" : ObjectId("61e2e432398c43c5df448f9b"), "name" : "王五" }
> db.c1.find({age:{$gt:18}}, {name:1,age:1})
{ "_id" : ObjectId("61e2e432398c43c5df448f9a"), "name" : "李四", "age" : 22 }
{ "_id" : ObjectId("61e2e432398c43c5df448f9b"), "name" : "王五", "age" : 30 }
2. 带条件查找
> db.inventory.find({status: {$in:["A","D"]}})
{ "_id" : ObjectId("61e3b4e451b2c0fb4601eea8"), "item" : "journal", "qty" : 25, "size" : { "h" : 14, "w" : 21, "uom" : "cm" }, "status" : "A" }
{ "_id" : ObjectId("61e3b4e451b2c0fb4601eea9"), "item" : "notebook", "qty" : 50, "size" : { "h" : 8.5, "w" : 11, "uom" : "in" }, "status" : "A" }
{ "_id" : ObjectId("61e3b4e451b2c0fb4601eeaa"), "item" : "paper", "qty" : 100, "size" : { "h" : 8.5, "w" : 11, "uom" : "in" }, "status" : "D" }
{ "_id" : ObjectId("61e3b4e451b2c0fb4601eeab"), "item" : "planner", "qty" : 75, "size" : { "h" : 22.85, "w" : 30, "uom" : "cm" }, "status" : "D" }
{ "_id" : ObjectId("61e3b4e451b2c0fb4601eeac"), "item" : "postcard", "qty" : 45, "size" : { "h" : 10, "w" : 15.25, "uom" : "cm" }, "status" : "A" }
3. 多个条件AND
> db.inventory.find({ status: "A", qty: { $lt: 30 } })
{ "_id" : ObjectId("61e3b4e451b2c0fb4601eea8"), "item" : "journal", "qty" : 25, "size" : { "h" : 14, "w" : 21, "uom" : "cm" }, "status" : "A" }
4. 多个条件OR
> db.inventory.find({ $or: [ { status: "A" }, { qty: { $lt: 30 } } ] })
{ "_id" : ObjectId("61e3b4e451b2c0fb4601eea8"), "item" : "journal", "qty" : 25, "size" : { "h" : 14, "w" : 21, "uom" : "cm" }, "status" : "A" }
{ "_id" : ObjectId("61e3b4e451b2c0fb4601eea9"), "item" : "notebook", "qty" : 50, "size" : { "h" : 8.5, "w" : 11, "uom" : "in" }, "status" : "A" }
{ "_id" : ObjectId("61e3b4e451b2c0fb4601eeac"), "item" : "postcard", "qty" : 45, "size" : { "h" : 10, "w" : 15.25, "uom" : "cm" }, "status" : "A" }
5. 查询的条件中嵌入文档

当查询的条件中存在字段时,字段的顺序需要与数据库里的保持一致,严格的顺序要求

> db.inventory.find( { size: { h: 14, w: 21, uom: "cm" } } ) # size等于某个文档
{ "_id" : ObjectId("61e3b4e451b2c0fb4601eea8"), "item" : "journal", "qty" : 25, "size" : { "h" : 14, "w" : 21, "uom" : "cm" }, "status" : "A" }
{ "_id" : ObjectId("61e3b85c51b2c0fb4601eead"), "item" : "journal", "qty" : 25, "size" : { "h" : 14, "w" : 21, "uom" : "cm" }, "status" : "A" }
>
db.inventory.find(  { size: { w: 21, h: 14, uom: "cm" } }  ) # 不会匹配任何一条数据,因为字段的顺序不对
6. 对于某些字段可以采用 outer.key的形式匹配
# 查询size中的属性uom为in的document
> db.inventory.find( { "size.uom": "in" } )
{ "_id" : ObjectId("61e3b4e451b2c0fb4601eea9"), "item" : "notebook", "qty" : 50, "size" : { "h" : 8.5, "w" : 11, "uom" : "in" }, "status" : "A" }
{ "_id" : ObjectId("61e3b4e451b2c0fb4601eeaa"), "item" : "paper", "qty" : 100, "size" : { "h" : 8.5, "w" : 11, "uom" : "in" }, "status" : "D" }
{ "_id" : ObjectId("61e3b85c51b2c0fb4601eeae"), "item" : "notebook", "qty" : 50, "size" : { "h" : 8.5, "w" : 11, "uom" : "in" }, "status" : "A" }
{ "_id" : ObjectId("61e3b85c51b2c0fb4601eeaf"), "item" : "paper", "qty" : 100, "size" : { "h" : 8.5, "w" : 11, "uom" : "in" }, "status" : "D" }
>
7. 当查询的条件中中value是一个数组时
  • 默认情况
  • $all
> db.inventory.find( { tags: ["red", "blank"] } ) # tags数组完全匹配(顺序)
{ "_id" : ObjectId("61e3bfa451b2c0fb4601eeb3"), "item" : "notebook", "qty" : 50, "tags" : [ "red", "blank" ], "dim_cm" : [ 14, 21 ] }

> db.inventory.find( { tags: {$all:["red", "blank"] }} ) # value作为子集,不要求顺序
{ "_id" : ObjectId("61e3bfa451b2c0fb4601eeb2"), "item" : "journal", "qty" : 25, "tags" : [ "blank", "red" ], "dim_cm" : [ 14, 21 ] }
{ "_id" : ObjectId("61e3bfa451b2c0fb4601eeb3"), "item" : "notebook", "qty" : 50, "tags" : [ "red", "blank" ], "dim_cm" : [ 14, 21 ] }
{ "_id" : ObjectId("61e3bfa451b2c0fb4601eeb4"), "item" : "paper", "qty" : 100, "tags" : [ "red", "blank", "plain" ], "dim_cm" : [ 14, 21 ] }
{ "_id" : ObjectId("61e3bfa451b2c0fb4601eeb5"), "item" : "planner", "qty" : 75, "tags" : [ "blank", "red" ], "dim_cm" : [ 22.85, 30 ] }


db.inventory.find( { dim_cm: { $gt: 25 } } ) # dim_cm的数组中至少有一个元素满足条件
db.inventory.find( { dim_cm: { $gt: 15, $lt: 20 } } ) # 元素至少一个元素满足一个条件
db.inventory.find( { dim_cm: { $elemMatch: { $gt: 22, $lt: 30 } } } ) # 同时满足两个条件
db.inventory.find( { "dim_cm.1": { $gt: 25 } } ) # 数组第一个元素大于25
db.inventory.find( { "tags": { $size: 3 } } ) # tags是一个数组且数组的长度为3
8. 查询的value是一个document
db.inventory.find( { "instock": { warehouse: "A", qty: 5 } } ) # instock是一个数组且存在满足条件的元素,具有顺序要求
db.inventory.find( { 'instock.qty': { $lte: 20 } } ) # instock是一个数组且数组元素是document,元组满足条件
db.inventory.find( { 'instock.0.qty': { $lte: 20 } } ) # instock数组的第一个元素满足条件
db.inventory.find( { "instock": { $elemMatch: { qty: 5, warehouse: "A" } } } ) # instock的value是一个document数组,且元素同时满足条件,不要求字段顺序
db.inventory.find( { "instock": { $elemMatch: { qty: { $gt: 10, $lte: 20 } } } } ) 
9. 指定返回某些字段
db.inventory.find( { status: "A" } ) # 默认返回所有字段
db.inventory.find( { status: "A" }, { item: 1, status: 1 } ) # 需要返回某些字段就将这些字段设为1,_id不用设置也返回,可将id设为0而不返回
db.inventory.find( { status: "A" }, { item: 1, status: 1, _id: 0 } ) # 不返回id字段
db.inventory.find( { status: "A" }, { status: 0, instock: 0 } ) # 不返回的字段设为0

db.inventory.find(
   { status: "A" },
   { item: 1, status: 1, "size.uom": 1 }
) # 返回某个内嵌文档的谋个属性
db.inventory.find( { status: "A" }, { item: 1, status: 1, "instock.qty": 1 } ) # instock为数组,返回数组中每个元素的特定字段

db.inventory.find( { status: "A" }, { item: 1, status: 1, instock: { $slice: -1 } } ) # 也可以指定返回某个下标的元素,不能通过instock.0来指定返回某个元素,也可以使用$elemMatch

10. 当查询的字段为null或者不存在
db.inventory.insertMany([
   { _id: 1, item: null },
   { _id: 2 }
]) # 插入数据
db.inventory.find( { item: null } ) # 同时返回item为null或者不存在item字段的document
db.inventory.find( { item : { $type: 10 } } ) # 只会返回item为null的document,因为在BSON(mongdb存储数据的格式)中,null类型对应的编码为10
db.inventory.find( { item : { $exists: false } } ) # 只会返回不存在item字段的document

Update

  1. 在mongdb中对于一个document的写操作是原子的

  2. _id一般都是自动生成的,一旦设置后就不能被修改以及replace

db.c1.updateOne({age:18},{$set: {name:"张三"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
1. 更新单个document
db.inventory.updateOne(
   { item: "paper" }, # 匹配条件
   {
     $set: { "size.uom": "cm", status: "P" }, # 修改属性,如果字段不存在就会自动创建
     $currentDate: { lastModified: true } # 设置lastModified为currentDate
   }
)

db.inventory.updateMany(
   { "qty": { $lt: 50 } },
   {
     $set: { "size.uom": "in", status: "P" },
     $currentDate: { lastModified: true }
   }
)# 更新数据类型为document的字段的内部属性
db.inventory.replaceOne(
   { item: "paper" },
   { item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 40 } ] }
) # 替换document
2. 使用流式计算进行更新
db.students.insertMany( [
   { _id: 1, test1: 95, test2: 92, test3: 90, modified: new Date("01/05/2020") },
   { _id: 2, test1: 98, test2: 100, test3: 102, modified: new Date("01/05/2020") },
   { _id: 3, test1: 95, test2: 110, modified: new Date("01/04/2020") }
] ) # 插入数据
  
db.students.updateOne( { _id: 3 }, [ { $set: { "test3": 98, modified: "$$NOW"} } ] ) # 基本操作

db.students2.updateMany( {}, # 匹配条件。{}匹配所有document
  [
    { $replaceRoot: { newRoot:   
       { $mergeObjects: [ { quiz1: 0, quiz2: 0, test1: 0, test2: 0 }, "$$ROOT" ] } # 将{}里的字段与原来的字段($ROOT)合并,没有的补充,有的不管
    } },   # 替换原来的结构
    { $set: { modified: "$$NOW"}  } # 修改字段值,$$NOW为读取当前的日期,$$类似于bash里面的操作
  ]
) # 在原来的基础之上添加字段以及修改字段

db.students3.insertMany( [
   { "_id" : 1, "tests" : [ 95, 92, 90 ], "modified" : ISODate("2019-01-01T00:00:00Z") },
   { "_id" : 2, "tests" : [ 94, 88, 90 ], "modified" : ISODate("2019-01-01T00:00:00Z") },
   { "_id" : 3, "tests" : [ 70, 75, 82 ], "modified" : ISODate("2019-01-01T00:00:00Z") }
] ); # 创建collection并添加数据

db.students3.updateMany(
   { },
   [
     { $set: { average : { $trunc: [ { $avg: "$tests" }, 0 ] }, modified: "$$NOW" } }, # 1. 计算tests字段所有元素的平均值,$trunc设置小数位数为0, 添加字段average并修改modified的值
     { $set: { grade: { $switch: { # 添加grade字段,值为switch中计算返回的值
                           branches: [
                               { case: { $gte: [ "$average", 90 ] }, then: "A" },
                               { case: { $gte: [ "$average", 80 ] }, then: "B" },
                               { case: { $gte: [ "$average", 70 ] }, then: "C" },
                               { case: { $gte: [ "$average", 60 ] }, then: "D" }
                           ],
                           default: "F"
     } } } }
   ]
)


db.students4.insertMany( [
  { "_id" : 1, "quizzes" : [ 4, 6, 7 ] },
  { "_id" : 2, "quizzes" : [ 5 ] },
  { "_id" : 3, "quizzes" : [ 10, 10, 10 ] }
] )# 创建collection并添加数据
db.students4.updateOne( { _id: 2 }, # 匹配条件
  [ { $set: { quizzes: { $concatArrays: [ "$quizzes", [ 8, 6 ]  ] } } } ] # quizzes字段添加[8,6]元素
)


db.temperatures.insertMany( [
  { "_id" : 1, "date" : ISODate("2019-06-23"), "tempsC" : [ 4, 12, 17 ] },
  { "_id" : 2, "date" : ISODate("2019-07-07"), "tempsC" : [ 14, 24, 11 ] },
  { "_id" : 3, "date" : ISODate("2019-10-30"), "tempsC" : [ 18, 6, 8 ] }
] ) # 创建collection并添加数据

db.temperatures.updateMany( { },
  [
    { $addFields: { "tempsF": { # 添加一个字段,其值为后面计算得到的数组
          $map: { # 对于数组中的每一个元素都进行操作,类似于python中的map,然后返回一个数组
             input: "$tempsC", # 入参
             as: "celsius", # 重命名
             in: { $add: [ { $multiply: ["$$celsius", 9/5 ] }, 32 ] } # 先执行乘法然后执行加法,然后
          } 
    } } }
  ]
) 


# 结果
> db.temperatures.find().pretty()
{
        "_id" : 1,
        "date" : ISODate("2019-06-23T00:00:00Z"),
        "tempsC" : [
                4,
                12,
                17
        ],
        "tempsF" : [
                39.2,
                53.6,
                62.6
        ]
}
{
        "_id" : 2,
        "date" : ISODate("2019-07-07T00:00:00Z"),
        "tempsC" : [
                14,
                24,
                11
        ],
        "tempsF" : [
                57.2,
                75.2,
                51.8
        ]
}
{
        "_id" : 3,
        "date" : ISODate("2019-10-30T00:00:00Z"),
        "tempsC" : [
                18,
                6,
                8
        ],
        "tempsF" : [
                64.4,
                42.8,
                46.4
        ]
}
>

Delete

1. Delete Documents

删除documents后不会删除index

db.inventory.insertMany( [
   { item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
   { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "P" },
   { item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
   { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
   { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" },
] ); # 创建collection并怼入数据

db.inventory.deleteMany({}) # 删除所有
db.inventory.deleteMany({ status : "A" }) # 删除符合条件的documents
db.inventory.deleteOne( { status: "D" } ) # 删除符合条件的document

Bulk Write Operations(批量写操作)

批量写有两种模式:顺序、并行

  1. 顺序:当批操作产生异常时会直接直接返回,不在执行后续操作
  2. 并行:当批操作产生异常时仍然会继续执行
> db.characters.insertMany([{ "_id" : 1, "char" : "Brisbane", "class" : "monk", "lvl" : 4 },{ "_id" : 2, "char" : "Eldon", "class" : "alchemist", "lvl" : 3 },{ "_id" : 3, "char" : "Meldane", "class" : "ranger", "lvl" : 3 }]) # 创建collection并生成数据

try {
   db.characters.bulkWrite(
      [
         { insertOne :
            {
               "document" :
               {
                  "_id" : 4, "char" : "Dithras", "class" : "barbarian", "lvl" : 4
               }
            }
         },
         { insertOne :
            {
               "document" :
               {
                  "_id" : 5, "char" : "Taeln", "class" : "fighter", "lvl" : 3
               }
            }
         },
         { updateOne :
            {
               "filter" : { "char" : "Eldon" },
               "update" : { $set : { "status" : "Critical Injury" } }
            }
         },
         { deleteOne :
            { "filter" : { "char" : "Brisbane" } }
         },
         { replaceOne :
            {
               "filter" : { "char" : "Meldane" },
               "replacement" : { "char" : "Tanys", "class" : "oracle", "lvl" : 4 }
            }
         }
      ]
   );
}
catch (e) {
   print(e);
} # 执行批量写入

Mysql VS MongDB

1. 概念
SQL Terms/ConceptsMongoDB Terms/Concepts
databasedatabase
tablecollection
rowdocument or BSON document
columnfield
indexindex
table joins$lookup, embedded documents
primary keySpecify any unique column or column combination as primary key.primary keyIn MongoDB, the primary key is automatically set to the _id field.
aggregation (e.g. group by)aggregation pipelineSee the SQL to Aggregation Mapping Chart.
SELECT INTO NEW_TABLE$outSee the SQL to Aggregation Mapping Chart.
MERGE INTO TABLE$merge (Available starting in MongoDB 4.2)See the SQL to Aggregation Mapping Chart.
UNION ALL$unionWith (Available starting in MongoDB 4.4)
2. 对表的操作
SQL Schema StatementsMongoDB Schema Statements
CREATE TABLE people ( id MEDIUMINT NOT NULL AUTO_INCREMENT, user_id Varchar(30), age Number, status char(1), PRIMARY KEY (id))Implicitly created on first insertOne() or insertMany() operation. The primary key _id is automatically added if _id field is not specified.db.people.insertOne( { user_id: "abc123", age: 55, status: "A" } )However, you can also explicitly create a collection:db.createCollection("people")
ALTER TABLE peopleADD join_date DATETIMECollections do not describe or enforce the structure of its documents; i.e. there is no structural alteration at the collection level.However, at the document level, updateMany() operations can add fields to existing documents using the $set operator.db.people.updateMany( { }, { $set: { join_date: new Date() } })
ALTER TABLE peopleDROP COLUMN join_dateCollections do not describe or enforce the structure of its documents; i.e. there is no structural alteration at the collection level.However, at the document level, updateMany() operations can remove fields from documents using the $unset operator.db.people.updateMany( { }, { $unset: { "join_date": "" } })
CREATE INDEX idx_user_id_ascON people(user_id)db.people.createIndex( { user_id: 1 } )
CREATE INDEX idx_user_id_asc_age_descON people(user_id, age DESC)db.people.createIndex( { user_id: 1, age: -1 } )
DROP TABLE peopledb.people.drop()
3. Select
SQL SELECT StatementsMongoDB find() Statements
SELECT *FROM peopledb.people.find()
SELECT id, user_id, statusFROM peopledb.people.find( { }, { user_id: 1, status: 1 })
SELECT user_id, statusFROM peopledb.people.find( { }, { user_id: 1, status: 1, _id: 0 })
SELECT *FROM peopleWHERE status = "A"db.people.find( { status: "A" })
SELECT user_id, statusFROM peopleWHERE status = "A"db.people.find( { status: "A" }, { user_id: 1, status: 1, _id: 0 })
SELECT *FROM peopleWHERE status != "A"db.people.find( { status: { $ne: "A" } })
SELECT *FROM peopleWHERE status = "A"AND age = 50db.people.find( { status: "A", age: 50 })
SELECT *FROM peopleWHERE status = "A"OR age = 50db.people.find( { $or: [ { status: "A" } , { age: 50 } ] })
SELECT *FROM peopleWHERE age > 25db.people.find( { age: { $gt: 25 } })
SELECT *FROM peopleWHERE age < 25db.people.find( { age: { $lt: 25 } })
SELECT *FROM peopleWHERE age > 25AND age <= 50db.people.find( { age: { $gt: 25, $lte: 50 } })
SELECT *FROM peopleWHERE user_id like "%bc%"db.people.find( { user_id: /bc/ } )-or-db.people.find( { user_id: { $regex: /bc/ } } )
SELECT *FROM peopleWHERE user_id like "bc%"db.people.find( { user_id: /^bc/ } )-or-db.people.find( { user_id: { $regex: /^bc/ } } )
SELECT *FROM peopleWHERE status = "A"ORDER BY user_id ASCdb.people.find( { status: "A" } ).sort( { user_id: 1 } )
SELECT *FROM peopleWHERE status = "A"ORDER BY user_id DESCdb.people.find( { status: "A" } ).sort( { user_id: -1 } )
SELECT COUNT(*)FROM peopledb.people.count()ordb.people.find().count()
SELECT COUNT(user_id)FROM peopledb.people.count( { user_id: { $exists: true } } )ordb.people.find( { user_id: { $exists: true } } ).count()
SELECT COUNT(*)FROM peopleWHERE age > 30db.people.count( { age: { $gt: 30 } } )ordb.people.find( { age: { $gt: 30 } } ).count()
SELECT DISTINCT(status)FROM peopledb.people.aggregate( [ { $group : { _id : "$status" } } ] )or, for distinct value sets that do not exceed the BSON size limitdb.people.distinct( "status" )
SELECT *FROM peopleLIMIT 1db.people.findOne()ordb.people.find().limit(1)
SELECT *FROM peopleLIMIT 5SKIP 10db.people.find().limit(5).skip(10)
EXPLAIN SELECT *FROM peopleWHERE status = "A"db.people.find( { status: "A" } ).explain()
4. Update
SQL Update StatementsMongoDB updateMany() Statements
UPDATE peopleSET status = "C"WHERE age > 25db.people.updateMany( { age: { $gt: 25 } }, { $set: { status: "C" } })
UPDATE peopleSET age = age + 3WHERE status = "A"db.people.updateMany( { status: "A" } , { $inc: { age: 3 } })
5. Delete
SQL Delete StatementsMongoDB deleteMany() Statements
DELETE FROM peopleWHERE status = "D"db.people.deleteMany( { status: "D" } )
DELETE FROM peopledb.people.deleteMany({})

文本搜索

一个collection中只有一个text index, 但是可以包含多个字段

db.stores.insert(
   [
     { _id: 1, name: "Java Hut", description: "Coffee and cakes" },
     { _id: 2, name: "Burger Buns", description: "Gourmet hamburgers" },
     { _id: 3, name: "Coffee Shop", description: "Just coffee" },
     { _id: 4, name: "Clothes Clothes Clothes", description: "Discount clothing" },
     { _id: 5, name: "Java Shopping", description: "Indonesian goods" }
   ]
) # 创建collection并插入数据
db.stores.createIndex( { name: "text", description: "text" } ) # 创建索引
db.stores.find( { $text: { $search: "java coffee shop" } } ) # 查找存在关键字之一的document
db.stores.find( { $text: { $search: "\"coffee shop\"" } } ) # 查找存在关键词语的
db.stores.find( { $text: { $search: "java shop -coffee" } } ) # 查找包含Java、shop同时不包含coffee的
db.stores.find(
   { $text: { $search: "java coffee shop" } }, # 使用text 索引查找
   { score: { $meta: "textScore" } } # 计算document对于关键词的匹配率
).sort( { score: { $meta: "textScore" } } ) # 查找并排序

Read Conserns 读策略

待更新

分析查询性能

创建数据
{ "_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 }
查询当前执行计划
db.inventory.find(
   { quantity: { $gte: 100, $lte: 200 } }
).explain("executionStats")

# 结果
{
   "queryPlanner" : {
         "plannerVersion" : 1,
         ...
         "winningPlan" : {
            "stage" : "COLLSCAN",  // 查询类型:全表查找
            ...
         }
   },
   "executionStats" : {
      "executionSuccess" : true,
      "nReturned" : 3, // 返回的结果
      "executionTimeMillis" : 0, 
      "totalKeysExamined" : 0,  // 查询使用的索引数
      "totalDocsExamined" : 10, // 显示10 ,MongoDB 必须扫描十个文档(即集合中的所有文档)才能找到三个匹配的文档。
      "executionStages" : {
         "stage" : "COLLSCAN", // 
         ...
      },
      ...
   },
   ...
}
创建索引
db.inventory.createIndex( { quantity: 1 } ) # 为quantity创建自增的索引
利用索引查询
db.inventory.find(
   { quantity: { $gte: 100, $lte: 200 } }
).explain("executionStats") 


{
   "queryPlanner" : {
         "plannerVersion" : 1,
         ...
         "winningPlan" : {
               "stage" : "FETCH", // 状态发生变化
               "inputStage" : {
                  "stage" : "IXSCAN", // 使用索引
                  "keyPattern" : {
                     "quantity" : 1
                  },
                  ...
               }
         },
         "rejectedPlans" : [ ]
   },
   "executionStats" : {
         "executionSuccess" : true,
         "nReturned" : 3, // 返回结果数
         "executionTimeMillis" : 0,
         "totalKeysExamined" : 3, // 使用索引数
         "totalDocsExamined" : 3, // 查找的文档数,只扫描了三个文档就找到了结果
         "executionStages" : {
            ...
         },
         ...
   },
   ...
}
总结
  1. 如果没有索引,查询将扫描整个10 文档集合以返回3匹配的文档。该查询还必须扫描每个文档的全部内容,可能会将它们拉入内存。这会导致昂贵且可能很慢的查询操作。

  2. 当使用索引运行时,查询会扫描3索引条目和3文档以返回3匹配的文档,从而实现非常高效的查询。

原子性和事务

原子性

在 MongoDB 中,写入操作在单个文档级别上是原子操作,即使该操作修改了单个文档的多个嵌入文档

事务

当单个写操作(例如 db.collection.updateMany())修改多个文档时,每个文档的修改是原子的,但整个操作不是原子的。

在执行多文档写操作时,无论是通过单个写操作还是多个写操作,其他操作都可能交错。

并发控制

并发控制允许多个应用程序同时运行而不会导致数据不一致或冲突。

一种方法是在只能具有唯一值的字段上创建唯一索引。这可以防止插入或更新创建重复数据。在多个字段上创建唯一索引以强制该字段值组合具有唯一性。

另一种方法是在查询谓词中为写入操作指定字段的预期当前值(类似于CAS)。

分布式查询

对集群的读取操作

默认情况下,客户端从副本集的节点中读取;但是,客户端可以指定读取首选项以将读取操作定向到其他成员。例如,客户端可以将读取首选项配置为从辅助节点或最近的成员读取到:

  • 减少多数据中心部署的延迟,
  • 通过分配高读取量(相对于写入量)来提高读取吞吐量,
  • 执行备份操作,和/或
  • 允许读取,直到选出新的主节点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-71bFTj74-1642866079563)(https://docs.mongodb.com/manual/images/replica-set-read-preference.bakedsvg.svg)]

当然,从集群的非主节点成员的读取操作读取的数据可能不是整个集群的最新数据。

集群上的写操作

集群中,所有写入操作都转到该集的主节点。主节点应用写操作并将操作记录在主节点的操作日志或oplog中。oplog 是对数据集的可重现操作序列。 集合的次要成员不断复制 oplog 并在异步过程中将操作应用于自己。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eQizp6pS-1642866079565)(https://docs.mongodb.com/manual/images/replica-set-read-write-operations-primary.bakedsvg.svg)]

分片集群的读取操作

对于分片集群,应用程序向 mongos与集群关联的实例之一发出操作。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NOOEuCaZ-1642866079565)(https://docs.mongodb.com/manual/images/sharded-cluster.bakedsvg.svg)]

分片集群上的读取操作在定向到特定分片时效率最高。对分片集合的查询应该包括集合的分片键。当查询包含分片键时,mongos可以使用配置数据库中的集群元数据将查询路由到分片。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jG2kDBEd-1642866079566)(https://docs.mongodb.com/manual/images/sharded-cluster-targeted-query.bakedsvg.svg)]

如果查询不包含分片键,则mongos必须将查询定向到集群中的所有分片。这些分散聚集查询可能效率低下。在较大的集群上,分散聚集查询对于常规操作是不可行的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JveZFSpf-1642866079566)(https://docs.mongodb.com/manual/images/sharded-cluster-scatter-gather-query.bakedsvg.svg)]

当然,如果是从非主节点读取数据,数据可能不是整个集群的最新的数据。

分片集群上的写操作

于分片集群中的分片集合, mongos将写入操作从应用程序定向到负责数据集特定部分的分片。使用配置数据库mongos中的集群元数据 将写入操作路由到适当的分片。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TV2KeXm4-1642866079566)(https://docs.mongodb.com/manual/images/sharded-cluster.bakedsvg.svg)]

MongoDB根据shard key的值将分片集合中的数据划分为范围。然后,MongoDB 将这些块分发到分片。分片键决定了块到分片的分布。这会影响集群中写入操作的性能。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HnKzkSvp-1642866079567)(https://docs.mongodb.com/manual/images/sharding-range-based.bakedsvg.svg)]

影响单个文档 的更新操作必须包括分片_id 字段。影响多个文档的更新在某些情况下如果具有shard key会更有效,但可以广播到所有分片。

系统级别

查看数据库

show dbs
show DataBases # 查看数据库
use database # 使用数据库
show collections
db.getCollectionNames() # 查看表
show users # 查看用户
show roles # 查看角色,在创建角色并分配权力的时候有用

创建用户

db.createUser({user:"accountAdmin01",
pwd: passwordPrompt(), # 这个选项设置后。在按下回车时会提示输入密码
roles: [
{ role: "clusterAdmin", db: "admin" }, # role:对于用户在某个db上赋予某种权限
{ role: "readAnyDatabase", db: "admin" }
],
writeConcern: { w: "majority" , wtimeout: 5000 } # w:写策略,默认majority, wtimeout:超时时间
})

db.runCommand( {
   dropUser: "reportUser1",
   writeConcern: { w: "majority", wtimeout: 5000 }
} ) # 删除用户

 db.runCommand({updateUser:"uuu", pwd:passwordPrompt()}) # 更改密码
 db.dropUser("uuu"); # 删除用户
  
 db.getAllUsers() # 获取所有用户

往日精彩: 爆肝!!!!JavaSE知识点1.3w字总结.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jonny Jiang-zh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值