虚化环境: 每节点2核心,4G内存,40G存储空间,iSCSI存储
IP:10.25.34.239,240和241
操作系统:CentOS 6.2 64bit 内核版本2.6.32
文件系统: ext4
MongoDB版本:2.0.6
桥论坛发帖和回帖数据,数据复制10分,每份数据当成一个租户,租户之间用uuid识别
1. 每个论坛板块的前20篇帖子的标题
2. 每个用户的最近20篇帖子标题
3. 根据帖子id查询帖子内容和回帖内容前20条
4. 根据帖子id查询回帖的最后20条
1. 插入帖子
2. 插入回帖
1. 根据ID更新帖子标题
2. 更加ID更新帖子内容
3. 根据ID更新回帖内容
85%查询+10%的插入+%5的更新
#numactl --interleave=all${MONGODB_HOME}/bin/mongod --config conf/replSet.conf
(关闭NUMA)
Mongodb配置文件
[root@eBSM-Server2 conf]# more replSet.conf
dbpath=/var/mongodb/data
logpath=/var/mongodb/log/rs0.log
fork = true
logappend = true
port = 27017
noauth = true
journal = true
rest = true
directoryperdb = true
replSet = rs0
oplogSize = 2048
参考
http://docs.mongodb.org/manual/core/replication/#
在10.25.34.240上执行
./mongo
rs.initiate()
rs.conf()
rs.add("10.25.34.241:27017")
rs.add({_id:2,host:'10.25.34.239:27017', arbiterOnly: true});
PRIMARY>rs.status();
{
"set" : "rs0",
"date" :ISODate("2012-07-27T16:42:07Z"),
"myState" : 1,
"members" : [
{
"_id" : 0,
"name" :"mongodb240:27017",
"health" : 1,
"state" : 1,
"stateStr" :"PRIMARY",
"optime" : {
"t" :1343407300000,
"i" :1
},
"optimeDate": ISODate("2012-07-27T16:41:40Z"),
"self" : true
},
{
"_id" : 1,
"name" :"10.25.34.241:27017",
"health" : 1,
"state" : 2,
"stateStr" :"SECONDARY",
"uptime" :1168,
"optime" : {
"t" :1343407300000,
"i" :1
},
"optimeDate": ISODate("2012-07-27T16:41:40Z"),
"lastHeartbeat" : ISODate("2012-07-27T16:42:06Z"),
"pingMs" : 0
},
{
"_id" : 2,
"name" :"10.25.34.239:27017",
"health" : 1,
"state" : 7,
"stateStr" :"ARBITER",
"uptime" :27,
"optime" : {
"t" :0,
"i" :0
},
"optimeDate" :ISODate("1970-01-01T00:00:00Z"),
"lastHeartbeat" : ISODate("2012-07-27T16:42:06Z"),
"pingMs" :177088741
}
],
"ok" : 1
}
导入数据后,collection posts 123万左右,2.4GB,reply 1258万条,4.6GB,具体信息见下图private DB getDB() {
dbProperties = new MongoDBProperties();
dbProperties.load();
String ips = dbProperties.getIp();
String[] ip = ips.split(",");
StringBuilder ipBuilder = new StringBuilder();
int port = dbProperties.getPort();
List addrs = new ArrayList();
for (String ipaddr : ip) {
try {
addrs.add(new ServerAddress(ipaddr, port));
ipBuilder.append(ipaddr);
ipBuilder.append("\t");
} catch (UnknownHostException e) {
LOGGER.error(e);
}
}
String dbname = dbProperties.getDBName();
MongoOptions options = new MongoOptions();
options.autoConnectRetry = true;
options.connectTimeout = 2000;
try {
mongo = new Mongo(addrs, options);
mongo.setReadPreference(ReadPreference.SECONDARY);
} catch (Exception e) {
LOGGER.error(e.getMessage());
}
db = mongo.getDB(dbname);
LOGGER.info("connect to MongoDB Server, ip = " + ipBuilder.toString() + " port =" + port
+ " db = " + dbname);
return db;
}
1. 增加一个节点34.238,复制节点数据qiao和local库到data目录
2. 在Primary上增加一条数据
3. 启动34.238
4. rs.add("10.25.34.238:27017")
5. 等待238成为secondary节点后,查看增加的数据复制到了238节点上:实验结果,正确复制了数据到238上
连接IP设置:可以设置集群中任意一个和多个节点,驱动程序会自己发现集群拓扑结构,注:如果mongo = new Mongo(addrs, options)中addrs不是List,那么驱动只是会去找具体的那个IP地址,当前IP连接不上或者不是master节点,程序会出错。
1. 加入一个不存在的节点IP,不影响,不报错
2. 加入一个关闭了的replica set节点ip,驱动程序会报告该节点可能down,如果有一个以上的正常节点可以链接,应用程序会正常执行
2012-8-2 13:12:18com.mongodb.ReplicaSetStatus$Node update
警告: Server seendown: 10.25.78.241:27017
3. 当连接的所有节点都不存在的时候,驱动会报错。
com.mongodb.MongoException:Rare case where master=null, probably all servers are down
atcom.mongodb.DBTCPConnector$MyPort.get(DBTCPConnector.java:366)
4. 通过一个线程定期检测配置文件的方式,当配置文件发送变化的时候,定期更新Mongo对象和DB对象。注意:更新DB对象后需要重新执行DBCollection coll = db.getCollection(dbProperties.getCommentsColl());才能生效,否则coll对象依然会老的,不会起到配置文件变化,影响到系统的作用。
/**
* 定周期执行
*/
public void doCheck(){
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
long initialDelay = 20;
long delay = 20; //n秒钟检测一次
final Runnable checker = new Runnable() {
public void run() {
getDB();
}
};
scheduler.scheduleWithFixedDelay(checker, initialDelay, delay, TimeUnit.SECONDS);
}
5. mongo.setReadPreference(ReadPreference.SECONDARY);基本上的读请求走secondary节点,当有多个secondary节点的时候,请求会分布到多个secondary节点上
6. 动态增加一个secondary节点的时候,读负载会自动请求到secondary节点上,对应用透明
2个secondary节点+1个primary节点
1. 关闭一个secondary节点,部分查询报错,过几秒后,所有查询转移到另外一个secondary节点上
2012-8-2 15:11:04com.mongodb.ReplicaSetStatus$Node update
警告: Server seen down: mongodb240:27017
java.io.EOFException
atorg.bson.io.Bits.readFully(Bits.java:37)
atorg.bson.io.Bits.readFully(Bits.java:28)
atcom.mongodb.Response.(Response.java:39)
atcom.mongodb.DBPort.go(DBPort.java:128)
atcom.mongodb.DBPort.go(DBPort.java:93)
atcom.mongodb.DBPort.findOne(DBPort.java:146)
atcom.mongodb.DBPort.runCommand(DBPort.java:157)
atcom.mongodb.ReplicaSetStatus$Node.update(ReplicaSetStatus.java:255)
atcom.mongodb.ReplicaSetStatus.updateAll(ReplicaSetStatus.java:462)
atcom.mongodb.ReplicaSetStatus$Updater.run(ReplicaSetStatus.java:409)
2012-8-2 15:11:04 com.mongodb.DBPort _open
严重: going to sleep and retry. total sleep time after = 2154ms this time:100ms
2012-8-2 15:11:04 com.mongodb.DBPort _open
严重: going to sleep and retry. total sleep time after = 2060ms this time:100ms
2012-8-2 15:11:05 com.mongodb.DBPort _open
严重: going to sleep and retry. total sleep time after = 4418ms this time:200ms
2012-8-2 15:11:05 com.mongodb.DBPort _open
严重: going to sleep and retry. total sleep time after = 4310ms this time:200ms
2012-8-2 15:11:06 com.mongodb.DBPort _open
严重: going to sleep and retry. total sleep time after = 6880ms this time:400ms
等待超时后,查询retry,并正确返回结果
2. 关闭第二个secondary节点,部分查询报错,过几秒后,所有查询转移到primary节点上
3. 重新启动2个secondary节点,读请求自动转移到secondary节点上。此时关闭primary节点,重新选举一个新的primary节点,在此期间会出现一次错误,读请求不受影响,并自动转移到secondary节点上,新的primary节点上不出现读请求。错误内容如下:
2012-8-2 15:27:50 com.mongodb.ReplicaSetStatus$Nodeupdate
警告: Server seen down: 10.25.34.241:27017
设置mongo的连接ip只要任意一个replica set节点的ip都可以,不会报错,即使是连接的secondary节点
1. 停止primary节点,对应用的影响
初始Primary节点34.241,secondary节点:34.240和34.238
在程序insert的过程中,执行db.shutdownServer();关闭34.241节点,经过几秒钟后,34.240成为primary节点。
现象:
WriteResult result =commentsColl.insert(object);
String errorMsg = result.getError();
先抛异常com.mongodb.MongoException$Network:Software caused connection abort: recv failed
errorMsg报告:not master
此时34.240还没有成为primary节点
当34.240成为primary节点后,驱动报告
警告: Master switching from10.25.34.241:27017 to mongodb240:27017
然后正常可以插入记录
在这个过程中,一共5个线程,每个线程插入1000条记录,插入完成一次sleep 100ms,执行结果每个线程正常插入970条,异常30条
16:21:13,891第一次插入记录失败,到16:21:16 primary节点切换成功,耗时3秒钟左右