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()
     
  • 相关阅读:
    OSX安装nginx和rtmp模块(rtmp直播服务器搭建)
    用runtime来重写Coder和deCode方法 归档解档的时候使用
    Homebrew安装卸载
    Cannot create a new pixel buffer adaptor with an asset writer input that has already started writing'
    OSX下面用ffmpeg抓取桌面以及摄像头推流进行直播
    让nginx支持HLS
    iOS 字典转json字符串
    iOS 七牛多张图片上传
    iOS9UICollectionView自定义布局modifying attributes returned by UICollectionViewFlowLayout without copying them
    Xcode6 iOS7模拟器和Xcode7 iOS8模拟器离线下载
  • 原文地址:https://www.cnblogs.com/zhangmingda/p/13183812.html
Copyright © 2011-2022 走看看