MongoDB副本集
副本集其实就是拥有高可用的主从同步
优点:
1. 提高数据可用性、安全性、方便数据故障恢复
2. 支持故障自动切换、自动修复成员节点
3. 拥有高可用的特性
工作过程:
主库记录所有操作在日志oplog中,从节点定期轮询主节点获取这些操作,然后对自己的数据库复本执行这些操作,从而保证从节点的数据和主节点一致
配置副本集:
1. 修改配置文件/工作目录/etc/mongodb.conf,添加一行数据
replSet=集群名(几个服务器要配置一样)
2. 进入预备成为主库的主机连接mongod服务器中,修改一个变量,这变量名可以自己定义,我定义为config,如果写错了,可以重新再定义一下,然后再继续下面的操作
config = {
_id:'集群名',
members:[
{_id:0,host:'服务器IP1:端口',prioricy 优先级1},
{_id:1,host:'服务器IP2:端口',prioricy 优先级2},
{_id:2,host:'服务器IP3:端口',prioricy 优先级3}
] }
# 优先级越大,升级成为主库
3. 启动副本集服务
rs.initiate(config)
4. 查看状态信息
# 主要查看集群中各个服务器的健康性,health值为0就是服务器有问题,1就是没问题
rs.status()
"members" : [
{
"_id" : 0,
"name" : "172.10.100.110:27017",
"health" : 1,
# 查看集群主库信息,ismaster值为true就是主库,为false就算从库
rs.isMaster()
"setName" : "rs1",
"setVersion" : 1,
"ismaster" : false,
"secondary" : true,
"primary" : "172.10.100.110:27017",
"me" : "1172.10.100.111:27017",
*. 设置副本可以查看数据(一般不用)
db.getMongo().setSlaveOk()
*. 撤除副本集群 (修改配置文件重新启动服务)
sed -i '/replSet/d' /工作目录/etc/mongdb.conf
/工作目录/bin/mongod -f /工作目录/etc/mongdb.conf --shutdown
/工作目录/bin/mongod -f /工作目录/etc/mongdb.conf
副本集合的文件存储,数据都是存储再数据库local库下
操作日志的记录: oplog.rs
db.oplog.rs.find()
{ "ts" : Timestamp(1541210028, 1), "h" : NumberLong("2710323194495978686"), "v" : 2, "op" : "n", "ns" : "", "wall" : ISODate("2018-11-03T01:53:48.154Z"), "o" : { "msg" : "initiating set" } }
{ "ts" : Timestamp(1541210040, 1), "t" : NumberLong(1), "h" : NumberLong("7795182114557876968"), "v" : 2, "op" : "n", "ns" : "", "wall" : ISODate("2018-11-03T01:54:00.243Z"), "o" : { "msg" : "new primary" } }
{ "ts" : Timestamp(1541210040, 2), "t" : NumberLong(1), "h" : NumberLong("-1376008138436262787"), "v" : 2, "op" : "c", "ns" : "config.$cmd", "ui" : UUID("ac663edb-b178-4c9a-9fda-2971043a46f3"), "wall" : ISODate("2018-11-03T01:54:00.393Z"), "o" : { "create" : "transactions", "idIndex" : { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "config.transactions" } } }
存储rs.status()的数据信息 system.replset
db.system.replset.find()
{ "_id" : "rs1", "version" : 1, "protocolVersion" : NumberLong(1), "members" : [ { "_id" : 0, "host" : "192.168.4.51:27051", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, ……
如何判断哪个从库做主库
1. 有一个从库发现主库出现问题,就会通知其他所有从库,然后其他从库都会立刻连接主库
2. 如果主库的确挂量,那么从库就会根据数据的更新时间进行比较
3. 最后,优先级最高的从库就会成为主库,如果优先级相同,比较更新时间,最近的成为主库
对于数据库文档的基本操作
添加文档
db.集合.save({字段1:值1,字段2:值2……}) | 添加或修改一个文档的值 |
如果想修改,必须指定_id的值,这样save就会修改指定文档的所有参数 | |
db.集合.insert({字段1:值1,字段2:值2……}) | 只添加一个文档的值 |
如果字段设定值中设置了_id的值,且这个_id的值事实存在,那么就会报错 | |
db.集合.insertMany([{字段1:值1,字段2:值2……},{字段m:值m,字段n:值n……}]) | 同时添加多个文档的值 |
和insert一样,只能添加不能修改 |
查看文档
db.集合.find() | 查看所有行 |
db.集合.findOne() | 查看第一行 |
db.集合.find({条件}) | 根据条件限制显示 |
db.集合.find({},{字段1:1,字段2:0}) | 指定显示出来的字段信息 |
指定字段值为1代表只显示该字段,指定字段为0代表只不显示该字段 | |
如果有一个字段最后设定值为1或0,其他所有字段的值只能一样,否则报错,除了_id最终设定值可以和其他的不一样,而如果_id和其他的字段设定不同,那么优先只显示这条件(之所以强调最后设定值,是一个重要注意点,详细解释在下面) | |
db.集合.find().limit(数字) | 限制输出多少行 |
如果数字大于20,那么也先只显示20行,然后再用it显示大于20小于指定值的部分 | |
如果限制的值比查询得到的总值大,那限制就没有具体作用 | |
db.集合.find().skip(数字) | 跳过前多少行还是查找读取 |
跳过前面n行,从第n+1行开始输出 | |
db.集合.find().sort(字段:升降序) | 根据某个字段升降序排列输出 |
1代表升序,2代表降序 | |
可以一起使用: db.集合.find({条件},{字段1:1,字段2:0}).limit(数字).skip(数字).sort(字段:升降序) |
修改文档
db.集合.update({条件},{修改字段:值,……}) | 修改符合条件的所有文档中的第一个文档,把里面所有数据清空,然后改为指定修改的字段 |
要注意两点,1.只作用于满足条件的第一个文档,2.修改字段会先删除其他的所有内容再添加,下面注意中有例子 | |
db.集合.update({条件},{修改字段:值,……},false,true) | 修改符合条件的所有文档,把里面所有数据清空,然后改为指定修改的字段 |
可以和下面的修改操作连用,一般操作的时候都会加上这两个关键字 | |
db.集合.update({条件},{$set:{修改字段:值,……}}) | 修改符合条件的所有文档中的第一个文档,把指定字段修改后,不清空其他数据 |
db.集合.update({条件},{$unset:{删除字段:值,……}}) | 修改符合条件的所有文档中的第一个文档,删除指定值的字段 |
db.集合.update({条件},{$inc:{删除字段:数字,……}}) | 修改符合条件的所有文档中的第一个文档,让指定值的字段加上后面的数字 |
db.集合.update({条件},{$push:{数组字段:值,……}}) | 修改符合条件的所有文档中的第一个文档,向数组添加新元素 |
如果添加的值是原来数组就有的也可以添加进去的 | |
db.集合.update({条件},{$addToSet:{数组字段:值,……}}) | 修改符合条件的所有文档中的第一个文档,向数组添加数组中不存在的新元素 |
db.集合.update({条件},{$pop:{数组字段:数字,……}}) | 修改符合条件的所有文档中的第一个文档,删除数组中倒数第几个元素 |
db.集合.update({条件},{$pull:{数组字段:值,……}}) | 修改符合条件的所有文档中的第一个文档,删除数组中指定值的元素 |
删除文档
db.集合.drop() | 删除集合,包括集合内容以及索引 |
db.集合.remove({条件}) | 删除集合中指定的文档 |
db.集合.remove({}) 删除集合中所有内容,但是索引依然存在 |
匹配条件(不单独使用,在其他语句中的条件栏写如下内容,当然大括号不要重复,拿上面的{条件}和下面进行替换,因为下面写了大括号了)
{字段:值} | 相等匹配 |
{字段1:值1,字段2:值2……} | 多个字段值都相等的时候匹配 |
{字段:{$in:[值1,值2……]}} | 字段等于这么多值中的一个就匹配(这么多值可以有相等的情况,但是毫无意义) |
{字段:{$nin:[值1,值2……]}} | 字段等于这么多值中的一个就不匹配,和里面完全不同的才匹配 |
{$or:[{字段1:值1},{字段2:值2}……]} | 多个字段值中有一个相等的时候匹配 |
{字段:/正则表达式/} | 正则匹配 |
注意只有字符类型数据才会参与正则匹配 | |
{字段:{$数值匹配符号:数字}} | 数值的大小比较 |
$lt $lte $gt $gte $ne | |
小于 小于等于 大于 大于等于 不等于 | |
{字段:{$gte:10,$lte:50}} 字段值 大于等于10、小于等于50 匹配 | |
{字段:null} | 匹配值为空,或者某个文档中没有该字段 |
这些匹配条件可以混用,基本组合可以得到想要的检索出来的数据。
注意:
1. 字段_id是一个文档的唯一标识,可以是对象类型,也可以是其他类型,反正必须满足不能一样
2. 如果添加或者修改的时候在一个文档的内容中先后添加两个一样字段名,后者覆盖前者,不单单限于文档的修改,诸多涉及字段赋值都有这个特性,比如查询设置条件,排序等等,举例如下
# 最后只显示了name字段,等价于 db.passwd.find({},{name:1})
rs1:PRIMARY> db.passwd.find({},{_id:1,name:1,_id:0})
{ "name" : "root" }
{ "name" : "bin" }
# 虽然有_id设为只显示,但后来被修改了,最终设定值就变为0了,最就是只不显示name和_id,也就等价于 db.pdb.passwd.find({},{name:0,_id:0})
rs1:PRIMARY> db.pdb.passwd.find({},{_id:1,name:0,_id:0})
{ "password" : "x", "uid" : 0, "gid" : 0, "comment" : "root", "homedir" : "/root", "shell" : "/bin/bash" }
asswd.find({},{_id:1,name:0,_id:0})
# 最后条件就变成了name是adm的文档内容,等价于 db.passwd.find({name:'adm'},{name:1})
rs1:PRIMARY> db.passwd.find({name:'root',name:'adm'},{name:1})
{ "_id" : ObjectId("5bdc0e50071f0652a84bc6a1"), "name" : "adm" }
可以理解为,任何语句中出现 {字段:值1,字段:值2,字段:值3} 的时候,可以将其等价为 {字段:值3},很重要,但是请注意,字段一样的时候才成立,不过还是尽量不要这么写,因为浪费资源
3. 所有的修改操作save、update没有特殊设定的时候默认都是覆盖修改,其操作过程是先删除文档中的所有数据,再添加指定的值;而且都只能操作修改一个文档
# 现在就update进行操作示范
rs1:PRIMARY> db.example.find()
{ "_id" : ObjectId("5bdd53b86be3955fa462fda2"), "name" : "test1", "gold" : "90" }
{ "_id" : ObjectId("5bdd53bd6be3955fa462fda3"), "name" : "test2", "gold" : "80" }
{ "_id" : ObjectId("5bdd53c16be3955fa462fda4"), "name" : "test3", "gold" : "70" }
rs1:PRIMARY> db.example.update({name:'test1'},{gold:'100'})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
rs1:PRIMARY> db.example.find()
{ "_id" : ObjectId("5bdd53b86be3955fa462fda2"), "gold" : "100" }
{ "_id" : ObjectId("5bdd53bd6be3955fa462fda3"), "name" : "test2", "gold" : "80" }
{ "_id" : ObjectId("5bdd53c16be3955fa462fda4"), "name" : "test3", "gold" : "70" }
# 这里我们看到修改后,原来test1修改后name字段没了,只有设置修改只剩余gold字段,解决方案是$set
rs1:PRIMARY> db.example.update({},{name:'test'})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
rs1:PRIMARY> db.example.find()
{ "_id" : ObjectId("5bdd53b86be3955fa462fda2"), "name" : "test" }
{ "_id" : ObjectId("5bdd53bd6be3955fa462fda3"), "name" : "test2", "gold" : "80" }
{ "_id" : ObjectId("5bdd53c16be3955fa462fda4"), "name" : "test3", "gold" : "70" }
# 这里我们看到没有条件,那么三个文档都匹配,但只有第一个文档被修改了,解决方案是 false,true
4. 修改文档内容中,所有 $关键字 连接的数据,都是只修改指定字段,不会删除其他部分
5. 设定
6. 修改文档中的pop删除数组元素,是从最后开始数,
db.集合.update({条件},{$pop:{数组字段:1}}) # 删除数组的最后一个元素
db.集合.update({条件},{$pop:{数组字段:-1}}) # 删除数组的第一个元素