2、MongoDB基础应用

2、MongoDB基础应用

TOC

安装MongoDB

    此处就直接指定官方的mongodb仓库进行安装,默认安装会创建一个mongod用户,所以我们如果要修改配置的相关目录记得更改其属主属组;
# 添加YUM源
[root@node01 ~]# cat > /etc/yum.repos.d/mongodb.repo << EOF
[mongodb]
name=MongoDB Repository
baseurl=https://mirrors.tuna.tsinghua.edu.cn/mongodb/yum/el7/
gpgcheck=0
enabled=1
EOF
# 安装MongoDB
[root@node01 ~]# yum install -y mongodb-org
[root@node01 ~]# systemctl start mongod.service

使用

    MongoDB给我们提供了一个延迟创建的功能,在MongoDB内部我们无需创建库直接使用use dbname直接可以进入数据库,但是我们使用show dbs也看不到这个数据库,这是MongoDB的一个特点,如果我们在这个数据库下面创建了一条文档,那么这个数据库会立即被创建,这也就是延迟创建的表现方式;
基础语法
show dbs:查看已有的数据库名称;
show collections:collections相当于数据库的table,因为mongodb是文档数据库,所以它要拔各个文档组合起来,一个文档相当于MySQL中的一行数据,多个文档组合起来相当于MySQL的一张表,也就是mongodb的一个collections,该命令就是查看某一库中已有的collections名称;
show users:查看已有用户;
show profile:性能评估工具;
show logs:显示日志文件;
use <db_name>:进入指定的database;
db.foo.find:列出foo collections内的所有的文档;
db.foo.find( { a : 1 } ):在foo collections内查到a等于1的文档;
db.help:db级别的命令;
db.stats:查看数据库状态;
db.serverStatus:查看MongoDB服务程序的状态;
db.getCollectionNames:查看当前数据库所有的collection;

MongoDB基础概念

    由于MongoDB是一个NoSQL非关系型数据库,所以它的所支持数据操作接口,比MySQL来将要简单得多,在MongoDB中,所有的数据都存储为文档格式,它是JSON风格的数据存储的键值对儿,如{ "name" : "cce" , "age" : 18 }就是一个文档,但是在MongoDB中为了追踪每一个文档,而每一个文档都有一个隐藏的_id字段,这是MongoDB自己定义的,它相当于我们MySQL数据表的主键;
    还需要注意的是,在MongoDB中的文档有内嵌子文档,而且文档之间也是可以互相引用的,在MongoDB当中由于每一行是一个文档,那么如果有相似的文档也通常存储在一块,这些相似的文档都叫做一个clollections,因此一个clollections就是一组具有类似相关联的文档的集合,他们是可以构建一个索引的,它相当于数据接库中的表;

Document:一条一条的文档;
Collection:文档集合;
基础操作
# 测试创建一条数据
> use cce;
switched to db cce
> db.cce.insert({"name":"cce","age":18})
WriteResult({ "nInserted" : 1 })
> db.cce.find()
{ "_id" : ObjectId("5eba07f465f0e8aa072af38d"), "name" : "cce", "age" : 18 }
比较操作符
    比较运算符,主要是以数值类型的值来做对象,对数据进行筛选;
语法格式:{ filed: { $gt: 1 } }
$gt:大于;
$gte:大于等于;
$le:小于;
$lte:小于等于;
$ne:不等于;
集合查询
    表示在给定的值列表中是否包含in所给定的值;
语法格式:{ filed: { $in: [10,20] } }
$in: 包含;
$nin:不包含;
逻辑运算
    主要是做与或运算,来计算数据是否符合我们的筛选条件;
语法格式:{ $or : [ filed: { $in: [ 10,20 ] } ,...] }
$or:或运算;
$and:与运算;
$not:非运算;
$nor:反运算,返回不符合条件的所有文档;
元素查询
    根据文档中是否存在指定字段进行查询;
语法格式:{ filed: { $exists: true } }
$exists:是否存在;
$mod:将指定字段做取模运算,并返回其余数为指定值的文档;
$type:返回指定字段的值的类型为指定类型的文档,Double、String、Object、Array、Binary data、Undefined、Date、Timestamp、Null...;
数据操作
更新数据格式:db.mycoll.update(  { expression },{ $set : { filed : new_value } } )
删除字段格式:db.mycoll.update(  { expression },{ $unset: { filed1 , filed2 } } )
重名字段格式:db.mycoll.update(  { expression },{ $rename: { old_name1 : new_name1 ,  old_name2 : new_name2,... } } )
删除数据格式:db.mycoll.remove( { expression } )

MongoDB索引以及复制集

    MongoDB在NoSQL的众多流派当中属于文档类型的存储数据库,每一个文档都是一个JSON的Object,也就意味着说,它存储的不是数据本身,还有数据本身的描述信息,所以向这种我们就称之为Object,那么Object在流式化是比较麻烦的,MongoDB其实在很大程度上参考了MySQL的实现的,所以大多数能用MySQL的应用场景MongoDB都适应,除了要求强一致性的,必须要支持事物的场景;
    对于MongoDB来讲,我们在大数据或者海量存储数据中是用得非常多是,所以它特别适应于分布式场景中去存储数据的,但是不管怎么讲,我们假如有一个前提,数据量可能会非常的达,所以当数据量很大的时候,我们想快速查询应该使用索引才能保证它的查询速度比较快,依赖于索引对特定字段给它抽取出来并事先定义好一个数据结构,对于这样的数据结构,来实现特定数据查找,查找之后它有一个指针指向了元数据稳定存在的位置;
    要想做索引应该对特定字段来实现,也就是查找条件中的字段,比如要查找出年龄大于30岁的人群,所以在这种场景下,年龄字段才是我们做索引的做好选择,将年龄字段给它抽取出来,专门放置到一个文件,把这个文件的内容根据我们对应的次序做好排序存储为特定结构,方便查找;
    索引也是需要增删查改的,我们向原始数据中插入任何一个数据,那么我们的索引中也会多一项,索引也必须随着我们数据的改变而改变,同样的如果我们把原始数据的内容删除了一个数据项,那么很显然索引中也需要移除这一项,所以说,索引的这种方式的确加速了查询操作,但是一定程序上还给我们增加了额外的写性能开销,但不管怎么讲对于特别大的数据集,索引的存在的确是有着很大帮助的,更何况我们现在很多索引应用慢慢都可以实现延迟索引修改,也就意味着,我们如果要批量去修改数据时,它不会每次都在修改数据的同时还修改索引,当所有数据更新完成之后,过一段时间,它一批修改一次索引,这样可以使得让索引对写性能影响有效降低;
    索引是一种特殊结构,而且索引中的数据通常是排序后的数据,也可能是升序也可能是降序,比如我们对年龄做索引,那么这个里面排序的有可能是从0到最大,或者最大到0的排序方式,由此,对于0岁的用户来讲,可能在我们整个文件中的很多文档都有,所以它会有指针指向这些0岁的用户所在原始的collection中的位置,由此可以看出,我们根据索引查找查到的不是直接结果,我们还需要根据索引所指向的位置,在执行一个IO,找到我们期望要获取到的数据;
索引类型
    很显然对于MongoDB来讲,它也需要索引,那我们如何去构建索引,其实他们所有命令接口是比较简单的,只是我们需要知道索引的类型,索引类型有B+ Tree索引、hash索引、空间索引、全文索引
    hash索引:把对应字段的每一个值当做建,给它构建成hash格式,而后根据hash值查找原始文件中的数据位置,hash值通畅而言是唯一的,因此这些hash值被有效的分散值多个hash桶当中,而后去定位某一个索引项值时,它可以先找到hash桶,再找到hash桶上的hash值,因此无论你有多少个条目,它所需要消耗的时间是一致的,所以hash索引也就是所谓的键值对查找,与此前的键值所不同的地方在于,hash索引找到的并不是值,找到的hash值是指向原始数据的指针,因此还需要额外一个IO才能找到原始数据;
    空间索引:空间索引是指依据空间对象的位置和形状或空间对象之间的某种空间关系按一定的顺序排列的一种数据结构  ,其中包含空间对象的概要信息,如对象的标识、外接矩形及指向空间对象实体的指针;
MongoDB索引
    MongoDB的索引并不是从索引本身的结构所描述的,而是应用字段来实现的,在MongoDB也这样去称呼他们;
单键索引:构建在一个指定字段上的索引,叫做单字段索引;
组合索引:我们把索引构建在多个字段上,叫做组合索引;
多键索引:这里的多键索引其实指的是索引是创建,本身在一个键的值的内部又是一个子文档的索引,因为MongoDB的文档是可以包含文档的,  如果把一个键内部有其他子文档的索引,叫做多键索引;
空间索引:基于位置来实现查找的,我们称其为空间索引;
文本索引:文本索引支持去搜索整个文档中的文本串,所以我们可以将其理解为一个全文索引;
hash索引:MongoDB也一样支持hash索引, hash索引仅支持精确值查找,因为hash的键当中保存是hash值,每个字符串的hash值是不一样的;
MongoDB索引应用
    语法中 Key 值为你要创建的索引字段,1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1 即可;
创建索引:db.collection.ensureIndex()
    语法:db.collection.ensureIndex( { filed1 : 1 , filed : 1 } , { optoins } )
查看索引:db.collection.getIndexes()
删除索引:db.collection.dropIndexes(indexName)
重建索引:db.collection.reIndex()
查看集合索引大小:db.col.totalIndexSize()
Parameter Type Description
background Boolean 建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 “background” 可选参数。 “background” 默认值为false。
unique Boolean 建立的索引是否唯一。指定为true创建唯一索引。默认值为false
name String 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。
dropDups Boolean 3.0+版本已废弃。在建立唯一索引时是否删除重复记录,指定 true 创建唯一索引。默认值为 false.
sparse Boolean 对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 false.
expireAfterSeconds integer 指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。
v index version  索引的版本号。默认的索引版本取决于mongod创建索引时运行的版本。
weights document 索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重。
default_language String 对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语
language_override String 对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language.
# 测试写入十万条数据
> for (i=1;i<=100000;i++) db.students.insert({name:"student"+i,age:i%120,address:"#89 Wenhua Road,Zhengzhou,China"})
WriteResult({ "nInserted" : 1 })
> db.students.count()
100000
# 对name字段添加索引
> db.students.ensureIndex({name: 1})  # 默认为升序
# 删除索引
> db.students.dropIndex("name_1")
# 创建唯一索引
> db.students.ensureIndex({name: 1},{unique: true})
# 创建联合索引
> db.students.ensureIndex({name:1,age:1},{background: true})
# 查看是否使用索引
> db.students.find({name: {$gt: "student500"}}).explain()
MongoDB常用配置参数
fork:Bool值,表示MongoDB是否运行在后天;
bind_ip:指定MongoDB的监听地址,默认为0.0.0.0;
port:指定MongoDB的监听端口,默认为27017;
maxConns:指定MongoDB的最大并发连接数,默认为100000;
logpath:指明日志文件的位置;
syslog:指定MongoDB的日志交给syslog来记录;
auth:启用这个配置之后,我们的MongoDB只有在认证之后才能访问;
repair:启动MongoDB时先修复各Databases,尤其是在突然断电,进程意外关闭时,启MongoDB通常要使用repair选项保证,修复数据文件之后再进行启动;
journal:启用日志功能,journal日志是实现数据写入时,为了保证数据一致性,先将此次事物记录在日志里面,我们可以理解其为事物日志;
cpu:阶段性的显示CPU和IO等待使用信息;
sysinfo:显示系统级别的相关诊断信息;
slowms:指明执行多长时间的查询才算满查询,默认为100ms;
profile:性能剖析,它是让检测每一个操作大体上执行的时常,来获取当前数据库服务器程序性能问题所在的可能性,0关闭1仅剖析满查询, 2剖析所有查询;

MongoDB复制

    数据库服务器其实出现故障的场景有很多,比如网络连接故障、停电、硬件级别的停机故障等,都有可能会导致数据库服务器会离线的,所以为了防范此类故障有可能对我们带来损失,我们通常要给我们的数据库服务器提供一定的冗余能力,所以MongoDB和MySQL一样都面临同样的问题,在这种场景中,我们通常要通过类似于复制的这种方式,来为某一个数据库服务器离线时,提供数据的可用性能力;
    一般来讲,在众多可用性的保障的场景中,对于数据服务器来讲,复制是比较常见解决方案,因为它本身是数据类的服务,不像像web服务器,通常而言,我们一般有很少的数据需要进行修改类的操作,所以说,一个节点下线让另外一个节点上线就OK,因此像这种数据库类的服务器要想实现为另外一个节点进行冗余,那么通常而言就是通过复制来实现了,而MongoDB提供了两种类型的复制,第一种是主从复制,这种复制架构和MySQL的主从复制架构是非常近似的,它的工作原理、工作模式几乎都一样,叫Master/Slave,第二类是Replica Set,我们称之为复制集,或者说副本集;
    对于MongoDB来讲Master/Slave这种架构已经被废弃了,现在都是用的副本集这种复制结构,它有一个好处,因为它本身可以实现自动故障转移,而且易于实现恢复,以及更高级的一些环境的部署操作,所以Master/Slave这种应用模型,已经被废弃了;
MongoDB复制集(Replica Set)
    复制集,也称为,副本集,副本集其实是指的同一个数据集的一组MongoDB的示例,说白了MongoDB的复制,它和我们的MySQL的复制效果是一模一样的,无非就是有多个节点,每个节点都持有同样数据,但是在这些架构中无论是主从还是副本集,他一定有一个节点是主节点,支持读写操作,而其他节点是从节点,只能支持读操作,所以无论是副本集,还是主从架构,它的效果和我们的MySQL的概念都是一致的;
    副本集,其实就是指的服务于同一数据集的一组MongoDB实例,对于这么一个副本集当中其中有一个节点负责读写,其他节点只能读,一个复制集只能有一个主节点,可以有多个从节点,那么对于主节点来讲,和MySQL很像,主节点拿到数据之后将数据保存在操作日志中(oplog),然后各从节点通过此操作日志(oplog),来复制数据并应用于本地的,所以从这里可以看出它是MySQL是差不多的,但是由于MongoDB本身工作机制以及的内部的数据格式和MySQL不同,所以它的操作日志(oplog),和二进制日志还是有着相当大的差别的;
    在整个副本集当中,集群之间是不断通过心跳传递来判定集群健康状态的,心跳信息传递每隔2秒钟,进行一次,当多少次心跳检测失败时,通过选举方式能实现自动故障转移;
主从节点
    MongoDB有诸多方案能保证主从节点尽可能时刻保持一致的,因为它有所谓的心跳机制,所以在这个工作架构中,读写主节点,而从只能负责读操作了,那么从节点复制的时候一般来讲,主节点不可用时,那么两个从节点,他们会通过心跳信息不断的进行检测着主节点,每个2秒钟发一次心跳,在一定时间内如果收不到主节点的心跳,那么这两个从节点,会重新触发一次选举操作,并选举其中一个节点成为新的主节点,那么如何选,就得看优先级了,谁的优先级高,谁就是新的主节点,比如通过网络带宽等来衡量,那么主节点选举完成之后,另外一个从节点,会自动将这个新主节点设定为自己的主节点,无需任何手动参数,这比MySQL要方便多了;
仲裁节点(Arbiter)
    因此一般来说,除了主从这样提供数据之外,一个复制集应该提供第三个节点,仲裁节点,所以这和我们的主从架构不一样,主从结构一主一从就OK,但副本集一般来讲,为了防止网络分区,那么我们最少得有三个节点,也就是我们的仲裁节点,当我们联系不到主节点之后,如果能联系到仲裁节点,说明,我们可以判定主节点确实挂了,而后我们的仲裁节点也将参与选举过程,所以我们称之为仲裁节点;
    即便是我们将来真的用不上仲裁节点,我们也可以随便找一个服务器,只需要在上面装上MongoDB即可,只需要参与仲裁就可以,它不用去真正保存副本,值用于参与选举,那么由此,我们的主从节点, 出现故障时,他们都会试图去联系仲裁节点,谁能联系到仲裁节点,那么谁就是胜出的一方,出任新的主节点,那么在MongoDB我们一般称之为Arbiter;
复制集节点分类
    如果我们只需要用到偶数个节点,那么我们就需要用到一个第三方节点当作仲裁者,其实MongoDB的复制集当中,有很多种特殊类型的复制需要,第一种是读偏好,也就是读的倾向性,一个从节点,既可以从主机复制数据,也可以从其他从节点复制数据,倾向性意思就是更倾向去那一个节点中去操作数据,因此必要时,从节点也可以通过选举跃升为主节点,不过我们也可以设定,我们也可以设定其他节点用来工作与特殊目的的,有如下几个;
0优先级节点:优先级为0,它本身持有副本集,但是不会被选举成为主节点,因此这种节点一般也称之为冷备节点,他们虽然不会被选举成为主节点,但是他们却可以参与选举过程,更多场景这种节点都是应用于异地容灾的,所以它可以参与选举,可以持有数据,但是它本身不会被选举成为主节点,不过它是可以被客户端来访问的;
隐藏的从节点:和0优先级节点差不多,只不过隐藏的从阶段,是不可以被访问的,一般来讲,主要用于同一副本中的其他节点中的不同流的场景中,所以说隐藏的从节点,它必须事先是一个0优先级节点,也就意味着它一定不会被选举成为主节点的,其他它也不会显示在对应的MongoDB的状态显示场景中,但隐藏的从节点一样拥有选举权,对客户端来讲是不可见的,客户端无法访问,但一样可以参与选举;
延迟复制的从节点:延迟复制的从节点有一个比较好的地方,比如我们有意的让一个节点比主节点慢一个小时,所以即使我们删除了主库,我们是可以在1个小时之内在延迟节点中找回的,因此延迟复制从节点是无法从未主节点的,所以它也是一个0优先级的节点,而它额外多一个特性,就是复制时间落后于主节点一个固定时长,尽管如此,延迟复制的从节点也拥有选票权;
仲裁节点:arbiter,它可以连数据都没有,只是一个仲裁节点;
MongoDB的复制架构
    对于MongoDB的副本集复制来讲,它依赖于两个基础工具,一个是操作日志(oplog),一个是心跳检测(heartbeat),heartbeat主要是为了心跳信息传递,并触发选举的,而oplog是实现复制过程的一个基础性工具,就像我们的MySQL一样,如果是主从复制的话,主节点将一直是主节点,从节点将始终是从节点,如果我们期望从节点能够提升为主节点,就意味着这个从节点,必须要打开二进制日志,否则它如何成为主节点呢,因此对于MongoDB来讲也是一样,MongoDB中的从节点,随时都有可能被提升为主节点,所以在MongoDB的副本集当中,每一个节点都会持有操作日志(oplog),所以oplog对于每个节点来讲,无论是主还是从,默认都有,而且它的大小是固定的;
    oplog是一个大小固定的文件,同常这个文件存储在local数据库中,可以发现我们的MongoDB安装完成之后默认就有一个数据库叫做local,这个local其实就是用来存储操作日志(oplog)的,而对于local库来说,默认里面就有一些数据,但你只有启用副本集功能,它才会真正产生和副本集相关的其他文件,操作日志(oplog)主要用于记录,本地数据库中的每个数据的修改操作的,尽管说每一个节点都有拥有操作日志(oplog),但是仅有主节点会写操作日志(oplog),并同步给其他节点的,因此当一个新的从阶段被提升为新的主节点之后,它就有权限写本地的操作日志(oplog);
    操作日志(oplog)很独特,从阶段即使有,也不会写,只有提升为主节点的时候才会写,它和MySQL的二进制日志不一样会随着时间的曾长变大,而MongoDB的大小是固定的, 所以你管用或者不用,都在这里,不增不减,由此我们刚启动数据库时,它就会初始化一个空间出来,这就是为什么我们第一次启动的时候会比较慢,原因就是这个,而大小一般来讲,是对应的操作日志(oplog)所在的文件系统大小的百分之五,如果磁盘是10G,那么默认就是500M,但是如果这个百分之五的结果小于1G,而操作日志(oplog)通常都会被指定为1G,所以它最小是1G;
    MongoDB的操作日志(oplog)和MySQL的二进制日志不一样,操作日志(oplog)是幂等性的oplog,也就意味着同一个oplog在同一个MongoDB上运行多次都没事,运行N次的结果会导致你的MongoDB都是同一个内容,结果是一抹一眼的,所以从这点来看它比MySQL的二进制日志更好用,那么oplog既然大小固定了,那么它能存储的数据有小时长的数据也是固定的,因此数据量比较大它没办法使用oplog来完全第一条数据开始复制的,所以你新加一个从节点过来,它不会从oplog开始复制数据,而是自动能够通过主节点的数据集当中复制数据,这个节点我们称之为初始化节点,自动完成初始化,而后才会从oplog中复制数据,所以oplog的每一个操作都具有幂等行;
    所以,一个新的从阶段加入进来之后大体上有三阶阶段,第一初始同步(initial sync),第二个是回滚后追赶(post-rollback catch-up),第三个是切分快迁移(sharding chunk migrations),对于MongoDB来讲操作日志(oplog)是存放在local库当中的,而local数据库存放了所有副本集的元数据和操作日志(oplog),不仅oplog还有元数据,不过local库自己是不会参与我们的复制过程的,类似我们的MySQL的mysql库,所以除了local其他的节点都会参与复制,此外local存储oplog是通过一个collection来实现的,这个collection叫做oplog.rs的collection;
    但是oplog.rs这个文件默认情况下是不会被创建的,只有当这个节点成为某一个副本集中的从节点,或者说加入副本集以后第一次启动时,会自动创建,而且它大小是一个默认值的,默认值大小本身依赖于操作系统,当然我们也可以通过oplogSize来指定它的大小;

MongoDB的数据同步类型

    和MySQL一样,我们加入一个MySQL从节点进来的时候,可能我们的MySQL已经跑了一段时间了, 那么这个时候我们进行复制时,可能它已经在主服务器有很多很多的数据了,复制需要很长时间才能赶上去的,或者说我们所有的节点都是从头构建的,这也是一种场景;
    对于MongoDB也一样,它的数据同步类型就有了两种,第一是初始同步,第二是复制,初始同步有两种方式,副本集成员在两种情况下会启动初始同步,第一MongoDB会查询每个数据库的每个collection,而后将数据插入到本地相应的collection中,这种我们称其为克隆数据库,第二,通过读取oplog操作日志,MongoDB更新本地数据集,应用数据集的所有数据改变,而达到数据同步状态,第三,为所有的collection构建相应的索引;
    为了保持数据的一致性,MongoDB要求仅主节点可以进行写操作,并且需要通来journal保证其数据的持久性,因此在单实例上,这种场景中必须把journal打开,否则你的持久性将无从得到保证,因此要想保证MongoDB的一致性和持久性,那就必须启用journal功能;
    MongoDB也是支持多线程复制的,这也是它优于MySQL的地方,因为它原生支持多线程复制,另外MongoDB是可以提供数据预取的,这也是它在设计上优于MySQL的地方;
初始化同步
    所以,副本集在两种情形下会启动初始化同步,第一节点没有任何数据,第二从节点丢失了副本集复制历史的时候会启动初始化同步,复制历史是指的上次复制的时间节点,因为每次复制都会记录一个已复制的时间节点信息,所以它不得不重新启动复制过程,初始化同步过程有三步;
克隆所以数据库:查询主节点的每个数据库的每个collection,并且将其应用在本地MongoDB中;
应用oplog:数据复制完成之后,也就是说复制oplog并应用在本地MongoDB;
构建索引:为所有的collection构建索引,初始同步就完成了;

发表评论