Mongodb 使用经验

MongoDB使用经验

 

 

执行mongo 默认执行.mongorc.js 脚本文件

.mongorc.js 文件存储在 root 目录下 对一般用户,~表示/home/(用户名)

对于root用户,~表示/root

可以在启动时 加上 --norc 命令 就可以禁止加载 .mongorc.js

 

批量插入

db.foo.insertMany 命令可以批量插入;

默认批量插入 大小为48MB,如果超过 多数驱动器会将数据拆分成多个48MB的批量插入请求。

如果在执行批量插入中有一条数据报错,那么在之后的数据都会插入失败。

可以使用ContinueOnError选项,这样就可以跳过错误数据 继续执行后面数据的插入。(shell并不支持这个选项,但是所有驱动程序都支持

 

插入校验

每个文档都必须小于16MB(可以在shell中执行 Object.bsonsize(doc)查看)

删除文档

删除数据是永久性的,不能撤销,也不能恢复。

 

Db.collection.drop() 删除整个集合

 

更新文档

Db.collection.update(参数1:查询文档,参数2:更新成当前文档) 会覆盖文档

如果有多个符合条件,则默认更新第一条

注意:Db.collection.update(query,{$set:{update}}) 使用set 函数只会更新 set指定的字段 不会完全覆盖

 

如果2个更新同时发生,先到达服务器的先执行,接着执行另外一个。最新的更新会取得胜利!

查询器(查询器是内层文档的键,写在键外部)

$elemMatch  (只查询数组 例:db.books.findOne({"arr":{$elemMatch:{$gt:3,$lt:5}}})  结果:查询数组arr 中大于3 小于5的记录,如果查询字段有索引 可以使用.min({“arr”:10}) and .max({“arr”:20}))

$size (查询数组长度为n的记录) 例:db.books.find({"ppp":{$size:3}})

$all (与$in有点相似,只不过$all是所有属性要与文档匹配。$in只匹配其一就行。)

$and (db.books.find({$and:[{"No":1},{"pp":12}]}))

$ne (not equels 例:db.orders.find({"order_id":{"$ne":"31"}}) 查找 order_id 不包含31 的记录)

$gt (大于

$gte (大于或等于)

$lt (小于)

$lte (小于或等于)组合使用db.mediaCollection.find( {Released : {$gte: 1990, $lt : 2010}}, { "Cast" : 0 } )

$in (db.mediaCollection.find( {Released : {$in : [1999,2008,2009] } })

$or (db.mediaCollection.find( {$or :  [ {Released:1999}, {Released:2008}, {Released:2009} ] } )

$nin (不包含)

$mod[0,1] (取模运算) db.books.find({"pp":{$mod:[5,2]}}) 查询pp 字段 除以5余2的记录

$text:{$search:value} 全文模糊匹配:必须先设置 集合的全文索引 (单字段必须全匹配不支持分词,或者说如果要分词必须把单词用符号或者空格隔开)

 

 

修改器 (修改器是外层文档的键,写在键内部)

$set(修改指定字段,也可以修改字段的类型)

$unset (删除指定字段)

$inc (递增是指定的字段,只能操作 整型,长整型,双精度浮点型;否则会报错)

$push(数组修改器,能在指定数组的末端添加一条数据,如果字段不存在则会创建这个字段)

$each (可以和push 配合使用 如:{$push:{orders_sn:{$each:[1,2,3,4,5]}}} , 这样会将 1,2,3,4,5, 五个元素添加进数组)

$slice 必须是负整数(必须将 slice 和 push 、each 配合使用,限制数组最大长度,保留最后指定的个数 例如:{$each:[123],$slice:-2} 则保留2,3)

$sort ($sort:1 升序,$sort:-1 降序) ,当$sort 与 $each 一起使用的时候,先执行 sort 再执行 each 例:

 

1:降序

db.orders.update({"order_id":"31"},{$push:{"order_sn":{$each:[1,2,3,4,5,6],$slice:-4,$sort:-1}}})

结果 order_sn[] = 4,3,2,1

1:升序

db.orders.update({"order_id":"31"},{$push:{"order_sn":{$each:[1,2,3,4,5,6],$slice:-4,$sort:1}}})

结果 order_sn[] = 3,4,5,6

$not (代表不匹配db.books.find({"pp":{$not:{$mod:[5,2]}}}) 结果:取模运算的相反记录)

$updateMany 批量更新

$addToSet (可以避免重复插入,如果已存在则不插入 结合$each例:db.orders.update({"order_sn":[4,3,2,1]},{$addToSet:{"order_sn":{$each:[1,2,6,7]}}})最后结果:432167)

$pop (删除数组中的元素,{$pop:{“field”:1}} 从尾端删除一个元素 ,{$pop:{“field”:-1}} 从头端删除一个元素)

 

$pull 只能用于包含数组的字段 (会将所有匹配的文档删除, 例如:db.orders.update({"order_sn":[4,3,2,1,1,6]},{$pull:{"order_sn":1}})  效果:删除所有1 的数组对象)

 

修改数组指定位置的值(db.orders.update({"order_sn":[4,3,2,6]},{$set:{"order_sn.0":1}} ,修改驻足位置0的值后,结果:1,3,2,6)

 

定位符 “$” (db.blog.update({comments.author:Join},{comments.$.author:Jim}) ,结果:会把comments数组中 Join的位置 用 $ 替代 ,修改成Jim)

 

$upsert (如果没有找到合适的条件,就会以这个条件 与 更新的文档组合成一个新的文档 例如:db.orders.update({order_id:100},{amout:120},true)) 第三个条件为true 则开启upsert

 

$setOnInsert (只会在新建文档的时候创建这个值,如果文档已存在则不会进行改变。db.books.update({"rep":3},{$setOnInsert:{"rep":4}},true) )

 

findAndModify 原子操作 (可以使用的字段:query查询 等同于findOne,sort排序,update更新,remove删除,new表示返回更新前的文档还是更新后的文档默认是更新前的文档,fields返回的字段,upsert值=true时,表示是一个upsert默认是false,update 和 remove必须有且同时只能有一个。例:db.books.findAndModify({"query":{"No":1},"update":{$set:{"pp":123}}})  查找No = 1 的数据, 并且更新 pp 字段喂 123 )

填充因子

默认填充因子=1 (无法手动设定填充因子)

如果添加或更新的新文档大小 超过原大小,填充因子(padding factor)会扩大。文档所在的原空间会被释放,并把修改后的文档存放到另一片空间。

 

优化策略:

1:把动态增长的数据独立放到一个集合里存放

2:如果你的模式在插入和删除时候会进行大量的移动或者经常打乱数据,可以使用usePowerOf2Sizes 选项提高磁盘复用率。可以通过collMod命令来设置这个选项:

db.runCommand({“collMod”:collectionName,”usePowerOf2Sizes”:true})

由于这个选项会导致初始分配空间不再高效,所以只应该在需要经常打乱的集合中使用。

 

Shell帮助程序

Save(文档):如果文档不存在,它会自动创建这个文档。如果文档存在且包含 _id 参数,则执行的是upsert 函数,否则调用 insert。 如果在shell中使用这个函数 就可以非常方便的对文档进行快速修改。

 

查询内嵌文档

查询姓名Joe Schmoe 的人:db.people.find({“name”:{“firstName”:”Joe”,”lastName”:”Schmoe”}})

注意:文档字段必须按顺序填写 lastName 和 firstName ,而且需要全文匹配。换位置就查不到了

查询文档可以用 . 来表达要进入内部 db.books.find({"user.lastname":"man"})

 

获取一致结果

Cursor = db.foo.find();

While(cursor.next()){

var doc = cursor.next();

doc = process(doc);

db.foo.save(doc);

}

当修改的文档持续增大时候,游标可能会返回由于体积变大而被移动到集合末尾的文档。

应对这个问题的方法就是执行 “查询快照” (snapshot)  db.foo.find().snapshot()

该方法会造成查询变慢

 

 

数据库命令

Drop :删除集合 ;db.runCommand({“drop”:”test”}) or Db.collectionName.drop()

 

执行计划

{

    "queryPlanner" : {

        "plannerVersion" : 1,

        "namespace" : "sang.sang_collect",

        "indexFilterSet" : false,

        "parsedQuery" : {

            "x" : {

                "$eq" : 1.0

            }

        },

        "winningPlan" : {

            "stage" : "COLLSCAN",

            "filter" : {

                "x" : {

                    "$eq" : 1.0

                }

            },

            "direction" : "forward"

        },

        "rejectedPlans" : []

    },

    "serverInfo" : {

        "host" : "localhost.localdomain",

        "port" : 27017,

        "version" : "3.4.9",

        "gitVersion" : "876ebee8c7dd0e2d992f36a848ff4dc50ee6603e"

    },

    "ok" : 1.0

}

参数

含义

plannerVersion

查询计划版本

namespace

要查询的集合

indexFilterSet

是否使用索引

parsedQuery

查询条件,此处为x=1

winningPlan

最佳执行计划

stage

查询方式,常见的有COLLSCAN/全表扫描、IXSCAN/索引扫描、FETCH/根据索引去检索文档、SHARD_MERGE/合并分片结果、IDHACK/针对_id进行查询

filter

过滤条件

direction

搜索方向

rejectedPlans

拒绝的执行计划

serverInfo

MongoDB服务器信息

 

 

 

 

 

 

 

{

    "queryPlanner" : {

        "plannerVersion" : 1,

        "namespace" : "sang.sang_collect",

        "indexFilterSet" : false,

        "parsedQuery" : {},

        "winningPlan" : {

            "stage" : "COLLSCAN",

            "direction" : "forward"

        },

        "rejectedPlans" : []

    },

    "executionStats" : {

        "executionSuccess" : true,

        "nReturned" : 10000,

        "executionTimeMillis" : 4,

        "totalKeysExamined" : 0,

        "totalDocsExamined" : 10000,

        "executionStages" : {

            "stage" : "COLLSCAN",

            "nReturned" : 10000,

            "executionTimeMillisEstimate" : 0,

            "works" : 10002,

            "advanced" : 10000,

            "needTime" : 1,

            "needYield" : 0,

            "saveState" : 78,

            "restoreState" : 78,

            "isEOF" : 1,

            "invalidates" : 0,

            "direction" : "forward",

            "docsExamined" : 10000

        }

    },

    "serverInfo" : {

        "host" : "localhost.localdomain",

        "port" : 27017,

        "version" : "3.4.9",

        "gitVersion" : "876ebee8c7dd0e2d992f36a848ff4dc50ee6603e"

    },

    "ok" : 1.0

}

 

索引

索引的类型:单列索引、覆盖索引、唯一索引、复合唯一索引、稀疏索引

单列索引:注意索引的方式(升序、降序等) 当你查询近期日期的时候 应该使用降序索引,因为索引是Btree的结构,右高左低。日期最近 值越高 ,应当使用降序索引效率更快。反之亦然。

 

覆盖索引/聚合索引:一般索引是用来查找对应文档,如果你的查询只需要返回索引所对应的字段,那就根本没必要获取实际文档。所需要的内容直接能从索引中获取。

使用覆盖索引时,把精确匹配的字段放第一位,把范围匹配的字段放最后一位。这样范围查询能直接从结果集内部进行查询。

 

隐式索引:如果一个索引 {“a”:1,”b”:1,”c”:1} ,那么他包含索引 {“a”:1} \{“a”:1,”b”:1}\{“a”:1,”b”:1,”c”:1} 这三个索引 。注意:索引字段必须依次排序才可以用 比如{“b”:1,”c”:1} 是不可以使用的

 

有些查询不能使用索引:

$where , $exists ,$nin ,$not(大多数not查询会退化为全表扫描)

 

一次查询使用几个索引?

Mongodb在一次查询中只能使用一个索引,$or 是个例外 ,因为$or 是执行2个查询然后合并结果集。通常来说执行2次查询往往没有1次查询效率高,因此尽可能使用$In 而不是$or .

 

Mongodb中允许对嵌套文档进行索引

比如(user.name),注意 对user 加索引 和user.name 加索引是不同的。 user 索引只会对整个子文档加快查询速度,比如 在查询 {“user”:{“name”:”1”}} 的时候,需要全文匹配才会触发索引。

 

也可以对数组进行索引

 

与子文档索引(上面)不同,无法对整个数组加上索引。对数组建立索引实际上是对数组的内一个元素加索引。如果元素有20个那么就是20个索引。注意:只有在精确匹配第11个元素以上时,数组索引才会生效。一个索引中的数组条目最多只能有一个。

 

索引基数

 

一个索引上不同的条目越多索引越有效 例如 username ,索引条目越少索引效果越低 例如 status

 

稀疏索引

稀疏索引的字段 = x

01{_id:0}

02{_id:01,x:0}

03{_id:01,x:1}

稀疏索引会返回 02 03 不会返回 01

 

索引存在哪里?

所有的索引信息都存储在 system.indexes 集合中 可以执行 db.collectionName.getIndexes()

 

 

特殊索引和集合

 

固定集合

固定集合已满的话,新插入的文档会把最老的文档给替换掉。

固定集合不能被分片。

 

没有_id索引的集合

如果创建了一个没有_id的集合,那就永远不能复制它所在的mongod了

 

TTL索引(time-to-live index)

这种索引允许为每个文档设置一个超时时间,一旦达到超时时间 就会删除这个文档。通常用于缓存。

 

 

全文索引

 

全文索引text ,模糊匹配集合内所有内容(不进行分词,不区分大小,但是可以用 or 来进行分词 如:$text:{$searcj:”value or value2”})。 必须使用text 命令才能使用这个索引。$text:{$searcj:”value”}  匹配到的文档是按照相关性排序的

 

也可以使用“-”指定特性的词不出现在结果中 如:$text:{$searcj:”-not value2”} 这样会返回匹配value2 但是不包含 not 的文档

 

分词必须按照空格 或者 符号把单词分割 才能查询到 例如: blue green red ;blue,green,red

 

地理空间索引

 

其中最常用的: 2dsphere(用于地球表面类型的地图) ,2d(用于平面地图或时间连续的数据)

 

2dsphere 允许使用JSON格式指定点、线、和多边形 例如:{“log”:{“type”:”Point”,{“cordinates”:[经度,维度]}}}

 

线可以用数组来表示、多边形也一样 如:[[0,1],[0,2],[0,3]]

 

地理空间查询的类型

 

可以使用多种不同类型的地理空间查询:交集、包含、

{“log”:{“type”:”Polygon”,”coordinates”:[[0,1],[0,2],[0,3]]}} log可以改变但是子对象必须不变。

Coordinate值可以为 点:point ,线:line,多边形:polygon

查询时,需要将希望查找的内容指定为 {$geometry:geoJsonDesc} 的对象。

例如,可以使用$geoIntersects 操作符查找与位置相交的文档。

 

交集查询$geoIntersects \ 包含 $within \ 距离查询 $near

 

$near 是唯一一个会对结果进行排序(距离从近到远)的索引

 

 

2D索引

 

2D索引适用去扁平表面而非地球表面。只能点对点进行索引。示例文档:

{“name”:”Water temp”,”title”:[32,32]}

$near 默认最多返回100个文档 最好进行分页:db.rule.find({“title”:{$near:[20,1]}}).limit(10)

 

可以直接适用$near 和 $within 查询 而不需要子对象 $geometry

$within 可以查询出某个形状(圆形,矩形,多边形)范围内的所有文档。

如果要使用矩形 可以指定$box 选项:db.rule.find({“title”:{$within:{$box:{[10,20],[15,30]}}}})

 

$box 接受一个二元数组,第一个参数指定左下角的坐标,第二个参数指定右上角的坐标。

 

类似的。可以使用$center 选项返回圆形范围内的所有文档,这个选项也是可以接受一个二元数组:db.rule.find({“title”:{$within:{$center:[[12,25],5]}}})

第一个参数是 圆心,第二个参数是半径  坐标轴例子如下:

 

 

还可以使用多个点来指定多边形:

db.rule.find({“title”:{$within:{$polygon:[[1,1],[2,2],[3,3]]}}})

这个例子会查询出包含三角形内的点的所有文档 最后一个点会连第一个点

 

 

聚合

几个简单聚合命令:count、distinct、group

使用聚合框架可以对集合中的文档进行变换和组合。

构建:可以利用多个构建创建管道,如下

filtering 筛选、projecting投射、grouping 分组、sorting 排序、limitting限制、skipping跳过

  1. ${project:{“author”:1}} 将 author从每个文档中投射出来
  2. ${group:{“author”:1}} 将按照 author 作者名字进行排序 group 可以进行多个字段分组(城市,地点) 因为城市可能一样,地点不一样。 
  3. ${limit:1} 截留文档数量为1
  4. ${sort:{“field”:-1}} 按照字段 降序排序

 

Demo:

 

db.orders.aggregate({$group:{"_id":"$pay_status","counts":{$sum:1}}},{$sort:{"counts":1}},{$limit:1})

注意:进行 group的字段 必须以 _id 为别名 。 {别名:$ 实际字段}

在Mongodb权威指南第二版时,聚合结果必须限制在16MB之内

管道操作符

 

前一个操作符会将操作的结果传递给下一个操作符,直到最后一个操作符将数据传输给客户端。

不同的管道操作符可以按任意顺序组合在一起使用,而且可以被重复任意次数。

例如:先执行 $match -》 $group - 》$match -》 (与之前的match 匹配不同的查询条件)

 

$match (可以使用所有常规的查询操作符 例如 $gt $lt $in ,但是不能使用地理位置操作符)

尽可能将$match 放在管道的前面位置执行。1,可以过滤掉数据减少管道工作量。 2,如果在投射和分组之前执行$match 可以是用索引

 

$project 可以从子文档中提取字段,并重命名字段。最简单的一个 $project 操作是从文档中选择想要的字段,可以是定包含或者不包含一个字段。如下可以只返回一个author字段:

Db.collection.aggregate({$project:{author:1,_id:0}})

也可以将投射过的字段重命名 如下:

Db.collection.aggregate({$project:{userName:$_id}})  result: userName:0123456

如果 tag 是一个 数组,那么 $tag.3 代表数组中第四个值

 

$unwind 可以将文档中的数组拆分为单独的文档 db.collection.aggregate({$unwind:$field})

 

数学表达式

 

$add 相加

Db.collection.aggregate({$project:{totalPay:{$add:[pay1,pay2]}}}) 把pay1 和pay 相加 为totalPay

 

$subtract 第一个表达式减去第二个表达式

db.test.aggregate({$project:{"totalpay":{$subtract:["$number","$num"]}}})

 

$multiply多个表达式相乘

$divide第一个表达式除以第二个表达式

$mod 第一个表达式除以第二个表达式取余

 

日期表达式 例子: db.orders.aggregate({$project:{"dates":{"$month":"$update_date"}}})

$year

$month

$week

$dayOfMonth

$dayOfWeek

$dayOfYear

$hour

$minute

$second

 

 

 

字符串表达式

$substr :[expr , startOffset,numToReturn]参数1:字符串2:开始的字节3:结束的字节  注意这里是字节 不是字符。

 

$concat 将给定的表达式或者字符串链接在一起作为返回结果

 

$toLower 返回小写

$toUpper 返回大写

 

逻辑表达式

$cmd :[x,y] 比较 x,y 如果x == y 返回0 如果 x < y 返回 负数 如果x > y 返回正数

$strcasecmp [x,y] 比较字符串区分大小写 ,只对罗马字符组成的字符串有效

$cond :[a,b,c] if a==true return b else return c;

$isNull [a,b] if a==null return b else return a;

 

 

 

MapReduce (用JS作为查询语言)

实现原理:

  1. 映射操作到集合中的每一个文档(这个操作要么无作为,要么产生一些键和X个值)
  2. 洗牌,按照键分组,将键值组成列表放入对应的键当中。
  3. Reduce,化简,把列表中的值化简成一个单值,这个值被返回然后继续洗牌,直到每个键的列表只有一个值为止,这个值也就是最终结果。

 

 

具体描述如下:

>db.collection.mapReduce(

   function() {emit(key,value);},  //map 函数

   function(key,values) {return reduceFunction},   //reduce 函数

   {

      out: collection,

      query: document,

      sort: document,

      limit: number

   })

使用 MapReduce 要实现两个函数 Map 函数和 Reduce 函数,Map 函数调用 emit(key, value), 遍历 collection 中所有的记录, 将 key 与 value 传递给 Reduce 函数进行处理。

Map 函数必须调用 emit(key, value) 返回键值对。

参数说明:

  • map :映射函数 (生成键值对序列,作为 reduce 函数参数)。
  • reduce 统计函数,reduce函数的任务就是将key-values变成key-value,也就是把values数组变成一个单一的值value。。
  • out 统计结果存放集合 (不指定则使用临时集合,在客户端断开后自动删除)。
  • query 一个筛选条件,只有满足条件的文档才会调用map函数。(query。limit,sort可以随意组合)
  • sort 和limit结合的sort排序参数(也是在发往map函数前给文档排序),可以优化分组机制
  • limit 发往map函数的文档数量的上限(要是没有limit,单独使用sort的用处不大)

 

数据:

执行的命令: this.name 代表当前文档的name字段,同样amount 也是,key代表 name 进行group 之后的数据, values 代表 key 对应的数据的 数组形式。

例如 {key:重庆小面,value=[123,456]}

db.location.mapReduce(function(){emit(this.name,this.amount)},function(key,values){return Array.sum(values)},{out:"post_total" })

 

 

 

 

 

 

 

返回数据:

{

    "result": "post_total",

    "timeMillis": NumberInt("34"),

    "counts": {

        "input": NumberInt("3"),

        "emit": NumberInt("3"),

        "reduce": NumberInt("1"),

        "output": NumberInt("2")

    },

    "ok": 1

}

timeMillis:执行的时间 毫秒为单位

Counts:{

Input:发送到map函数的文档个数

Emit:在map函数中emit 被调用的次数

Output: 结果集中的文档个数

}

 

参数说明:

  •  

Mapreduce:要操作的目标集合

  •  
  •  

Map:映射函数(生成键值对序列,作为reduce函数参数)

  •  
  •  

Reduce:统计函数

  •  
  •  

Query:目标记录过滤

  •  
  •  

Sort:目标记录排序

  •  
  •  

Limit:限制目标记录数量

  •  
  •  

Out:统计结果存放集合(不指定使用临时集合,在客户端断开后自动删除)

  •  
  •  

Keeptemp:是否保留临时集合

  •  
  •  

Finalize:最终处理函数(对reduce返回结果进行最终整理后存入结果集合)

  •  
  •  

Scope:向map、reduce、finalize导入外部变量

  •  
  •  

jsMode说明:为false时 BSON-->JS-->map-->BSON-->JS-->reduce-->BSON,可处理非常大的mapreduce,为true时 BSON-->js-->map-->reduce-->BSON

  •  
  •  

Verbose:显示详细的时间统计信息

  •  

 

 

应用程序设计

范式:插入速度快。反范式:读取数据块。

不适合Mongodb的场景:MongoDB不支持事物

 

 

  • 复制

副本集:一组服务器,其中有一个主服务器,用于处理客户端请求;还有多个备份服务器,用于保存主服务器的数据副本。如果主服务器崩溃了,备份服务器就会自动将其中一个成员升级为新的主服务器。

 

建立副本集测试

 

  1. 使用 mongo --nodb 启动一个mongo shell (这个命令是不链接任何DB的)
  2.  replicaSet = new ReplSetTest({"nodes":3}) (这行代码可以创建包含三个服务器的副本集,1主2备)
  3. replicaSet.startSet() 启动3个mongo 进程
  4. replicaSet.stopSet() 关闭副本集

replicaSet.initiate() 配置复制功能

默认从库是不可以查询的 要设置setSlaveOk() 才可以执行查询。

 

正式建立副本集(已有一个实例并且拥有数据)

 

  1. 以replSet 方式启动实例 ./mongo --replSet=spock --dbpath=/mongodb/dbpath 这里的spock代表副本集名称

 

2.设置副本集配置(注:_id 代表副本集名称,就是启动实例的时候传递进来的spock;members代表副本集的成员;之后的_id 代表每个实例的编号;host 代表主机名)

配置如下

Var config = {"_id":"spock","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"}]}

 

  1. 启动配置:rs.initiate(config) 让所有成员知道有这个副本集
  2. Rs.isMaster() 可以检查自身的配置.rs.config() 可以查看整个副本集配置状态

小技巧(将replSet 选项添加到每个成员各有的mongod.conf 文件中,以后启动的时候就会自动使用这个选项,如果有一个实例已经有数据了,那么只能在这台实例上操作第 2 个步骤,如果有2台以上实例有数据,那么就无法创建副本集)

  1. 添加或删除成员:添加成员 rs.add(“ip:port”) 删除成员 rs.remove(“ip.port”),当添加或删除成员操作成功是,shell中会出现许多无法连接数据库的信息。这是正常的,说明配置已经修改成功!
  2. 重新配置副本集 rs.reconfig() 重新配置副本集必须在主节点执行命令

 

设计副本集 (一定要是奇数,不能是偶数,包含仲裁者)

副本集中一个很重要的概念 “大多数”,选择主节点是由大多数决定。

如果副集中有些成员挂了或者不可用,并不会影响“大多数”。因为大多数是基于副本集的配置来计算的。

副本集中的成员总数

副本集中的大多数

1

1

2

2

3

2

4

3

5

3

6

4

7

4

 

假设有一个包含5个成员的副本集。其中3个不可用,那么剩下的2个节点(如果其中有主节点的话)都会退化成备份节点。

Priority: 更高的会被优先选举被主节点。可以通过 rs.config() 查看,当设置为0的时候表示永远不能成为主节点

 

Arbiter 仲裁者(参与选举,不保存数据)

1.仲裁者由于不需要处理数据,可以放在低配的服务器上。

2.如果可以,将仲裁者放入独立的服务器,以第三方角度观察副本集。

3.可以使用 rs.addArb(“ip:port”) 添加 仲裁者

4.最多只能使用一个仲裁者

 

 

隐藏成员(不能隐藏主节点)

Var config = rs.config()

Config.members[0].hidden = 0

使用rs.status() rs.config() 能够看到隐藏成员

使用 rs.isMaster() 就看不到了

 

延迟备份节点

当你的主节点因为BUG或认为破坏了数据,就可以从延迟备份节点恢复数据。

延迟备份节点的意思就是,主节点12.00 的数据,延迟x 秒再进行备份。

创建索引

如果某个备份节点,仅仅是备份数据那么你可能希望不创建索引,在该成员配置中指定 “buildIndex:false” ,这个选项可以阻止该成员创建索引。

这是一个不可逆的选择,另外也需要将 priority 设为0

 

 

副本集的组成

Mongodb复制功能是使用操作日志oplog实现的,oplog是主节点的local数据库中的一个固定集合。备份节点通过查询这个集合就可以知道需要进行的复制操作。

 

如果某个备份节点挂掉了,重启之后就会从最后一个操作开始同步。

 

回滚操作

回滚原理:检查2个节点的相同节点,相同点之后的进行回滚,进行重新操作。

无法回滚300M以上的数据,或30分钟以上的数据。如果回滚失败 就要重新进行同步

 

 

使用getLastError 等待执行result ,可以设置timeout 防止一直等待。

例如:{“getLastError”:1,”w”:”majority”,”wtimeout”:1000}  w也可以是数字 w:2

 

 

保证复制到每个数据中心的一台服务器上

 

  1. 首先按照数据中心对成员分类,可以在副本集的配置中添加一个字段 “tags”:

Var config = rs.config()

config.members[0].tags={“dc”:”us-east”}

config.members[1].tags={“dc”:”us-east”}

config.members[2].tags={“dc”:”us-west”}

config.members[3].tags={“dc”:”us-west”}

“tags” 是一个对象,每个成员可以拥有多个标签。

例如:{”dc”:”us-east”} 数据中心的服务器可能是 “high quality” 服务器,这样的话可以将 tags 字段配置为 {“dc”:“us-east”,”quality”:”high”}

 

  1. 创建自己的规则

可以通过在副本集的配置中创建 ”getLastErrorMode” 规则字段实现。

每条规则的形式都是 {“name”:{“key”:”number”}}

Name 就是规则的名称 。 key 就是标签键的值,所以这个例子中对应的是dc

Number 是2(因为在本例中,我们希望写操作被复制到”us-ease  和 us-west” 两个分组平均中每个分组至少1台服务器中)

  1. 在副本集中添加 “getLastErrorMode” 字段,创建下面的规则,重新执行配置。

Config.settings = {}

Config.settings.getLastErrorModes = [{“eachDc”:{“dc”:2}}

Rs.reconfig(config)

 

现在,可以对写操作应用这条规则:

Db.foo.insert({“name”:”leo”})

Db.runCommand({“getLastError”:1,”w”:”eachDc”,”wtimeout”:1000})

 

 

 

保证写操作被复制到可见节点中的大多数

Var config =rs.config()

Config.members[0].tags=[{“normal”:”A”}]

Config.members[1].tags=[{“normal”:”B”}]

Config.members[2].tags=[{“normal”:”C”}]

Config.members[3].tags=[{“normal”:”D”}]

#[{规则名称:{分组名称,数量}]}

Config.settings.getLastErrorModes = [{“visibleMajority”:{“normal”:3}}]

Rs.reconfig(config)

 

开始使用:

Db.foo.insert({“x”:1})

Db.runCommand({“getLastErro”:1,”w”:”visibleMajority”,”wtimeout”:1000})

命令会一直等待,知道写操作被复制到至少3个非隐藏节点

 

 

 

读请求分散

  1. Nearest ,讲读请求转发到 ping 值最低的备份服务器
  2. Secondary ,会始终将读请求分发到备份服务器。
  3. Secondary preferred 优先将读请求发送到备份节点,如果备份节点都不可用则发送到主节点。
  4. Primary ,将读请求始终发送到 主节点

 

 

 

 

 

 

 

你可能感兴趣的:(mongodb)