一、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 270174.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操作