MongoDB Replica Set使用几点总结

本文会涉及到MongoDB副本集的初始化,读写性能,scala driver,简单运维等内容。


副本集初始化

在各个节点上replica set进程,

nohup numactl --interleave=all ./mongod --dbpath /home/mongodb/data/  --logpath /home/mongodb/mongodb-linux-x86_64-2.4.7/run.log --port 8017 --rest --journal --replSet smartq --oplogSize 500 --profile=1 --slowms=5 --fork &
我的启动中,开启了rest接口,journal log,设置了oplog size大小500M(因为之后再修改oplog大小会比较麻烦),还开启了慢查询profile,如果有不熟悉这三块日志的,可以参考下面的简单描述:

  1. Journal日志。Journal日志通过预写式的redo日志为MongoDB增加了额外的可靠性,开启该功能时,数据的更新会先写入Journal日志,定期提交,然后在真实数据上执行这些变更,如果服务器安全关闭,日志会被清除在服务器启动时,如果存在Journal日志,则会执行提交.启动Journal功能只需要在启动mongod时指定-journal参数即可,这样,系统的Journal信息都会被放到数据库目录(默认是/data/db)的journal文件夹中
  2. oplog日志。MongoDB的高可复用策略中有一种叫做Replica Sets,Replica Sets复制过程中一个服务器充当主服务器,而一个或多个服务器充当从服务器,主服务器将更新写入一个本地的collection中,这个collection记录着发生在主服务器的更新操作,并将这些操作分发到从服务器上。这个日志是一个capped Collection,且有大小之分,所以最好在启动mongod服务时配置好大小(单位:MB). mongd -oplogsize=1024
  3. 慢查询日志。慢查询日志记录了执行时间超过所设定时间阈值的操作语句,慢查询日志对于发现性能有问题的语句很有帮助,建议开启此功能经常分析该日志的内容.要配置这个功能值需要在mongod启动时指定profile参数即可。Eg.将超过5s的操作记录都记录下来 mongod --profile=1 --slowms=5 运行一段时间后,可以通过查看db.system.profile 这个collection来获取慢日志信息
要注意的是

numactl --interleave=all
这块设置。NUMA和UMA(SMP)多核CPU架构的不同实现方式,推荐阅读下 Introduction to Parallel Computing的章节内容。如果不设置这个参数,进入mongod后会有相应提示,可能带来的问题可以参考 记一次MongoDB性能问题,附原理解析这篇文章。

MongoDB Replica Set使用几点总结_第1张图片

起了各个节点后,连接到某一台mongod上,进行副本集初始化工作:

var config = { 
  _id: "smartq", 
  members: [ 
    {
      _id:0, 
      host:"host0-ip:8017"
    }, 
    {
      _id:1, 
      host:"host1-ip:8017"
    }, 
    {
      _id:2, 
      host:"host2-ip:8017"
    } 
  ] 
}
rs.initiate(config)
输入rs.status()可以查看primary和secondary节点情况,刚初始化的时候节点的状态会经历一些变化,之后选举出primary。更多指令可以参考 rs.help() 。更多细节可以参考文章 mongodb副本集架构搭建


读写性能

我的副本集的写性能,在java driver环境下,差不多是1W-2W+ 行每秒,吞吐量大约2M+ 每秒。写只能在primary节点上进行。

我的副本集的读性能,在不带索引的情况下,DBCursor的扫描速度是4K~7K 行 每秒。输入一个查询,执行后,返回一个DBCursor是很快的,但是游标的顺序fetch行数还是比较慢的。我尝试了DBCursor提供的一些方式(我使用的是mongo-java-driver-2.10.1的包),对比了下以下几种获取速度,

rs = coll.find().sort(new BasicDBObject("_id", 1)).toArray();
rs = coll.find().batchSize(100).limit(l).toArray();
rs = coll.find().toArray(); // toArray()开销
Iterator it = coll.find().iterator(); // little faster than toArray()
while (it.hasNext()) {
    it.next();
}
刚开始一直使用toArray()的方式把DBCursor能指向的数据全部吐到内存里来,但其实toArray()的开销稍稍大于返回一个iterator之后逐个扫描一次。而基于_id字段进行排序之后再toArray(),带来的额外开销很少,侧面说明_id字段因为有索引,做排序很快很方便,值得好好利用。batchSize这个设置,我尝试设置了100,1000,感受是对于几万到几十万的数据吞吐没有多大影响。

总结是读性能在速度上还是需要索引支撑,且在能承受最终一致性的前提下,将读分布到secondary上缓解。


Scala Driver
尝试了下Scala Driver来进行读性能的测试,速度和java driver是一致的,而scala driver本身也是对java driver的简单封装,且目前支持的api也不全。

Scala Driver项目叫Casbah,是10gen官方的Toolkit。在build.sbt下的配置如下:

name := "hi-scala"

organization := "xx.xxx.xxx"

version := "0.0.1-SNAPSHOT"

scalaVersion := "2.9.3"

libraryDependencies ++= Seq(
  "org.mongodb" %% "casbah" % "2.6.3"
)
简单使用:

import com.mongodb.casbah.Imports._

object CasbahTest extends Logging {
  
  def main(args: Array[String]): Unit = {

    val mongoClient = MongoClient("host-ip", 8017)
	
    val db = mongoClient("db")

    val coll = db("collection")

    val start = System.currentTimeMillis()

    val rs = coll.find()

    // for (doc <- rs) {
    //   doc
    // }

    logInfo("Result: " + rs.size) // no toArray()

    val end = System.currentTimeMillis()

    logInfo("Time: " + (end-start))
    
  }
}
更多内容参看 Casbah Tutorial

Copy Collection

在同个db下拷贝collection似乎没有快速的方法,那就写简单的js,进行insert操作,

 var i = 0;
 while(i < 10000) {
   var cname = 'copy' + i;
   db.copy1.find().forEach( function(x){
     db.getCollection(cname).insert(x);
   });
   i++
 }
然后让mongod在后台执行,效率也很慢,

nohup  ./mongo localhost:8017/hdfs ../../jscript/copy_collection.js &
执行一段时间后,secondary的status可能会显示RECOVERING,原因是oplog记录的内容过多,primary的oplog可能已经重新刷过一次了,导致secondary与primary脱节,无法再持续进行本身的同步数据的操作。解决方法是把secondary kill掉,删掉data数据,重新起mongod加入副本集。这利用的是 副本集同步机制中的初始化同步,即对于新的没有数据的member,拷贝现有副本集内某个member的整份数据,整个过程流程为先clone数据,然后apply all changes,最后建索引。启动之后,secondary会出于STARTUP状态,开始比较快速地进行数据的同步,这里比较快也就是上百M每秒的样子。

(全文完)


你可能感兴趣的:(mongodb)