mongoDB介绍
MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。
mongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
mongoDB和关系型数据库的对比
1.关系数据的表的record必须保证拥有每一个field
2.mongoDB的每一个document的可以不一样
3.关系型数据查询使用SQL
4.mondoDB查询使用内置find函数 -> 基于BSON的特殊查询工具
mongoDB安装(CentOS7.x系列)
下载安装包
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-3.4.4.tgz
解压:tar -xvf mongodb-linux-x86_64-3.4.4.tgz
改名称:mv mongodb-linux-x86_64-3.4.4.tgz mongodb
cd mongodb
创建一个存放数据文件的目录:mkdir db
创建一个存放日志的目录:mkdir logs
cd bin
创建配置文件:vi mongodb.conf
在配置文件中写入:
dbpath=/usr/local/mongodb/db
logpath=/usr/local/mongodb/logs/mongodb.log
port=27017
fork=true
nohttpinterface=true
参数解释:
--dbpath 数据库路径(数据文件)
--logpath 日志文件路径
--master 指定为主机器
--slave 指定为从机器
--source 指定主机器的IP地址
--pologSize 指定日志文件大小不超过64M.因为resync是非常操作量大且耗时,最好通过设置一个足够大的oplogSize来避免resync(默认的 oplog大小是空闲磁盘大小的5%)。
--logappend 日志文件末尾添加,即使用追加的方式写日志
--journal 启用日志
--port 启用端口号
--fork 在后台运行
--only 指定只复制哪一个数据库
--slavedelay 指从复制检测的时间间隔
--auth 是否需要验证权限登录(用户名和密码)
--syncdelay 数据写入硬盘的时间(秒),0是不等待,直接写入
--notablescan 不允许表扫描
--maxConns 最大的并发连接数,默认2000
--pidfilepath 指定进程文件,不指定则不产生进程文件
--bind_ip 绑定IP,绑定后只能绑定的IP访问服务
以自定义的 mongodb 配置文件方式启动:
./mongod –config mongodb.conf
查看 mongodb 进程:
ps aux |grep mongodb
查看 mongodb 服务的运行日志:tail -200f /usr/local/mongodb/logs/mongodb.log
检查端口是否已被启动:
netstat -lanp | grep 27017
开机自动启动mongodb:
vi /etc/rc.d/rc.local
/usr/local/mongodb/bin/mongod –config /usr/local/mongodb/bin/mongodb.conf
重启一下系统测试下能不能自启
进入mongodb的shell模式
/usr/local/mongodb/bin/mongo
查看数据库列表
show dbs
当前db版本
db.version();
admin数据库命令关闭数据:
db.shutdownServer()
bin目录结构
mongoDB基本概念
文档
文档是mongoDB的核心概念,文档就是键值对的一个有序集。MongoDB不但区分类型,而且区分大小写。MongoDB的文档不能有重复的键。
集合
集合就是一组文档。如果将MongoDB中的一个文档比喻为关系型数据库中的一行。那么一个集合就相当于一张表。
动态模式
集合是动态模式的。一个集合中的文档可以是各式各样的。
命名
集合使用名称进行标识。集合名可以是满足下列条件的任意UTF-8字符串:
- 集合名不能是空字符串
- 集合名不能包含\0字符(空字符串),这个字符表示集合名的结束。
- 集合名不能以”system.”开头,这是为系统集合准备的前缀。
- 用户创建的集合额不能在集合名中包含字符’
′。因为某些系统生成的集合中包含
。
子集合
组织集合的一种惯例是使用“.”分隔不同命名空间的子集合。
数据库
在MongoDB中,多个文档组成集合,而多个集合可以组成数据库。一个MongoDB实例可以承载多个数据库,每个数据库拥有0个或者多个集合。每个数据库都有独立的权限。
数据库命名可以是满足以下条件的任意UTF-8字符串:
- 不能是空字符串
- 不得含有/、\、.、”、*、<、>、:、|、?、$(一个空格)、\0(空字符)。
- 数据库名应全部小写。
- 数据库名最多为64字节。
- 不能使用admin、local、config,这些数据库名是保留的。
BSON数据类型
BSON是JSON的扩展,新增了如日期、浮点等JSON不支持的数据类型。
mongoDB基本操作
1、创建一个数据库
use [databaseName]
如果什么都不做就离开的话,这个空数据库就会被删除。
2、查看所有数据库
show dbs
3、给指定数据库添加集合并且添加记录
db.[documentName].insert({…})
4、查看数据库中所有文档(集合)
show collections
5、查询指定文档的数据
查询所有db.[documentName].find()
查询第一条数据 db.[documentName].findOne()
6、更新文档数据
db.[documentName].update({查询条件},{更新条件})
修改器:$set
示例:
var p = db.persons.findOne();
db.persons.update(p,{name:”jack”})
7、删除文档中的数据
db.[documentName].remove({…})
示例:
db.persons.remove({name:”jack”})
8、删除数据库中的集合
db.[documentName].drop()
9、删除数据库
db.dropDatabase()
10、Shell的help
全局的help
数据库相关的db.help()
集合相关的db.[documentName].help()
文档(Document)
Document数据插入
1.插入文档
db.[documentName].insert({})
2.批量插入文档
shell 这样执行是错误的 db.[documentName].insert([{},{},{},……..])
shell 不支持批量插入
想完成批量插入可以用mongo的应用驱动或是shell的for循环
3.Save操作
save操作和insert操作区别在于当遇到_id相同的情况下
save完成保存操作
insert则会报错
Document数据删除
1.删除列表中所有数据
db.[documentName].remove()
集合的本身和索引不会别删除
2.根据条件删除
db.[documentName].remove({})
删除集合text中name等于uspcat的纪录
db.text.remove({name:”uspcat”})
3.注意事项
如果你想清楚一个数据量十分庞大的集合,直接删除该集合并且重新建立索引的办法比直接用remove的效率和高很多
Document数据更新
- 强硬的文档替换式更新操作
db.[documentName].update({查询器},{修改器})
强硬的更新会用新的文档代替老的文档 - 主键冲突的时候会报错并且停止更新操作
因为是强硬替换当替换的文档和已有文档ID冲突的时候
则系统会报错 - insertOrUpdate操作
目的:查询器查出来数据就执行更新操作,查不出来就替换操作
做法:db.[documentName].update({查询器},{修改器},true) - 批量更新操作
默认情况当查询器查询出多条数据的时候默认就修改第一条数据
如何实现批量修改
db.[documentName].update({查询器},{修改器},false, true) 使用修改器来完成局部更新操作
修改器是放到最外面,查询器是放到内层的$addToSet
与$each
结合完成批量数组更新
db.text.update({_id:1000},{$addToSet
:{books:{$
each:[“JS”,”DB”]}}})
$
each会循环后面的数组把每一个数值进行$addToSet操作存在分配与查询效率
当document被创建的时候DB为其分配没存和预留内存当修改操作不超过预留内层的时候则速度非常快反而超过了就要分配新的内存则会消耗时间runCommand函数和findAndModify函数
runCommand可以执行mongoDB中的特殊函数
findAndModify就是特殊函数之一他的用于是返回update或remove后的文档runCommand({“findAndModify”:”processes”, query:{查询器}, sort{排序}, new:true update:{更新器}, remove:true }).value ps = db.runCommand({ "findAndModify":"persons", "query":{"name":"text"}, "update":{"$set":{"email":"1221"}}, "new":true }).value do_something(ps)
查询
Find
1.指定返回的键
db.[documentName].find ({条件},{键指定})
查询出所有数据的指定键(name ,age ,country)
db.persons.find({},{name:1,age:1,country:1,_id:0})
2.查询条件
$lt <
$lte <=
$gt >
$gte >=
$ne !=
案例:
2.查询条件
2.1查询出年龄在25到27岁之间的学生
db.persons.find({age: {$gte:25,$lte:27},{_id:0,age:1})
2.2查询出所有不是韩国籍的学生的数学成绩
db.persons.find({country:{$ne:” Korea”}},{_id:0,m:1})
3.包含或不包含
$in或$nin
2.3查询国籍是中国或美国的学生信息
db.persons.find({country:{$in:[“USA”,“China”]}})
2.4查询国籍不是中国或美国的学生信息
db.persons.find({country:{$nin:[“USA”,“China”]}})
4.OR查询
$or
2.4查询语文成绩大于85或者英语大于90的学生信息
db.persons.find({$or:[{c:{$gte:85}},{e:{$gte:90}}]},{_id:0,c:1,e:1})
5.Null
把中国国籍的学生上增加新的键sex
db.person.update({country:”China”},{$set:{sex:”m”}})
2.5查询出sex 等于 null的学生
db.persons.find({sex:{$in:[null]}},{country:1})
6.正则查询
2.6查询出名字中存在”li”的学生的信息
db.persons.find({name:/li/i},{_id:0,name:1})
7.$not的使用
$not可以用到任何地方进行取反操作
2.7查询出名字中不存在”li”的学生的信息
db.persons.find({name:{$not:/li/i}},{_id:0,name:1})
$not和$nin的区别是$not可以用在任何地方儿$nin是用到集合上的
8.数组查询$all和index应用
2.8查询喜欢看MONGOD和JS的学生
db.persons.find({books:{$all:[“MONGOBD”,”JS”]}},{books:1,_id:0})
2.9查询第二本书是JAVA的学习信息
db.persons.find({“books.1”:”JAVA”})
9.查询指定长度数组$size它不能与比较查询符一起使用(这是弊端)
2.8查询出喜欢的书籍数量是4本的学生
db.persons.find({books:{$size:4}},{_id:0,books:1})
2.9查询出喜欢的书籍数量大于3本的学生
1.增加字段size
db.persons.update({},{$set:{size:4}},false, true)
2.改变书籍的更新方式,每次增加书籍的时候size增加1
db.persons.update({查询器},{$push:{books:”ORACLE”},$inc:{size:1}})
3.利用$gt查询
db.persons.find({size:{$gt:3}})
2.10利用shell查询出Jim喜欢看的书的数量
var persons = db.persons.find({name:"jim"})
while(persons.hasNext()){
obj = persons.next();
print(obj.books.length)
}
10.$slice操作符返回文档中指定数组的内部值
2.11查询出Jim书架中第2~4本书
db.persons.find({name:"jim"},{books:{"$slice":[1,3]}})
2.12查询出最后一本书
db.persons.find({name:"jim"},{books:{"$slice":-1},_id:0,name:1})
11.文档查询
为jim添加学习简历文档 jim.json
2.13查询出在K上过学的学生
1. 这个我们用绝对匹配可以完成,但是有些问题(找找问题?顺序?总要带着score?)
db.persons.find({school:{school:"K",score:"A"}},{_id:0,school:1})
2.为了解决顺序的问题我可以用对象”.”的方式定位
db.persons.find({"school.score":"A","school.school":"K"},{_id:0,school:1})
3.这样也问题看例子:
db.persons.find({"school.score":"A","school.school":”J”},{_id:0,school:1})
同样能查出刚才那条数据,原因是score和school会去其他对象对比
4.正确做法单条条件组查询$elemMatch
db.persons.find({school:{$elemMatch:{school:"K",score:"A"}}})
12.$where
复杂的查询我们就可以用$where因为他是万能,但是我们要尽量避免少使用它因为他会有性能的代价
分组
- Count
请查询persons中美国学生的人数.
db.persons.find({country:”USA”}).count() - Distinct
请查询出persons中一共有多少个国家分别是什么.
db.runCommand({distinct:”persons“ , key:”country”}).values Group
语法:db.runCommand({group:{ ns:集合名字, Key:分组的键对象, Initial:初始化累加器, $reduce:组分解器, Condition:条件, Finalize:组完成器 }})
分组首先会按照key进行分组,每组的 每一个文档全要执行$reduce的方法,
他接收2个参数一个是组内本条记录,一个是累加器数据。
3.1请查出persons中每个国家学生数学成绩最好的学生信息(必须在90以上)db.runCommand({group:{ ns:"persons", key:{"country":true}, initial:{m:0}, $reduce:function(doc,prev){ if(doc.m > prev.m){ prev.m = doc.m; prev.name = doc.name; prev.country = doc.country; } }, condition:{m:{$gt:90}} }})
3.2在3.1要求基础之上吧没个人的信息链接起来写一个描述赋值到m上
finalize:function(prev){ prev.m = prev.name+" Math scores "+prev.m }
- 用函数格式化分组的键
如果集合中出现键Counrty和counTry同时存在那分组有点麻烦这要如何解决呢?
$keyf:function(doc){
return {country:doc.counTry}
},…
分页
- Limit返回指定的数据条数
1.1 查询出persons文档中前5条数据
db.persons.find({},{_id:0,name:1}).limit(5) - Skip返回指定数据的跨度
2.1 查询出persons文档中5~10条的数据
db.persons.find({},{_id:0,name:1}).limit(5).skip(5) Sort返回按照年龄排序的数据[1,-1]
db.persons.find({},{_id:0,name:1,age:1}).sort({age:1})
注意:mongodb的key可以存不同类型的数据排序就也有优先级Limit和Skip完成分页
4.1三条数据位一页进行分页
第一页->db.persons.find({},{_id:0,name:1}).limit(3).skip(0)
第二页->db.persons.find({},{_id:0,name:1}).limit(3).skip(3)
游标
1、游标
利用游标遍历查询数据,游标到了底部就会释放资源不能再读取了.
var persons = db.persons.find();
while(persons.hasNext()){
obj = persons.next();
print(obj.name)
}
2、 游标几个销毁条件
1.客户端发来信息叫他销毁
2.游标迭代完毕
3.默认游标超过10分钟没用也会别清除
3、查询快照
快照后就会针对不变的集合进行游标运动了,看看使用方法.
db.persons.find({
query:name:”Jim”,
snapshot:true})
高级查询选项
query
orderby
maxsan:integer最多扫描的文档数
min:doc 查询开始
max:doc查询结束
hint:doc 使用哪个索引
explain:boolean统计
snapshot:boolean 一致快照
索引
索引介绍
创建简单索引
//为number 创建索引
db.books.ensureIndex({number:1})索引使用需要注意事项:
1.创建索引的时候注意: 1是正序创建索引,-1是倒序创建索引 2.索引的创建在提高查询性能的同事会影响插入的性能,对于经常查询少插入的文档可以考虑用索引 3.符合索引要注意索引的先后顺序 4.每个键全建立索引不一定就能提高性能 5.在做排序工作的时候如果是超大数据量也可以考虑加上索引 用来提高排序的性能
索引的名称
1.使用可视化工具查询 2.创建索引同时指定索引的名字 db.books.ensureIndex({name:-1},{name:”bookname”})
唯一索引
建立唯一索引
db.books.ensureIndex({name:-1},{unique:true})去除重复值
如果建议唯一索引之前已经有重复数值如何处理 db.books.ensureIndex({name:-1},{unique:true,dropDups:true})
- Hint
如何强制查询使用指定的索引
db.books.find({name:”1book”,number:1}).hint({name:-1})
指定索引必须是已经创建了的索引 Expain
如何详细查看本次查询使用那个索引和查询数据的状态信息 db.books.find({name:"1book"}).explain() “cursor” : “BtreeCursor name_-1“ 使用索引 “nscanned” : 1 查到几个文档 “millis” : 0 查询时间0是很不错的性能
索引管理
1、system.indexes
在shell查看数据库已经建立的索引
db.system.indexes.find()
db.system.namespaces.find()
2、后台执行
执行创建索引的过程会暂时锁表问题如何解决?
为了不影响查询我们可以叫索引的创建过程在后台
db.books.ensureIndex({name:-1},{background:true})
3、删除索引
批量和精确删除索引
db.runCommand({dropIndexes : ”books” , index:”name_-1”})
db.runCommand({dropIndexes : ”books” , index:”*”})
空间索引
mongoDB提供强大的空间索引可以查询出一定范围的地理坐标.
地理空间查询的类型:交集(intersection)、包含(within)、接近(nearness)。
案例:
1.查询出距离点(70,180)最近的3个点
添加2D索引
db.map.ensureIndex({"gis":"2d"},{min:-1,max:201})
默认会建立一个[-180,180]之间的2D索引
查询点(70,180)最近的3个点
db.map.find({"gis":{$near:[70,180]}},{gis:1,_id:0}).limit(3)
2.查询以点(50,50)和点(190,190)为对角线的正方形中的所有的点
db.map.find({gis:{"$within":{$box:[[50,50],[190,190]]}}},{_id:0,gis:1})
3.查询出以圆心为(56,80)半径为50规则下的圆心面积中的点
db.map.find({gis:{$within:{$center:[[56,80],50]}}},{_id:0,gis:1})
集合
固定集合
- 固定集合需要事先创建好,而且它的大小是固定的。当固定集合被占满时,如果再插入新文档,固定集合会自动将最老的文档从集合中删除。
- 固定特性
2.1固定集合默认是没有索引的就算是_id也是没有索引的
2.2由于不需分配新的空间他的插入速度是非常快的
2.3固定集合的顺序是确定的导致查询速度是非常快的
2.4最适合的是应用就是日志管理 - 创建固定集合
3.1创建一个新的固定集合要求大小是100个字节,可以存储文档10个
db.createCollection(“mycoll”,{size:100,capped:true,max:10})
3.2把一个普通集合转换成固定集合
db.runCommand({convertToCapped:”persons”,size:100000}) - 反向排序,默认是插入顺序排序.
4.1查询固定集合mycoll并且反响排序
db.mycoll.find().sort({$natural:-1}) - 尾部游标,可惜shell不支持java和php等驱动是支持的
5.1尾部游标概念
这是个特殊的只能用到固定级和身上的游标,他在没有结果的时候,也不会自动销毁他是一直等待结果的到来。
GridFS存储文件
- GridFS是mongoDB自带的文件系统他用二进制的形式存储文件,大型文件系统的绝大多是特性GridFS全可以完成。
- 使用工具:mongofiles
- 使用GridFS
1.查看GridFS的所有功能
mongofiles
2.上传一个文件
mongofiles -d foobar -l “E:\a.txt” put “a.txt“
3.查看GridFS的文件存储状态
db.fs.chunks.find() 和db.fs.files.find() 存储了文件系统的所有文件信息
4.查看文件内容
mongofiles -d foobar get “a.txt“
MongoDBVUE可以查看,shell无法打开文件
5.查看所有文件
mongofiles -d foobar list
6.删除已经存在的文件VUE中操作
mongofiles -d foobar delete ‘a.txt’
管理
导入导出
1.导出数据(中断其他操作)
利用mongoexport
-d 指明使用的库
-c 指明要导出的表
-o 指明要导出的文件名
-csv 制定导出的csv格式
-q 过滤导出
–type
Fsync锁
- 上锁可以让缓存池的数据全部进到数据库,这在数据库备份的时候很有意义.
- 上锁和解锁
上锁
db.runCommand({fsync:1,lock:1});
解锁
db.currentOp() - 数据修复
当停电等不可逆转灾难来临的时候,由于mongodb的存储结构导致会产生垃圾数据,在数据恢复以后这垃圾数据依然存在,这是数据库提供一个自我修复的能力.使用起来很简单
db.repairDatabase()
用户管理,安全认证
1.添加一个用户
为admin添加uspcat用户和foobar数据库的shuai用户
use admin
db.addUser(“uspcat”,”123”);
use foobar
db.addUser(“shuai”,”123”);
2.启用用户
db.auth(“名称”,”密码”)
3.安全检查 –auth
非foobar是不能操作数据库的
启用自己的用户才能访问
非admin数据库的用户不能使用数据库命令
admin数据库中的数据经过认证为管理员用户
4.用户删除操作
db.system.users.remove({user:”shuai”});
副本集
主从复制
1、主从复制是一个简单的数据库同步备份的集群技术。
- 在数据库集群中要明确的知道谁是主服务器,主服务器只有一台.
- 从服务器要知道自己的数据源也就是对于的主服务是谁.
- –master用来确定主服务器,–slave 和 –source 来控制从服务器
2、主从复制的其他设置项
–only 从节点指定复制某个数据库,默认是复制全部数据库
–slavedelay 从节点设置主数据库同步数据的延迟(单位是秒)
–fastsync 从节点以主数据库的节点快照为节点启动从数据库
–autoresync 从节点如果不同步则从新同步数据库
–oplogSize 主节点设置oplog的大小(主节点操作记录存储到local的oplog中)
副本集
1、副本集是一组服务器,其中有一个主服务器(primary),用于处理客户端请求;还有多个备份服务器(secondary),用于保存主服务器的数据副本。如果主服务器奔溃了,备份服务器会自动将其中一个成员升级为新的主服务器。
2、初始化副本集
use admin
db.runCommand({"replSetInitiate":
{
"_id":'child',
"members":[{
"_id":1,
"host":"127.0.0.1:1111"
},{
"_id":2,
"host":"127.0.0.1:2222"
},{
"_id":3,
"host":"127.0.0.1:3333"
}]
}
})
3、查看副本集状态
rs.status()
4、节点和初始化高级参数
standard 常规节点:参与投票有可能成为活跃节点
passive 副本节点:参与投票,但是不能成为活跃节点
arbiter 仲裁节点:只是参与投票不复制节点也不能成为活跃节点
5、高级参数
Priority 0到1000之间 ,0代表是副本节点 ,1到1000是常规节点
arbiterOnly : true 仲裁节点
用法:
members":[{
"_id":1,
"host":"127.0.0.1:1111“,
arbiterOnly : true
}]”
6、优先级相同时候仲裁组建的规则
7、读写分离操作
slaveOkay : true
shell中无法实验,这个特性是被写到mongoDB的驱动程序中的,在java和node等其他语言中可以完成。
8、Oplog
改变oplog大小
主库 --master --oplogSize size
分片
分片(sharding)是指将数据拆分,将其分散存放在不同的机器上的过程。MongoDB支持自动分片,可以使数据库架构对应用程序不可见,也可以简化系统管理。
1、利用key为片键进行自动分片
2、何时分片?
- 机器的磁盘空间不足
- 单个的mongoDB服务器已经不能满足大量的插入操作
- 想通过把大数据放到内存中来提高性能
3、分片步骤
1、创建一个配置服务器
2、创建路由服务器,并且连接配置服务器
路由器是调用mongos命令
3、添加2个分片数据库
8081和8082
4、利用路由为集群添加分片(允许本地访问)
db.runCommand({addshard:"127.0.0.1:8081",allowLocal:true})
db.runCommand({addshard:"127.0.0.1:8081",allowLocal:true})
注意事项:切记之前不能使用任何数据库语句
5、打开数据分片功能,为数据库foobar打开分片功能
db.runCommand({"enablesharding":"foobar"})
6、对集合进行分片
db.runCommand({"shardcollection":"foobar.bar","key":{"_id":1}})
7、利用大数据量进行测试
4、查看配置库对于分片服务器的配置存储
db.printShardingStatus()
5、查看集群对bar的自动分片机制配置信息
db.shards.find()
{ "_id" : "shard0000", "host" : "127.0.0.1:8081" }
{ "_id" : "shard0001", "host" : "127.0.0.1:8082" }