mongodb

一、Mongodb学习笔记



基础实验方面>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

1.mongo:文档数据库,存储的是文档(Bson->json的二进制化)
特点:内部执行引擎为JS解释器,把文档存储为bson结构,在查询时转为js对象,并且可以通过js语法来操作




2.mongo与sql数据库相比:

sql数据库:结构化数据,定好了表格后,每一行的内容都是结构化的
mongo:文档数据,表下的数据都可以有自己的特点(有自己独特的属性和值),存储更加灵活



3.下载安装(直接去官网下载)

     1.bin目录下文件解释:核心:
                      mongo:客户端
                      mongod:服务端
                      mongos:路由器(集群分片的时候用)
                       
                      二进制导入导出:
                      mongodump:导出bson数据
                      mongorestore:导入bson数据
                      bsondump:bson转为json
                      mongolog:运行日志

                      数据导出导入:
                      mongoexport:导出容易识别json、csv、tsv格式文档
                      mongoimport:导入json、csv、tsv
                  
                      运维工具:
                      mongosniff、mongotop、mongostat:观察mongo运行状态(运维时使用)

    2.启动mongo服务:mongod --dbpath --logpath --fork(后台运行模式) --port 27017





4.mongo常用命令:
    
    1.基本入门命令:
              show dbs                 查看当前数据库
              use databaseName         选择数据库
              show tables\collections  查看表

              use databaseName         隐式创建数据库(如果存在则使用,不存在则在创建collection后创建此数据库,
                                       如果没有创建collection,就不会创建库)

              db.createCollection("collectionName")   直接创建表,不需要去定义结构(也可以隐式创建,在写入内容的时候自动创建)
              db.collectionName.insert({key:value})   往表中插入数据(创建数据的时候,如果没有指定_id的值,数据库会默认指定)
              db.collectionName.find()                查看表中的数据
              
              db.collectionName.drop()                删除collection(删除成功返回true,否则false)
              
              db.collectionName.count()               统计数据条数
    2.基本CURD命令:
        增:
            db.collectionName.insert({key:value})     增加单个文档(key/value类型任意)

            db.collectionName.insert({_id:intValue,key:value}) 增加并指定id,不指定会自动生成

            db.collectionName.insert(                  增加多个文档
                                    [
                                    {_id:1,key:value},
                                    {_id:2,key:value}
                                    ]
                                    )

        删:
            db.collectionName.remove({查询表达式,true\false}) 条件可以是特有的id、查询语句内容例如:{_id:10},就可以把这个文档_id:10删除
                                                            如果不写条件内容,整个collection都被清空,true则找到多个匹配也只删除一行, false则找到多少删除多少

        查:find()、findOne()
            db.collectionNmae.find({查询表达式,})   查询表达式也为json格式,如果不写则全部查询
            db.collectionName.find({查询语句},{key:1})   查询某个属性,默认id也被查出来
            db.collectionName.find({},{key:1,_id:0})  查询某个属性,不查询_id,要查询的为1,不查询的不写,不查_id的必须写0

 


        改:update({查询语句},新值,选项)
         db.collectionName.update({表达式},{新值},可选参数)   新值会覆盖掉原来全部的内容----1
         db.collectionName.update({表达式},{$set:{要修改的key->value}},{upsert:true})   后面参数为可选,为true,则$set的内容存在则修改,不存在则添加,这样的话只修改需要修改的内容,其他不改变(修改某个列)--2

         修改时的赋值表达式:$set  修改某个值(有则)
                             $unset 删除某个列key
                             $rename 重名某个列key
                             $incr 增长某个列




    3.查询表达式深入:
          1.最简单查询表达式
             {field:value}              查询field列的值为value的文档
          2.$ne    (!=查询表达式)  
             {field:{$ne:value}}       查field列的值,不等于value的文档
          3.$nin  {}

          4.$gt  {大于}
              {field:{$gt:value}}
          5.$lt   {小于}
              {field:{$lt:value}}
          6.$gte  {大于或等于}
          
          7.lte   {小于或等于}

          8.$in   {在哪个区域内}
            {field:{$in:[start:end]}

          9.$all  {所有}
            {field:{$all:{[k1,k2,k3]}}} 同时存在k1,k2,k3采薇真



          10.$or   {$or:同下}  或

          11.$and  {$and:[{field:{条件}},{field:{条件}}]}  连接两个查询条件,将要连接的条件放在数组内

          12.$nor  {$nor:同上}  非

          13.$not

          14.$exists  某列存在则为true  取出存在某个列的文档
                   {field:{$exists:1}}
          15.$mod:满足某求余条件为真

                  {field:{$mod:[5:0]}}  取出模5后等于0的列

          16.$typc:数据为某类型则为真

          
          这两种方式尽量少用,查询效率不高,大数据的时候
          17.$where 表达式为真则为真
              {$where:'this.key<100'} 说明:找出key对应value小于100的值(将二进制首先转为json,再比较,比较慢,表达式容易写)

          18.$regex  正则表达式为真则为真
                     {$regex:{field:/正则表达式内容/}}



5.游标操作
 
  1.解决一次性取出过多的数据
    var mycursor=db.collectionName.find(query.project);   声明游标
    mycurson.hasNext() 判断游标是否到末尾
    cursor.Next();     取出游标下一个单元

    例子:
          var mycursor=db.bar.find({_id:{$lte:5}})
          print(mycursor.next())      //无法直接输出bson的值
          printjson(mycursor.next())   //需要转为json格式输出


 2.游标在分页中如何使用?使用skip()函数和limit()函数来查询
   var mycursor=db.bar.find().skip(995);   说明前面跳过995行
   mycursor.forEach(function(obj){printjson(obj)});  系统的迭代函数(循环取出文档对象内容)
   
   var mycursor=db.bar.find().skip(200).limit(10);  说明:跳过200条,取出10条;
   mycursor.toArray();        说明:也可以直接转为array,然后输出
   mycursor.toArray()[1];     说明:指明取出数组的第几个





6.索引


   索引作用类型:
               单列索引
               多列索引
               子文档索引

    索引性质:
               普通索引
               唯一索引   db.bar.ensureIndex({field:1/-1},{unique:true})    默认的_id就是唯一索引,索引的value不能重复
               稀疏索引   db.bar.ensureIndex({field:1/-1},{sparse:true})    稀疏索引:不存在的field不会建立索引,普通的话会建立对应
                                                                               null
               哈希索引   db.bar.ensureIndex({field:"hashed"})           适用于随机性的散列,查询速度快,但是无法对范围查询进行优化



  1.创建索引:db.bar.ensureIndex({field:1/-1})  说明:参数为1是升序,-1是降序
              db.bar.dropIndex({一个完整索引})  
              db.bar.dropIndexe()   说明:删除所有所引


  2.多列索引:重要解释:健多列索引是把这几个列作为一个集合来查询,如果要查询多个列的时候
            创建多列索引:db.bar.ensureIndex({表达式1,表达式2,.....})    



  3.子文档索引:
             
             建立子文档索引:db.fruits.ensureIndex({'fieldName.fieldNameChild':1})


  4.重建索引:一个表经过多次修改,导致表的文件产生空洞,索引文件也是,可以通过索引重建来提交的
             db.collectionName.reIndex()






运维方面>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


7.用户管理

  1.注意:
        A、mongodb有个admin数据库,要进行服务器配置的操作,需要先切换到admin数据库,相当于切换到超级管理员
        B、mongo的用户是以数据库为单位来建立的,每个用户有自己的管理员
        C、在设置用户时,需要在admin数据库下建立管理员,这个管理员登录后相当于超级管理员

 
  2.添加用户:
         db.createUser({user:userName,pwd:userPassword,roles:[{role:"userAdminAnyDatabase",db:"admin"}]})

          1.创建超级管理员

          use admin
          db.createUser(
           {
              user: "adminUserName",
              pwd: "userPassword",
              roles:
              [
                {
                 roles: "userAdminAnyDatabase",
                 db: "admin"
                }
              ]
           }
           )
 

                  说明:超级用户的role有两种,userAdmin或者userAdminAnyDatabase(比前一种多加了对所有数据库的访问)。
                        db是指定数据库的名字,admin是管理数据库。
 



        2. 用新创建的用户登录
 
           mongo --host xxx -u adminUserName -p userPassword --authenticationDatabase admin
 

        3. 查看当前用户的权限
 
           db.runCommand(
          {
            usersInfo:"userName",
            showPrivileges:true
          }
          )
 

        4. 创建一般用户,也是用createUser
 
         use db01
         db.createUser(
         {
           user:"oneUser",
           pwd:"12345",
           roles:[
           {role:"read",db:"db01"},
           {role:"read",db:"db02"},
           {role:"read",db:"db03"}
           ]
         }
         )
 

        5. 创建一个不受访问限制的超级用户

           use admin
           db.createUser(
           {
             user:"superuser",
             pwd:"pwd",
             roles:["root"]
          }
          )
 

        6. 修改密码
 
         use admin
         db.changeUserPassword("username", "xxx")
 

        7. 查看用户信息
 
          db.runCommand({usersInfo:"userName"})
 

        8. 修改密码和用户信息
 
          db.runCommand(
         {
           updateUser:"username",
           pwd:"xxx",
           customData:{title:"xxx"}
         }
         )
 



8.mongodb的导入与导出
     
     1.导入导出可以操作本地数据库,也可以是远程的一般选项如下
        -h            主机
        -port port    端口
        -u username   用户名
        -p password   密码

     2.mongodb导出的是json格式的文件  mongoexport   (与其他数据库进行数据交互)

        可以选择导出哪个库,哪个collection,哪几列,哪几行

         -d 库名
         -c 表名
         -f field1,field2 列名
         -o 导出的文件名
         -q 查询条件
         --cvs 导出cvs格式


        导出csv格式:mongoexport.exe -d shop -c bar -f _id,aricle -q "{_id:{$let:100}}" --csv D:/data/export/myexport.csv
        导出json格式:mongoexport.exe -d shop -c bar -f _id,aricle -q "{_id:{$let:100}}" D:/data/export/myexport.json


     3.mongodb导入选项  mongoimport.exe
        -d 待导入的数据库
        -c 待导入的表
        --type csv/json(默认为json)
        --file 备份文件路径

        json导入例子:mongoimport.exe -d shop -c im1(可以是存在的,不存在则自动创建) --type json --file D:/data/export/myexport.json
        csv导入例子:mongoimport.exe -d shop -c im2 --type csv -f _id,article --file headerline(跳过第一行-->_id和article) D:/data/export/myexport.csv





     4.二进制文件导出mongodump.exe 导出二进制bson结构及其索引信息(做备份,预防被攻击,数据丢失)

        -d 库名
        -c 表名
        -f field1,field2 列名
         
        例子:mongodump.exe -d shop -c im -o D:/data/export/

         


     5.二进制文件导入mongorestore.exe  导入二进制文件
       -d 库名
       --directoryperdb  要导入的路径

       例子:mongorestore.exe  -d shop --directoryperdb D:/data/export/







9.replication复制集
    
    replication set多台服务器维护相同的数据副本,提高服务器的可用性

    1.启动3个服务,打开数据库方式:完整打开方式+“--replSet 复制集名”   把数据库都指向相同的复制集
     Mongod.exe --dbpath D:/MongodbTest/data/db_17 --logpath D:/MongodbTest/data/log17/log17.log --logappend --port 27017


    2.配置
    var reconf={_id:'rs2',members:[{_id:0,host:'127.0.0.1:27017'},{_id:1,host:'127.0.0.1:27018'},{_id:2,host:'127.0.0.1:27019'}]}

    3.根据配置做初始化
      rs.initiate(reconf);

    4.查看状态  rs.status

    5.添加节点  rs.add("127.0.0.1:27020")     里面是要添加的端口号

    6.删除节点  rs.remove("127.0.0.1:27020")  里面是要删除的端口号






10.shard分片

   1.原因:数据量太大的时候(几亿眺),需要将不同的数据往指定的mongo存储


   2.配置信息
     1.要有(N>2)个mongo服务器做片节点
     2.要有configserver维护meta信息
     3.要设计好数据的分片规则(configsvr才方便维护)
     4.要启动mongos做路由


   3.动手操作

     1.启动两台mongod作为分片服务---启动方式:与一般的启动方式相同----------------(27017+27018)

     2.启动一台mongod作为configsever---启动方式正常启动方式+(--configsvr)()-------(27019)


    以下操作均在mongos命令下完成

     3.启动路由器:mongos.exe --logpath --port  --configdb(指定路由服务的ip)------与configsvr联系

                         例如:mongos.exe --logpath D:/data/mslog.log--port 27030     --configdb 127.0.0.1:21019

     4.mongos命令下增加节点(与片联系27017,27018)
        命令:sh.addShard('IP:port')
        例如:sh.addShard("127.0.0.1:21017")

     5.查看状态:mongos命令下,sh.status()


     6.configsvr设置
        也是在mongo命令下

                 声明哪个库可以分片:sh.enableSharding('databasesName')                            数据库名
                              例子:sh.enableSharding('shop')       商品数据库

                 声明哪个表分片   :sh.shardCollection('databasesName.collectionName',field)       feild分片的键

                              例子:sh.shardCollection('databasesName.collectionName',{good_id:1})  以商品id为键


                  chunk的概念:N个文档形成一个chunk
                               优先存在某个片上
                               当这片上的chunk比另一个片的chunk区别比较明显时(大于3),会把本片上的chunk移动到另一个上,以chunk为单位,维护片之间的均衡



                 详细内容:

                          问题1.为什么插入10万条数据,才有两个chunk?
                               答:configsvr数据库中chunk默认为64M,比较大,这里可以用命令修改

                                     db.settings.find()---->找到chunk设置,默认chunk为64M

                                     db.settings.save({_id:'chunksize',{set:{value:4}}})   这里改为4

                          问题2.当随着数据的增多,shard之间有chunk来回移动,会带来哪些问题?
                                 答:服务器之间的IO变大,速度会减慢

                          问题3.能否定义一个规则,某N条数据作为一个chunk,预先分配M个chunk,M个预分配在不同的片上,后面的数据直接进入                                                                自己预分配好的chunk,不再来回移动

                                 答:手动预先分片    
                                     实际操作:
                                              1.预先分chunk:

                                              sh.shardCollection('databasesName.collectionName',field)

                                              for(var i=0;i<=40;i++){
                                                sh.splitAt('databaseName.collectionName',{fieldkey:i*1000}) 说明:在1k-2k--切块

                                              }

                                              2.添加数据-->数据会添加到预先分配好的chunk上,chunk就不会再来回移动





                



11.replication和shard的结合使用
        
        思路:将复制集主作为分片来使用,其他如分片操作一般







12.PHP-mongodb的扩展编译及应用案例

    1.扩展编译步骤(win)

      1.下载对应php-mongodb数据库的扩展
      2.找到php_mongo.dll,复制到ext扩展文件夹下
      3.php配置文件中添加;extction=php_mongo.dll
      4.将php根目录下的libsasl.dll复制到apache的bin下即可配置成功


    2.应用小案例(短网址应用生成案例)
      
      这里省略





13.聚集运算之group
    

  1.分组统计:groud()------------->不支持shard,无法分布式运算,需要我们自己写业务逻辑

    等价的sql语法:select max(shop_price) from goods grounp by cat_id

    语法:db.colllectionName.groud(document)--->{
                                                  key:{key1:1,key2:1},   说明:相当于 "grounp by cat_id"

                                                  cond:{},               说明:相当于 "where"      条件语句

                                                  reduce:function(curr,result){   说明:自己写的一个聚合函数,实现业务逻辑

                                                  },
                                                  initial:{},                   说明 :进入时触发
                                                  finalize:function(){          说明 :结束时触发

                                                  }

                                              }





  2.简单聚合:aggregate()--------->支持分布式运算

    MongoDB 聚合
    MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似sql语句中的 count(*)。

    aggregate() 方法
    MongoDB中聚合的方法使用aggregate()。

    语法
    aggregate() 方法的基本语法格式如下所示:
    >db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
    实例
    集合中的数据如下:
    {
    _id: ObjectId(7df78ad8902c)
    title: 'MongoDB Overview',
    description: 'MongoDB is no sql database',
    by_user: 'w3cschool.cc',
    url: 'http://www.w3cschool.cc',
    tags: ['mongodb', 'database', 'NoSQL'],
    likes: 100
   },
   {
   _id: ObjectId(7df78ad8902d)
   title: 'NoSQL Overview',
   description: 'No sql database is very fast',
   by_user: 'w3cschool.cc',
   url: 'http://www.w3cschool.cc',
   tags: ['mongodb', 'database', 'NoSQL'],
   likes: 10
   },
  {
   _id: ObjectId(7df78ad8902e)
   title: 'Neo4j Overview',
   description: 'Neo4j is no sql database',
   by_user: 'Neo4j',
   url: 'http://www.neo4j.com',
   tags: ['neo4j', 'database', 'NoSQL'],
   likes: 750
  },
  现在我们通过以上集合计算每个作者所写的文章数,使用aggregate()计算结果如下:
  > db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : 1}}}])
  {
   "result" : [
      {
         "_id" : "w3cschool.cc",
         "num_tutorial" : 2
      },
      {
         "_id" : "Neo4j",
         "num_tutorial" : 1
      }
   ],
   "ok" : 1
  }
  >

  以上实例类似sql语句: select by_user, count(*) from mycol group by by_user
  在上面的例子中,我们通过字段by_user字段对数据进行分组,并计算by_user字段相同值的总和。
  下表展示了一些聚合的表达式:
  表达式       描述            实例

  $sum         计算总和。      db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : "$likes"}}}])

  $avg         计算平均值      db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$avg : "$likes"}}}])

  $min         获取集合中所有文档对应值得最小值。   db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$min : "$likes"}}}])

  $max         获取集合中所有文档对应值得最大值。   db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$max : "$likes"}}}])

  $push        在结果文档中插入值到一个数组中。     db.mycol.aggregate([{$group : {_id : "$by_user", url : {$push: "$url"}}}])
  $addToSet    在结果文档中插入值到一个数组中,但不创建副本。   db.mycol.aggregate([{$group : {_id : "$by_user", url : {$addToSet : "$url"}}}])

  $first      根据资源文档的排序获取第一个文档数据。            db.mycol.aggregate([{$group : {_id : "$by_user", first_url : {$first : "$url"}}}])

  $last      根据资源文档的排序获取最后一个文档数据             db.mycol.aggregate([{$group : {_id : "$by_user", last_url : {$last : "$url"}}}])






  3.强大统计:mapReduce()---->随着大数据的概念而流行

            1.强大的地方:1.支持分布式
                          2.支持大量服务器同时工作,用蛮力来统计

            2.工作过程:1.map-->映射        概念:把属于同一个组的数据映射到一个数组上,
                        2.reduce-->规约     概念:把数组的数据进行运算

            3.map函数

              var map=function(){
              emit(this.cat_id,this.shop_price);
              }

              var reduce=function(key,value){
                return Array.sum(values)
              }
              
              {
              query  query:{status:"A"};
              output out:"order_totals"
              }
            
            
            var m=function(){emit(this.age,this.name);}

            var r=function(age,name){return name}

            {query:{},out:res}

            db.emp.mapReduce(m,r,{out:'res'});



   1.mapReduce重点解释:
        MongoDB中的MapReduce相当于关系数据库中的group  by。使用MapReduce要实现两个函数Map和Reduce函数。Map函数调用emit(key,value),遍历
        Collection中所有的记录,将key与value传递给Reduce函数进行处理。

   2、MapReduce

       (1)其基本语法如下所示:
        db.runCommand({
        mapreduce:<collection>,
        map:<mapfunction>,
        reduce:<reducefunction>,
        [,query:<query filter object>]
        [,sort:<sorts the input objects using this key.Useful for optimization,like sorting by the emit key for fewer reduces>]
        [,limit:<number of objects to return from collection>]
        [,out:<see output options below>]
        [,keeptemp:<true|false>]
        [,finalize:<finalizefunction>]
        [,scope:<object where fields go into javascript global scope>]
        [,verbose:true]
        });

        参数说明:
        Mapreduce:要操作的目标集合
        Map:映射函数(生成键值对序列,作为reduce函数参数)
        Reduce:统计函数
        Query:目标记录过滤
        Sort:目标记录排序
        Limit:限制目标记录数量
        Out:统计结果存放集合(不指定使用临时集合,在客户端断开后自动删除)
        Keeptemp:是否保留临时集合
        Finalize:最终处理函数(对reduce返回结果进行最终整理后存入结果集合)
        Scope:向map、reduce、finalize导入外部变量
        Verbose:显示详细的时间统计信息。

       (2)执行查询的步骤
        A.MapReduce对指定的集合Collection进行查询
        B.对A的结果集进行mapper方法采集
        C.对B的结果执行finalize方法处理
        D.最终结果集输出到临时Collection中
        E.断开连接,临时Collection删除或保留。

    3、Map函数
        Map函数调用当前对象进行处理,把值传递给reduce函数。Map方法使用this来操作当前对象,至少调用一次emit(key,value)方法向reduce提供参数。其中的key为最终结果集中的_id。

    4、Reduce函数
        该函数接受map函数传来的key和value值。reduce函数中的key就是emit(key,value)中的key,而value是emit函数中同一个key返回的value数组。

    5.执行案例
        
        map函数:
                var map=function(){
                if(this.age<20){
                 emit(this.age,{name:this.name});
                }

                }

                var reduce=function(key,values){
                 var ciunt=0;
                 values.forEach(function(){count +=1;});
                 return count;
                }

                var result=db.runCommand(
                 {
                  mapreduce:"emp",
                  map:map
                  reduce:reduce,
                  out:"emp_result"
                 }
                );








14.mapReduce实际应用案例(对地震数据的分析)
       
       步骤:1.建立集群(复制集)
             2.建立分片(shard)
             3.将csv文件导入

             4.mapReduce操作

posted @ 2017-10-30 23:36  小白bruce  阅读(595)  评论(0编辑  收藏  举报