zoukankan      html  css  js  c++  java
  • Mongodb 学习二

    聚合 aggregate

    • 聚合(aggregate)主要用于计算数据,类似sql中的sum()、avg()
    • 语法
    db.集合名称.aggregate([{管道:{表达式}}])
    

    管道

    • 管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的输入
    ps ajx | grep mongo
    
    • 在mongodb中,管道具有同样的作用,文档处理完毕后,通过管道进行下一次处理
    • 常用管道
      • $group:将集合中的文档分组,可用于统计结果
      • $match:过滤数据,只输出符合条件的文档
      • $project:修改输入文档的结构,如重命名、增加、删除字段、创建计算结果
      • $sort:将输入文档排序后输出
      • $limit:限制聚合管道返回的文档数
      • $skip:跳过指定数量的文档,并返回余下的文档
      • $unwind:将数组类型的字段进行拆分

    表达式

    • 处理输入文档并输出
    • 语法
    表达式:'$列名'
    
    • 常用表达式
      • $sum:计算总和,$sum:1同count表示计数
      • $avg:计算平均值
      • $min:获取最小值
      • $max:获取最大值
      • $push:在结果文档中插入值到一个数组中
      • $first:根据资源文档的排序获取第一个文档数据

    $last:根据资源文档的排序获取最后一个文档数据

    $group

    • 将集合中的文档分组,可用于统计结果
    • _id表示分组的依据,使用某个字段的格式为'$字段'
    • 例1:统计男生、女生的总人数
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.stu.aggregate([{$group:{_id:'$gender',count:{$sum:1}}}])
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.stu.find()
    { "_id" : ObjectId("5ef1d0b85192e418c6c6e198"), "name" : "zmd", "age" : 22, "gender" : true }
    { "_id" : ObjectId("5ef1d0df5192e418c6c6e199"), "name" : "tdq", "age" : 28, "gender" : true }
    { "_id" : ObjectId("5ef1d0f25192e418c6c6e19a"), "name" : "yanmeihua", "age" : 28, "gender" : false }
    { "_id" : ObjectId("5ef1d10b5192e418c6c6e19b"), "name" : "qjj", "age" : 20, "gender" : false }
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.stu.aggregate([{$group:{_id:'$gender',count:{$sum:1}}}])
    { "_id" : false, "count" : 2 }
    { "_id" : true, "count" : 2 }
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> 

    Group by null

    • 将集合中所有文档分为一组
    • 例2:求学生总人数、平均年龄
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.stu.aggregate([{$group:{_id:null,count:{$sum:1},avgAge:{$avg:'$age'}}}])
    { "_id" : null, "count" : 4, "avgAge" : 24.5 }

    透视数据

    • 例3:统计学生性别及学生姓名
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.stu.aggregate([{$group:{_id:'$gender',name:{$push:'$name'}}}])
    { "_id" : false, "name" : [ "yanmeihua", "qjj" ] }
    { "_id" : true, "name" : [ "zmd", "tdq" ] }
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> 
    • 使用$$ROOT可以将文档内容加入到结果集的数组中,代码如下
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.stu.aggregate([{$group:{_id:'$gender',name:{$push:'$$ROOT'}}}]).pretty()
    {
        "_id" : false,
        "name" : [
            {
                "_id" : ObjectId("5ef1d0f25192e418c6c6e19a"),
                "name" : "yanmeihua",
                "age" : 28,
                "gender" : false
            },
            {
                "_id" : ObjectId("5ef1d10b5192e418c6c6e19b"),
                "name" : "qjj",
                "age" : 20,
                "gender" : false
            }
        ]
    }
    {
        "_id" : true,
        "name" : [
            {
                "_id" : ObjectId("5ef1d0b85192e418c6c6e198"),
                "name" : "zmd",
                "age" : 22,
                "gender" : true
            },
            {
                "_id" : ObjectId("5ef1d0df5192e418c6c6e199"),
                "name" : "tdq",
                "age" : 28,
                "gender" : true
            }
        ]
    }
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> 
    db.stu.aggregate([{$group:{_id:'$gender',name:{$push:'$$ROOT'}}}]).pretty()

    $match

    • 用于过滤数据,只输出符合条件的文档

    使用MongoDB的标准查询操作

    例1:查询年龄大于20的学生

    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.stu.find()
    { "_id" : ObjectId("5ef1d0b85192e418c6c6e198"), "name" : "zmd", "age" : 22, "gender" : true }
    { "_id" : ObjectId("5ef1d0df5192e418c6c6e199"), "name" : "tdq", "age" : 28, "gender" : true }
    { "_id" : ObjectId("5ef1d0f25192e418c6c6e19a"), "name" : "yanmeihua", "age" : 28, "gender" : false }
    { "_id" : ObjectId("5ef1d10b5192e418c6c6e19b"), "name" : "qjj", "age" : 20, "gender" : false }
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.stu.aggregate([{$match:{age:{$gt:20}}}])
    { "_id" : ObjectId("5ef1d0b85192e418c6c6e198"), "name" : "zmd", "age" : 22, "gender" : true }
    { "_id" : ObjectId("5ef1d0df5192e418c6c6e199"), "name" : "tdq", "age" : 28, "gender" : true }
    { "_id" : ObjectId("5ef1d0f25192e418c6c6e19a"), "name" : "yanmeihua", "age" : 28, "gender" : false }
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> 
    • 例2:查询年龄大于20的男生、女生人数
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.stu.aggregate([{$match:{age:{$gt:20}}},{$group:{_id:"$gender",counter:{$sum:1}}}])
    { "_id" : false, "counter" : 1 }
    { "_id" : true, "counter" : 2 }
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> 

    $project 投影(显示部分字段)

    • 例1:查询学生的姓名、年龄
    db.stu.aggregate([
        {$project:{_id:0,name:1,age:1}}
    ])
    
    • 例2:查询男生、女生人数,输出人数
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.stu.find()
    { "_id" : ObjectId("5ef1d0b85192e418c6c6e198"), "name" : "zmd", "age" : 22, "gender" : true }
    { "_id" : ObjectId("5ef1d0df5192e418c6c6e199"), "name" : "tdq", "age" : 28, "gender" : true }
    { "_id" : ObjectId("5ef1d0f25192e418c6c6e19a"), "name" : "yanmeihua", "age" : 28, "gender" : false }
    { "_id" : ObjectId("5ef1d10b5192e418c6c6e19b"), "name" : "qjj", "age" : 20, "gender" : false }
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.stu.aggregate([
    ...     {$group:{_id:'$gender',counter:{$sum:1}}},
    ...     {$project:{_id:0,counter:1}}
    ... ])
    { "counter" : 2 }
    { "counter" : 2 }
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> 

    $sort

    • 将输入文档排序后输出
    • 例1:查询学生信息,按年龄升序
    db.stu.aggregate([{$sort:{age:1}}])
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.stu.aggregate([{$sort:{age:1}}])
    { "_id" : ObjectId("5ef1d10b5192e418c6c6e19b"), "name" : "qjj", "age" : 20, "gender" : false }
    { "_id" : ObjectId("5ef1d0b85192e418c6c6e198"), "name" : "zmd", "age" : 22, "gender" : true }
    { "_id" : ObjectId("5ef1d0df5192e418c6c6e199"), "name" : "tdq", "age" : 28, "gender" : true }
    { "_id" : ObjectId("5ef1d0f25192e418c6c6e19a"), "name" : "yanmeihua", "age" : 28, "gender" : false }
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> 
    • 例2:查询男生、女生年龄汇总,看哪个年龄的多
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.stu.find()
    { "_id" : ObjectId("5ef1d0b85192e418c6c6e198"), "name" : "zmd", "age" : 22, "gender" : true }
    { "_id" : ObjectId("5ef1d0df5192e418c6c6e199"), "name" : "tdq", "age" : 28, "gender" : true }
    { "_id" : ObjectId("5ef1d0f25192e418c6c6e19a"), "name" : "yanmeihua", "age" : 28, "gender" : false }
    { "_id" : ObjectId("5ef1d10b5192e418c6c6e19b"), "name" : "qjj", "age" : 20, "gender" : false }
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.stu.aggregate([{$group:{_id:'$age',counter:{$sum:1}}},{$sort:{counter:-1}}])
    { "_id" : 28, "counter" : 2 }
    { "_id" : 20, "counter" : 1 }
    { "_id" : 22, "counter" : 1 }
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> 

    $limit

    • 限制聚合管道返回的文档数
    • 例1:查询2条学生信息
    db.stu.aggregate([{$limit:2}])
    

    $skip

    • 跳过指定数量的文档,并返回余下的文档
    • 例2:查询从第3条开始的学生信息
    db.stu.aggregate([{$skip:2}])
    
    • 例3:统计男生、女生人数,按人数升序,取第二条数据
    db.stu.aggregate([
        {$group:{_id:'$gender',counter:{$sum:1}}},
        {$sort:{counter:1}},
        {$skip:1},
        {$limit:1}
    ])
    
    • 注意顺序:先写skip,再写limit

    $unwind

    • 将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值

    语法1

    • 对某字段值进行拆分
    db.集合名称.aggregate([{$unwind:'$字段名称'}])
    
    • 构造数据
    db.t2.insert({_id:1,item:'t-shirt',size:['S','M','L']})
    
    • 查询
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.t2.find()
    { "_id" : 1, "item" : "t-shirt", "size" : [ "S", "M", "L" ] }
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.t2.aggregate([{$unwind:'$size'}] )
    { "_id" : 1, "item" : "t-shirt", "size" : "S" }
    { "_id" : 1, "item" : "t-shirt", "size" : "M" }
    { "_id" : 1, "item" : "t-shirt", "size" : "L" }
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> 

    语法2

    • 对某字段值进行拆分
    • 处理空数组、非数组、无字段、null情况
    db.inventory.aggregate([{
        $unwind:{
            path:'$字段名称',
            preserveNullAndEmptyArrays:<boolean>#防止数据丢失
        }
    }])
    
    • 构造数据
    db.t3.insert([
    { "_id" : 1, "item" : "a", "size": [ "S", "M", "L"] },
    { "_id" : 2, "item" : "b", "size" : [ ] },
    { "_id" : 3, "item" : "c", "size": "M" },
    { "_id" : 4, "item" : "d" },
    { "_id" : 5, "item" : "e", "size" : null }
    ])
    
    • 使用语法1查询
    db.t3.aggregate([{$unwind:'$size'}])
    
    • 查看查询结果,发现对于空数组、无字段、null的文档,都被丢弃了
    • 问:如何能不丢弃呢?
    • 答:使用语法2查询
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.t3.find()
    { "_id" : 1, "item" : "a", "size" : [ "S", "M", "L" ] }
    { "_id" : 2, "item" : "b", "size" : [ ] }
    { "_id" : 3, "item" : "c", "size" : "M" }
    { "_id" : 4, "item" : "d" }
    { "_id" : 5, "item" : "e", "size" : null }
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.t3.aggregate([{$unwind:{path:'$size',preserveNullAndEmptyArrays:true}}])
    { "_id" : 1, "item" : "a", "size" : "S" }
    { "_id" : 1, "item" : "a", "size" : "M" }
    { "_id" : 1, "item" : "a", "size" : "L" }
    { "_id" : 2, "item" : "b" }
    { "_id" : 3, "item" : "c", "size" : "M" }
    { "_id" : 4, "item" : "d" }
    { "_id" : 5, "item" : "e", "size" : null }

    索引

    无索引创建10万条数据,查一条测试

    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> for(i=0;i<100000;i++){db.testindex.insert({name:'test'+i, age:i})}
    WriteResult({ "nInserted" : 1 })
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> 
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.testindex.find({name:"test90000"}).explain('executionStats')
    {
        "queryPlanner" : {
            "plannerVersion" : 1,
            "namespace" : "by3.testindex",
            "indexFilterSet" : false,
            "parsedQuery" : {
                "name" : {
                    "$eq" : "test90000"
                }
            },
            "winningPlan" : {
                "stage" : "COLLSCAN",
                "filter" : {
                    "name" : {
                        "$eq" : "test90000"
                    }
                },
                "direction" : "forward"
            },
            "rejectedPlans" : [ ]
        },
        "executionStats" : {
            "executionSuccess" : true,
            "nReturned" : 1,
            "executionTimeMillis" : 47,
            "totalKeysExamined" : 0,
            "totalDocsExamined" : 100000,
            "executionStages" : {
                "stage" : "COLLSCAN",
                "filter" : {
                    "name" : {
                        "$eq" : "test90000"
                    }
                },
                "nReturned" : 1,
                "executionTimeMillisEstimate" : 40,
                "works" : 100002,
                "advanced" : 1,
                "needTime" : 100000,
                "needYield" : 0,
                "saveState" : 781,
                "restoreState" : 781,
                "isEOF" : 1,
                "invalidates" : 0,
                "direction" : "forward",
                "docsExamined" : 100000
            }
        },
        "serverInfo" : {
            "host" : "vm10-81-6-235.ksc.com",
            "port" : 27017,
            "version" : "3.6.8",
            "gitVersion" : "20190823"
        },
        "ok" : 1,
        "operationTime" : Timestamp(1592970676, 1),
        "$clusterTime" : {
            "clusterTime" : Timestamp(1592970676, 1),
            "signature" : {
                "hash" : BinData(0,"AcFdOawz8xcrezIsRma7VB1T4YE="),
                "keyId" : NumberLong("6841423388271968257")
            }
        }
    }
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> 
    无 索引 执行时间 "executionTimeMillis" : 47,

    备注:db.testindex.find({name:"test90000"}).explain('executionStats')  为看语句执行的时间是多少,以及细节。

    创建索引

    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.testindex.ensureIndex({name:1})
    {
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1,
        "operationTime" : Timestamp(1592970886, 1),
        "$clusterTime" : {
            "clusterTime" : Timestamp(1592970886, 1),
            "signature" : {
                "hash" : BinData(0,"lOt2+mr5cpVt87BczfnmIeSO5CA="),
                "keyId" : NumberLong("6841423388271968257")
            }
        }
    }
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> 
    db.testindex.ensureIndex({name:1})

    再次查询

    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.testindex.find({name:"test90000"}).explain('executionStats')
    {
        "queryPlanner" : {
            "plannerVersion" : 1,
            "namespace" : "by3.testindex",
            "indexFilterSet" : false,
            "parsedQuery" : {
                "name" : {
                    "$eq" : "test90000"
                }
            },
            "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" : [
                            "["test90000", "test90000"]"
                        ]
                    }
                }
            },
            "rejectedPlans" : [ ]
        },
        "executionStats" : {
            "executionSuccess" : true,
            "nReturned" : 1,
            "executionTimeMillis" : 1,
            "totalKeysExamined" : 1,
            "totalDocsExamined" : 1,
            "executionStages" : {
                "stage" : "FETCH",
                "nReturned" : 1,
                "executionTimeMillisEstimate" : 0,
                "works" : 2,
                "advanced" : 1,
                "needTime" : 0,
                "needYield" : 0,
                "saveState" : 0,
                "restoreState" : 0,
                "isEOF" : 1,
                "invalidates" : 0,
                "docsExamined" : 1,
                "alreadyHasObj" : 0,
                "inputStage" : {
                    "stage" : "IXSCAN",
                    "nReturned" : 1,
                    "executionTimeMillisEstimate" : 0,
                    "works" : 2,
                    "advanced" : 1,
                    "needTime" : 0,
                    "needYield" : 0,
                    "saveState" : 0,
                    "restoreState" : 0,
                    "isEOF" : 1,
                    "invalidates" : 0,
                    "keyPattern" : {
                        "name" : 1
                    },
                    "indexName" : "name_1",
                    "isMultiKey" : false,
                    "multiKeyPaths" : {
                        "name" : [ ]
                    },
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 2,
                    "direction" : "forward",
                    "indexBounds" : {
                        "name" : [
                            "["test90000", "test90000"]"
                        ]
                    },
                    "keysExamined" : 1,
                    "seeks" : 1,
                    "dupsTested" : 0,
                    "dupsDropped" : 0,
                    "seenInvalidated" : 0
                }
            }
        },
        "serverInfo" : {
            "host" : "vm10-81-6-235.ksc.com",
            "port" : 27017,
            "version" : "3.6.8",
            "gitVersion" : "20190823"
        },
        "ok" : 1,
        "operationTime" : Timestamp(1592970956, 1),
        "$clusterTime" : {
            "clusterTime" : Timestamp(1592970956, 1),
            "signature" : {
                "hash" : BinData(0,"a4cVUU0kd/OWDgJMRu8s/qy4M+8="),
                "keyId" : NumberLong("6841423388271968257")
            }
        }
    }
    "executionTimeMillis" : 1,

     创建唯一索引

    db.testindex..ensureIndex({"name":1},{"unique":true})

    联合索引.对多个属性建立一个索引,按照find()出现的顺序

    db.testindex..ensureIndex({name:1,age:1})

    查看文档所有索引

    db.testindex.getIndexes()

    删除索引

    db.testindex.dropIndexes("索引名称")

    安全:超级管理员

    • 为了更安全的访问mongodb,需要访问者提供用户名和密码,于是需要在mongodb中创建用户
    • 采用了角色-用户-数据库的安全管理方式
    • 常用系统角色如下:
      • root:只在admin数据库中可用,超级账号,超级权限
      • Read:允许用户读取指定数据库
      • readWrite:允许用户读写指定数据库
    • 创建超级管理用户
    use admin
    db.createUser({
        user:'admin',
        pwd:'123',
        roles:[{role:'root',db:'admin'}]
    })

    启用安全认证

    • 修改配置文件
    sudo vi /etc/mongod.conf
    
    • 启用身份验证
    • 注意:keys and values之间一定要加空格, 否则解析会报错
    security:
      authorization: enabled
    
    • 重启服务
    sudo service mongod stop
    sudo service mongod start
    
    • 终端连接
     mongo -u 'admin' -p '123' --authenticationDatabase 'admin'
    

    普通用户管理

      查看所有用户:

      在admin库内 

    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> show collections
    system.keys
    system.users
    system.version
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> db.system.users.find()

    { "_id" : "admin.ksc_admin", "user" : "ksc_admin", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "Q0+etwmKSBsIbCI+qdZknQ==", "storedKey" : "UknzmBNc0JX46VDPaQsY+sSpNcI=", "serverKey" : "HEK1kuxzZcNnchNWL4QVd7/PKJU=" } }, "roles" : [ { "role" : "root", "db" : "admin" } ] }
    { "_id" : "admin.root", "user" : "root", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "YgJf62mAewhc8w5Vjun+EQ==", "storedKey" : "ellqgsZ3IPbLnVq6JDJ7CdQq9uY=", "serverKey" : "QHrYXXo/wYDREdxN5x0mtzilH1M=" } }, "roles" : [ { "role" : "root", "db" : "admin" } ] }
    { "_id" : "dbs.admin", "user" : "admin", "db" : "dbs", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "EbcI3zDnHpHUi+qDS5NEmQ==", "storedKey" : "kYX/O8NJ31CVhTC6N4ulyGrS/0U=", "serverKey" : "nQIf53rbZqSfB2ht8RttAMpOyrw=" } }, "roles" : [ { "role" : "root", "db" : "admin" } ] }
    { "_id" : "dbs.zhangmingda", "user" : "zhangmingda", "db" : "dbs", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "Vvlps5YCOeta/4wNTFNJ9Q==", "storedKey" : "HuAl5ZIuuStHEPLk6vSnqPFgE2Q=", "serverKey" : "B1pkSfHJG8XzHasdgxAdEfO4mGE=" } }, "roles" : [ { "role" : "root", "db" : "admin" } ] }
    { "_id" : "test1.zhangmingda", "user" : "zhangmingda", "db" : "test1", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "OaDCMsS6OvNCvxkMBaxRRw==", "storedKey" : "cv/XLiwohe3zfX5MPdeD/1vb8MY=", "serverKey" : "6QebKzNxEG8CNtBheVDEi5YLkUo=" } }, "roles" : [ { "role" : "readWrite", "db" : "test1" } ] }
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY>

     生产中用法

    • 使用超级管理员登录,然后进入对应的库,进行用户管理操作(如对某个库给某个用户授权----生产上的用法)
    • 查看当前数据库的用户(切换到对应的库内,show users)
    use test1
    show users
    
    • 创建普通用户
    db.createUser({
        user:'t1',
        pwd:'123',
        roles:[{role:'readWrite',db:'test1'}]
    })
    • 终端连接
    mongo -u t1 -p 123 --authenticationDatabase test1
    • 切换数据库,执行命令查看效果

    • 修改用户:可以修改pwd、roles属性

    db.updateUser('t1',{pwd:'456'})

    复制(副本集)

    什么是复制

    • 复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性,并可以保证数据的安全性
    • 复制还允许从硬件故障和服务中断中恢复数据

    为什么要复制

    • 数据备份
    • 数据灾难恢复
    • 读写分离
    • 高(24* 7)数据可用性
    • 无宕机维护
    • 副本集对应用程序是透明

    复制的工作原理

    • 复制至少需要两个节点A、B...
    • A是主节点,负责处理客户端请求
    • 其余的都是从节点,负责复制主节点上的数据
    • 节点常见的搭配方式为:一主一从、一主多从
    • 主节点记录在其上的所有操作,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致
    • 主节点与从节点进行数据交互保障数据的一致性

    复制的特点

    • N 个节点的集群
    • 任何节点可作为主节点
    • 所有写入操作都在主节点上
    • 自动故障转移
    • 自动恢复

    设置复制节点

    • 接下来的操作需要打开多个终端窗口,而且可能会连接多台ubuntu主机,会显得有些乱,建议在xshell中实现
    • step1:创建数据库目录t1、t2
    • 在Desktop目录下演示,其它目录也可以,注意权限即可
    mkdir t1
    mkdir t2
    
    • step2:使用如下格式启动mongod,注意replSet的名称是一致的
    mongod --bind_ip 192.168.196.128 --port 27017 --dbpath ~/Desktop/t1 --replSet rs0
    mongod --bind_ip 192.168.196.128 --port 27018 --dbpath ~/Desktop/t2 --replSet rs0
    
    • step3:连接主服务器,此处设置192.168.196.128:27017为主服务器
    mongo --host 192.168.196.128 --port 27017
    
    • step4:初始化
    rs.initiate()
    
    • 初始化完成后,提示符如下图:

    • step5:查看当前状态
    rs.status()
    
    • 当前状态如下图:

    • step6:添加复本集
    rs.add('192.168.196.128:27018')
    
    • step7:复本集添加成功后,当前状态如下图:

    • step8:连接第二个mongo服务
    mongo --host 192.168.196.128 --port 27018
    
    • 连接成功后,提示符如下图:
    • step9:向主服务器中插入数据
    use test1
    for(i=0;i<10;i++){db.t1.insert({_id:i})}
    db.t1.find()
    
    • step10:在从服务器中插查询
    • 说明:如果在从服务器上进行读操作,需要设置rs.slaveOk()
    rs.slaveOk()
    db.t1.find()
    

    其它说明

    • 删除从节点
    rs.remove('192.168.196.128:27018')
    
    • 关闭主服务器后,再重新启动,会发现原来的从服务器变为了从服务器,新启动的服务器(原来的从服务器)变为了从服务器

     副本集正确的连接方式

    当客户端连接副本集时,如果以primary地址连接实例,当发生上述情况时,P节点降级为Secondary节点,客户端将无法继续执行写操作。为了解决这一问题,连接线上数据时,最好是使用副本集方式连接副本集。

    正确连接副本集,可以参考官网的 https://docs.mongodb.com/manual/reference/connection-string/该文档描述了官方MongoDB drivers连接MongoDB实例的URI格式。

    URI形如:

    mongodb://[username:password@]host1[:port1][,...hostN[:portN]]][/[database][?options]]

    options都怎么写:参考官方https://docs.mongodb.com/manual/reference/connection-string/#connections-connection-options


    mongo shell 连接常见报错:FailedToParse: Password must be URL Encoded for mongod

    [root@zmdsdkhost ~]# mongo "mongodb://root:Wyf@1314@192.168.1.21:27017,192.168.1.22:27017/test?authSource=admin&replicaSet=8e08b0f0-7db1-49aa-8274-05c3bca10e67&readPreference=secondaryPreferred" 
    FailedToParse: Password must be URL Encoded for mongodb:// URL: mongodb://root:Wyf@1314@192.168.1.21:27017,192.168.1.22:27017/test?authSource=admin&replicaSet=8e08b0f0-7db1-49aa-8274-05c3bca10e67&readPreference=secondaryPreferred
    try 'mongo --help' for more information

    原因:密码必须经过URL编码 如果用户名或密码包含at符号@,冒号:,斜杠/或百分号%字符,请使用百分比编码

    参考https://docs.mongodb.com/manual/reference/connection-string/#examples

    连接成功示例:

    (base) [root@zmdsdkhost ~]# mongo "mongodb://root:Wyf%401314@192.168.1.21:27017,192.168.1.22:27017/test?authSource=admin&replicaSet=8e08b0f0-7db1-49aa-8274-05c3bca10e67&readPreference=secondaryPreferred" 
    MongoDB shell version v3.6.14
    connecting to: mongodb://192.168.1.21:27017,192.168.1.22:27017/test?authSource=admin&gssapiServiceName=mongodb&readPreference=secondaryPreferred&replicaSet=8e08b0f0-7db1-49aa-8274-05c3bca10e67
    2020-06-25T11:10:22.446+0800 I NETWORK  [thread1] Starting new replica set monitor for 8e08b0f0-7db1-49aa-8274-05c3bca10e67/192.168.1.21:27017,192.168.1.22:27017
    2020-06-25T11:10:22.447+0800 I NETWORK  [ReplicaSetMonitor-TaskExecutor-0] Successfully connected to 192.168.1.22:27017 (1 connections now open to 192.168.1.22:27017 with a 5 second timeout)
    2020-06-25T11:10:22.447+0800 I NETWORK  [thread1] Successfully connected to 192.168.1.21:27017 (1 connections now open to 192.168.1.21:27017 with a 5 second timeout)
    Implicit session: session { "id" : UUID("f4f2f8ba-1c7a-4872-8756-3b5efb2d493d") }
    MongoDB server version: 3.6.8
    8e08b0f0-7db1-49aa-8274-05c3bca10e67:PRIMARY> 

    下面摘录几个常用的options:

    authSource= //认证用的数据库
    replicaSet=  //副本集名称
    readPreference=secondaryPreferred  //实现读写分离
    tls=true                //启用TLS加密传输(从MongoDB 4.2开始可用
    connectTimeoutMS   //超时之前尝试连接的时间(以毫秒为单位)。默认值是永不超时,尽管不同的驱动程序可能有所不同。请参阅驱动程序 文档。

    socketTimeoutMS      //尝试超时之前在套接字上尝试发送或接收的时间(以毫秒为单位)。默认值是永不超时,尽管不同的驱动程序可能有所不同。请参阅 驱动程序文档。

    连接池选项

    maxPoolSize  //连接池中的最大连接数。默认值为100

    minPoolSize //连接池中的最小连接数。默认值为0。 (注意minPoolSize并非所有驱动程序都支持该选项。有关驱动程序的信息,请参阅 驱动程序文档。)

    maxIdleTimeMS  //在删除和关闭连接之前,连接在池中可以保持空闲状态的最大毫秒数。 (并非所有驱动程序都支持此选项。)

    waitQueueMultiple //驱动程序将maxPoolSize 值乘以的数字,以提供允许最大数量的线程从池中等待可用连接。有关默认值,请参见/ drivers 文档。

    写选项

    wtimeoutMS  // wtimeoutMS指定写关注的时间限制(以毫秒为单位)。如果wtimeoutMS0(默认),写操作永远不会超时。有关更多信息,请参见wtimeout

    备份

    工具:在mongodb的服务端软件内

    下载安装包

    cd /usr/local/src
    
    wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.0.6.tgz
    
    tar -zxvf   mongodb-linux-x86_64-3.0.6.tgz
    
    mv mongodb-linux-x86_64-3.0.6   /usr/local/mongodb

    添加全局变量 

    MongoDB 的可执行文件位于 bin 目录下

    可以将其添加到 PATH 路径中:

        export   PATH=/usr/local/mongodb/bin:$PATH   
    • 语法
    mongodump  -u 用户名 -p 密码 --authenticationDatabase <认证数据库> -h <ip:port> -d <dbname> -o <dbdirectory>
    
    • -h:服务器地址,也可以指定端口号
    • -d:需要备份的数据库名称
    • -o:备份的数据存放位置(目录/文件夹位置),此目录中存放着备份出来的数据
    • 例:
    (base) [root@zmdsdkhost bin]# ./mongodump -u root -p Wyf@1314 -h 192.168.1.21:27017 --authenticationDatabase admin -d py3 -o /opt/my_mongodb_py3db.bak
    2020-06-25T11:29:57.197+0800    writing py3.stu to /opt/my_mongodb_py3db.bak/py3/stu.bson
    2020-06-25T11:29:57.198+0800    writing py3.stu metadata to /opt/my_mongodb_py3db.bak/py3/stu.metadata.json
    2020-06-25T11:29:57.199+0800    done dumping py3.stu (4 documents)

     

    恢复

    • 语法
    mongorestore -u 用户名 -p 密码 --authenticationDatabase <认证数据库> -h <ip:port> -d <dbname> --dir <backed_dbdirectory>
    • -h:服务器地址
    • -d:需要恢复的数据库实例
    • --dir:备份数据所在位置
    • 例2
    (base) [root@zmdsdkhost bin]# ./mongorestore -u root -p Wyf@1314 -h 192.168.1.21:27017 --authenticationDatabase admin -d py3bak --dir /opt/my_mongodb_py3db.bak/py3/
    2020-06-25T11:53:04.075+0800    building a list of collections to restore from /opt/my_mongodb_py3db.bak/py3/ dir
    2020-06-25T11:53:04.076+0800    reading metadata file from /opt/my_mongodb_py3db.bak/py3/stu.metadata.json
    2020-06-25T11:53:04.076+0800    restoring py3bak.stu from file /opt/my_mongodb_py3db.bak/py3/stu.bson
    2020-06-25T11:53:04.085+0800    restoring indexes for collection py3bak.stu from metadata
    2020-06-25T11:53:04.086+0800    finished restoring py3bak.stu (4 documents)
    2020-06-25T11:53:04.086+0800    done
    (base) [root@zmdsdkhost bin]# 
    查看已恢复的备份
     

    与python交互

    进入虚拟环境
    sudo pip install pymongo
    或源码安装
    python setup.py
    
    • 引入包pymongo
    import pymongo
    
    • 连接,创建客户端
    client=pymongo.MongoClient("localhost", 27017)

    副本集方式连接客户端
    写法一:url方式
    >>> client = pymongo.MongoClient( "mongodb://root:Wyf%401314@192.168.1.21:27017,192.168.1.22:27017/test?authSource=admin&replicaSet=8e08b0f0-7db1-49aa-8274-05c3bca10e67&readPreference=secondaryPreferred&connectTimeoutMS=5000")
    >>> r = client.py3.stu.find_one()
    >>> print(r)
    {'_id': ObjectId('5ef1d0b85192e418c6c6e198'), 'name': 'zmd', 'age': 22.0, 'gender': True}

    写法二:options方式

    >>> import pymongo
    >>> client = pymongo.MongoClient(
    ...     host=['192.168.1.21:27017','192.168.1.22:27017'],
    ...     username="root",
    ...     password='Wyf@1314', #这里无需转为URL%的格式
    ...     replicaSet='8e08b0f0-7db1-49aa-8274-05c3bca10e67',
    ...     authSource='admin',
    ... )
    >>> r = client.py3.stu.find()
    >>> print(1)
    1
    >>> print(r)
    <pymongo.cursor.Cursor object at 0x7efdfa528eb8>
    >>> r = client.py3.stu.find_one()
    >>> print(r)
    {'_id': ObjectId('5ef1d0b85192e418c6c6e198'), 'name': 'zmd', 'age': 22.0, 'gender': True}
     
    • 获得数据库test1
    db=client.test1
    
    • 获得集合stu
    stu = db.stu
    
    • 添加文档
    s1={name:'gj',age:18}
    s1_id = stu.insert_one(s1).inserted_id
    
    • 查找一个文档
    s2=stu.find_one()
    
    • 查找多个文档1
    for cur in stu.find():
        print cur
    
    • 查找多个文档2
    cur=stu.find()
    cur.next()
    cur.next()
    cur.next()
    
    • 获取文档个数
    print stu.count()
     
  • 相关阅读:
    HTTP协议:POST请求
    HTTP协议:GET请求
    HTTP协议
    理解server.xml
    用session实现简单的购物
    session:的工作原理
    session:的生命周期
    session:例子代码(一个session为一个会话而服务)
    session概述&作用
    时间复杂度和空间复杂度
  • 原文地址:https://www.cnblogs.com/zhangmingda/p/13183812.html
Copyright © 2011-2022 走看看