MongoDB 非关系型数据库 nosql的介绍 “NoSQL”一词最先于1998年被用于多个轻量级的关系数据库的名字 随着web2.0的快速发展,NoSQL概念在2009年被提了出来 NoSQL在2010年起,如今国内外众多应用网站, 如facebook、 google、 淘宝、 京东、 百度等 都在使用nosql开发高性能的产品 对于一名程序员来说,使用nosql已经成为一条必备技能 NoSQL最经常使用的解释是"non-relational","Not Only SQL"也被不少人接受,指的是非关系型的数据库 NOSQL和RDBMS的区别 NOSQL: 1)无需通过sql层的解析,读写性能很高 2)基于键值对,数据没有耦合性,容易扩展 3)存储数据的格式:nosql的存储格式是key,value形式、文档形式、图片形式等等, 而关系型数据库则只支持基础类型 RDBMS: 1)容易理解:二维表结构是很是贴近逻辑世界一个概念,关系模型相对网状、层次等 其余模型来讲更容易理解 2)使用方便:通用的SQL语言使得操做关系型数据库很是方便 3)易于维护:丰富的完整性(实体完整性、参照完整性和用户定义的完整性)大大减低了 数据冗余和数据不一致的几率 4)支持SQL,可用于复杂的查询 MongoDB的优点: 1)更高的写负载能力 默认状况下,对比事务安全,MongoDB更关注高的插入速度,若是你须要加载大量低价值的 业务数据,好比日志收集,那么MongoDB将很适合你的用例,可是必须避免在要求高事务安全 的情景下使用MongoDB,好比金钱交易 2)处理很大的规模的单表 数据库扩展是很是有挑战性的,当单表格大小达到5-10GB时,MySQL表格性能会毫无疑问的下降。 若是你须要分片而且分割你的数据库,MongoDB将很容易实现这一点 3)不可靠环境保证高可用性 设置副本集(主-从服务器设置)不只方便并且很快,此外,使用MongoDB还能够快速、 安全及自动化的实现节点(或数据中心)故障转移 4)使用基于位置的数据查询,查的更快 MongoDB支持二维空间索引,好比管道,所以能够快速及精确的从指定位置获取数据, MongoDB在启动后会将数据库中的数据以文件映射的方式加载到内存中,若是内存资源至关丰富的话, 这将极大地提升数据库的查询速度,由于内存的I/O效率比磁盘高多了 5)非结构化数据的爆发增加 增长列在有些状况下可能锁定整个数据库,或者增长负载从而致使性能降低,这个问题一般发生 在表格大于1GB的状况下,鉴于MongoDB的弱数据结构模式,添加1个新字段不会对旧表格有任何影响, 整个过程会很是快速;所以,在应用程序发生改变时,你不须要一个专门的DBA去修改数据库模式 6)缺乏专业的数据库管理员 若是你没有专业的DBA,同时你也不须要结构化你的数据及作join查询,MongoDB将会是你的首选, MongoDB很是适合类的持久化,类能够被序列化成JSON并储存在MongoDB;须要注意的是, 若是指望得到一个更大的规模,你必需要了解一些最佳实践来避免走入误区 常见类型数据类型: 1)null。{"x":null} 2)Boolean。{"x":true} 、{"x":false} 3)数据类型 在Mongodb Shell中默认使用64位浮点型数据,如{"x":2.32}、{"x":2},若是要使用整数类型则用 {"x":NumberInt(2)}、{"x":NumberLong(2)} 4)字符串。Mongodb中字符串采用UTF-8编码方式,{"x":"hello world"} 5)日期类型。{"x":new Date()} 6)正则表达式。 Mongodb中可以使用和javascript相同的正则表达式 {"x":/itbilu/i} 7)数据。Mongodb中数组的使用和javascript相同{"x":["hello","world"]} 8)内嵌文档。{"x":{"y":"Hello"}} 9)Id和ObjectId(),Mongodb每一个文档都会生成一个_id,若是你不指定时Mongodb会自动生成一个ObjectId对象, 10)代码。{"x":function aa(){}} 11)二进制 MongoDB的操做 默认端口号:27017 进入数据库 mongo 查看当前所在的数据库 db 查看全部的数据库(2) show dbs/show databases 使用数据库 use texts 删除数据库 db.dropDatabase() 集合(至关于关系型中的数据表) 查看全部集合 show collections 建立集合 格式: db.create.Collection(name,options) 建立一个名为test的集合 db.create.Collection(test) 删除集合 db.text.drop() 增长(插入)数据 会自动生成一个"_id"字段 db.test.insert({address:"陕西省西安市"}) db.test.insert({address:"陕西省西安市草滩六路",tel:"13141939210",gender:"man"}) 保存数据 # 若是ID重复,自动转为更新 db.test.save({}) 查看全部数据 db.test.find() 修改(更新数据) # 先插入小明的数据 * 3 db.test.insert({name:"小明"}) # 会把知足条件的第一条更新为{age:26} db.test.update({name:"小明"},{age:"26"}) # 只更新一条 db.test.update({name:"小明"},{$set:{age:"100"}}) # 所有更新 db.test.update({name:"小明"},{$set:{age:"88"}},{multi:true}) 删除数据 第一个参数为条件 # 所有删除 db.test.remove({name:"小明"},{justOne:"false"}) # 删除一条 db.test.remove({name:"小明"},{justOne:"true"}) 查询数据 普通查询 查看全部数据 db.test.find() 查看第一条数据 db.test.findOne() pretty用来美化样式 db.test.find().pretty() # 构造查询数据 db.test.insert([ {"name" : "郭靖", "hometown" : "蒙古", "age" : 20, "gender" : true }, {"name" : "黄蓉", "hometown" : "桃花岛", "age" : 18, "gender" : false }, {"name" : "华筝", "hometown" : "蒙古", "age" : 18, "gender" : false }, {"name" : "黄药师", "hometown" : "桃花岛", "age" : 40, "gender" : true }, {"name" : "段誉", "hometown" : "大理", "age" : 16, "gender" : true }, {"name" : "段王爷", "hometown" : "大理", "age" : 45, "gender" : true }, {"name" : "洪七公", "hometown" : "终南山", "age" : 18, "gender" : true } ]) # true表明男 false表明女 # 查询年龄等于18岁的 db.test.find({age:18}).pretty() # 查询年龄大于18岁的 db.test.find({age:{$gt:18}}).pretty() # 查询年龄大于等于18岁的 db.test.find({age:{$gte:18}}).pretty() # 查询年龄小于等于18岁的 db.test.find({age:{$lte:18}}).pretty() # 查询年龄不等于18岁的 db.test.find({age:{$ne:18}}).pretty() # 查询年龄大于18岁而且是男性的 db.test.find({$and:[{age:{$gt:18},gender:true}]}).pretty() db.test.find({age:{$gt:18},gender:true}).pretty() # 查询年龄小于等于18岁而且是男性的 db.test.find({age:{$lte:18},gender:true}).pretty() # 查询年龄小于等于18岁而且是女性的 db.test.find({age:{$lte:18},gender:false}).pretty() # 查询年龄小于等于18岁或者是女性的 db.test.find({$or:[{age:{$lte:18}},{gender:false}]}).pretty() # 查询年龄大于18岁或者hometown是大理 db.test.find({$or:[{age:{$gt:18}},{hometown:"大理"}]}).pretty() # 查询年龄大于18岁的而且姓名是郭靖的,或者hometown是大理 # 先查找年龄大于18且姓名是郭靖的 db.test.find({age:{$gt:18},name:"郭靖"}).pretty() # 再将hometown是大理的拼接进去 db.test.find({$or:[{age:{$gt:18},name:"郭靖"},{hometown:"大理"}]}).pretty() # 查询年龄是18,20,45的 db.test.find({age:{$in:[18,20,45]}}).pretty() skip和limit使用 注意: 先使用skip在使用limit的效率要高于前者 skip: 跳过指定的文档 limit: 读取指定的文档 # 获取test集合的前三条 db.test.find().limit(3).pretty() # 跳过test集合的前三条,从后面取两条 db.test.find().skip(3).limit(2).pretty() 还支持正则表达式 db.products.insert([ { "_id" : 100, "sku" : "abc123", "description" : "Single line description." }, { "_id" : 101, "sku" : "abc789", "description" : "First line\nSecond line" }, { "_id" : 102, "sku" : "xyz456", "description" : "Many spaces before line" }, { "_id" : 103, "sku" : "xyz789", "description" : "Multiple\nline description" } ]) # 查询以xyz开头的sku 第一种方法: db.products.find({"sku":/^xyz/}).pretty() 第二种方法: db.products.find({sku:{$regex:"^xyz"}}).pretty() 自定义查询方法 有点像js代码 # 年龄大于30的英雄 db.test.find({ $where:function() { return this.age>30;} }).pretty() 投影 注意: 第一个参数{}必传 在查询到的返回结果中,只选择必要的字段 命令:db.集合名称.find({},{字段名称:1,...}) 参数为字段与值,值为1表示显示,值为0不显特别注意:对于_id列默认是显示 若是不显示须要明确设置为0 # 只显示name和age字段,其他字段不显示 db.test.find({},{"_id":0,name:1,age:1}).pretty() 排序 命令:db.集合名称.find().sort({字段:1,...}) 参数1为升序排列 参数-1为降序排列 # 根据_id降序,年龄增序 # 先根据_id降序 db.test.find().sort({"_id":-1}).pretty() db.test.find().sort({"_id":-1,age:1}).pretty() 统计个数 命令:db.集合名称.find({条件}).count() 命令:db.集合名称.count({条件}) # 统计性别为ture的个数 db.test.find({gender:true}).count() # 统计hometown为大理的个数 db.test.find({hometown:"大理"}).count() 消除重复 注意: 返回的是一个列表 命令:db.集合名称.distinct('需去重字段',{条件}) # 根据hometown去重,age是大于18的 db.test.distinct("hometown",{age:{$gt:18}}) # 根据gender去重,age是大于等于18的 db.test.distinct("gender",{age:{$gte:18}}) mongodb的聚合 聚合(aggregate)是基于数据处理的聚合管道,每一个文档经过一个由多个阶段(stage) 组成的管道,能够对每一个阶段的管道进行分组、过滤等功能,而后通过一系列的处理, 输出相应的结果 语法:db.集合名称.aggregate({管道:{表达式}}) mongodb的经常使用管道和表达式 经常使用的管道以下 $group: 将集合中的文档分组,可便于统计结果 $match: 过滤数据,只输出符合条件的文档 $project: 修改文档的输入输出结构,如重命名,增长,删除字段,建立计算结果 $sort: 将输入的文档排序后输出 $limit: 限制聚合管道返回的文档数 $skip: 跳过指定数量的文档,并返回余下的文档 经常使用表达式 表达式: 处理输入文档并输出 语法: 表达式:'$列名' 经常使用表达式 $sum: 计算总和, $sum:1 表示以一倍计数 $avg: 计算平均值 $min: 获取最小值 $max: 获取最大值 $push: 在结果文档中插入值到整个数组中 管道命令之$group # 先对gender过滤,再对hometown进行分组,并计算年龄和 db.test.aggregate([ {$match:{gender:true}}, {$group:{_id:"$hometown",calc_age:{$sum:"$age"}}} ]) group by null 当咱们须要统计整个文档的时候,$group 的另外一种用途就是把整个文档分为一组进行统计 使用实例以下: db.test.aggregate( {$group: { _id:null, counter:{$sum:1} } } ) 其中注意点 _id:null 表示不指定分组的字段,即统计整个文档,此时获取的counter表示整个文档的个数 数据透视 正常状况在统计的不一样性别的数据的时候,须要知道全部的name,须要逐条观察, 若是经过某种方式把全部的name放到一块儿,那么此时就能够理解为数据透视 统计不一样的学生 注意: 查询出name:后面是一个列表 1)db.test.aggregate( {$group: { _id:null, name:{$push:"$name"} } } ) 使用$$ROOT能够将整个文档放入数组中 2)db.test.aggregate( {$group: { _id:null, name:{$push:"$$ROOT"} } } ) 练习: db.example.insert([ { "country" : "china", "province" : "sh", "userid" : "a" }, { "country" : "china", "province" : "sh", "userid" : "b" }, { "country" : "china", "province" : "sh", "userid" : "a" }, { "country" : "china", "province" : "sh", "userid" : "c" }, { "country" : "china", "province" : "bj", "userid" : "da" }, { "country" : "china", "province" : "bj", "userid" : "fa" } ]) # 须要统计出每一个country/province下的userid的数量(同一个userid只统计一次) db.example.aggregate( {$group:{"_id":{"country":"$country","province":"$province","userid":"$userid"}}}, {$group:{"_id":{"country":"$_id.country","province":"$_id.province"},count:{$sum:1}}}, {$project:{"_id":0,"country":"$_id.country","province":"$_id.province","count":"$count"}} ) 管道命令之$match # 查询年龄大于20的学生 db.test.aggregate( {$match:{age:{$gt:20}} ) # 查询年龄大于20的男女学生的人数 db.test.aggregate( {$match:{age:{$gt:20}} {$group:{_id:"$gender",counter:{$sum:1}}} ) 管道命令之$project # 查询学生的年龄、姓名,仅输出年龄姓名 db.test.aggregate( {$project:{_id:0,name:1,age:1}} ) # 查询男女生人生,输出人数 db.test.aggregate( {$group:{_id:"$gender",counter:{$sum:1}}} {$project:{_id:0,counter:1}} ) 管道命令之$sort # 查询学生信息,按照年龄升序 db.test.aggregate({$sort:{age:1}}) # 查询男女人数,按照人数降序 db.test.aggregate( {$group:{_id:"$gender",counts:{$sum:1}}}, {$sort:{counts:-1}} ) 管道命令之$skip和$limit # 查询2条学生信息 db.test.aggregate( {$limit:2} ) # 查询从第三条开始的学生信息 db.test.aggregate( {$skip:3} ) # 统计男女生人数,按照人数升序,返回第二条数据 db.test.aggregate( {$group:{_id:"$gender",counts:{$sum:1}}}, {$sort:{counts:1}}, {$skip:1}, {$limit:1} ) 索引 普通索引 mongodb建立简单的索引方法 db.test01.ensureIndex({属性:1}),1表示升序, -1表示降序 db.test01.createIndex({属性:1}) # 插入10万条测试数据 for(i=0;i<100000;i++){ db.test01.insert({name:'test'+i,age:i}) } 建立索引前: db.test01.find({name:'test10000'}) db.test01.find({name:'test10000'}).explain('executionStats') 在executionStats中查找 executionTimeMillis : xx 建立索引后: db.test01.ensureIndex({name:1}) db.test01.createIndex({name:1}) db.test01.find({name:'test10000'}).explain('executionStats') 在executionStats中查找 executionTimeMillis : xx 查看索引 默认状况下_id是集合的索引 查看当前集合中全部的索引 db.test01.getIndexes() 惟一索引 db.test01.ensureIndex({"age":1},{"unique":true}) 删除索引 db.test01.dropIndex({'name':1}) db.test01.dropIndex({'age':1}) 创建复合索引 创建复合索引的语法 db.test01.ensureIndex({name:1,age:1}) 建立索引的注意点 索引不是越多越好 根据须要选择是否须要创建惟一索引 索引字段是升序仍是降序在单个索引的状况下不影响查询效率,可是带复合索引的条件下会有影响 例如:在进行查询的时候若是字段1须要升序的方式排序输出,字段2须要降序的方式排序输出, 那么此时复合索引的创建须要把字段1设置为1,字段2设置为-1 mongodb的备份和恢复 备份 -h: 服务器地址, 也能够指定端⼝号 -d: 须要备份的数据库名称 -o: 备份的数据存放位置, 此目录中存放着备份出来的数据 mongodump -h dbhost -d dbname -o dbdirectory mongodump -h localhost:27017 -d test -o ~/Desktop/test12 恢复 -h: 服务器地址 -d: 须要恢复的数据库实例 --dir: 备份数据所在位置 mongorestore -h loacalhost:27017 -d test --dir ~/Desktop/test11 mongdb和python交互 pip install pymongo from pymongo import MongoClient client = MongoClient(127.0.0.1,27017) collection = client[db名][集合名] # 添加一条数据 ret = collection.insert_one({"name":"alex10010","age":33}) print(ret) # 添加多条数据 item_list = [{"name":"alex1000{}".format(i)} for i in range(10)] #insert_many接收一个列表,列表中为全部须要插入的字典 t = collection.insert_many(item_list) # 查找一条数据 #find_one查找而且返回一个结果,接收一个字典形式的条件 t = collection.find_one({"name":"alex10005"}) print(t) 查找所有数据 结果是一个Cursor游标对象,是一个可迭代对象,能够相似读文件的指针,可是只可以进行一次读取 #find返回全部知足条件的结果,若是条件为空,则返回数据库的全部 t = collection.find({"name":"alex10005"}) #结果是一个Cursor游标对象,是一个可迭代对象,能够相似读文件的指针 for i in t: print(i) for i in t: #此时t中没有内容 print(i) # 更新一条数据 注意使用$set命令 #update_one更新一条数据 collection.update_one({"name":"alex10005"},{"$set":{"name":"new_alex10005"}}) # 更行所有数据 # update_one更新所有数据 collection.update_many({"name":"alex10005"},{"$set":{"name":"new_alex10005"}}) # 删除一条数据 #delete_one删除一条数据 collection.delete_one({"name":"alex10010"}) # 删除所有数据 #delete_may删除全部知足条件的数据 collection.delete_many({"name":"alex10010"})