13
回答
mongodb 插入性能问题
利用AWS快速构建适用于生产的无服务器应用程序,免费试用12个月>>>   
各位大神们求解,为什么我插入的时候这么慢?是我的启动配置搞错了?还是java程序的问题?之前测试,用mongodb shell插入速度能达到1w-2w。求大神帮我瞧瞧我的程序。小弟不甚感激。

下面是我的java程序:

int a=500;
		long start=System.currentTimeMillis();
		System.out.println("---------"+start);
		while(a>0){
			MongodbTest mt=new MongodbTest("insert","test","person",500,"10.123.1.57",40005);
			mt.start();
			if (mt.isAlive()) {
				try {
					mt.join();
				} catch (InterruptedException e) {
					System.out.println(e.getMessage());
	                System.out.println(e.toString());
				}
			}
			a--;
			mt=null;
		}
		long end=System.currentTimeMillis();
		System.out.println("---------"+end);
		long sum=500*500;
		System.out.println(sum+"----"+(end-start));
		System.out.println("并发数:"+sum/(end-start));


public class MongodbTest extends Thread{
	private Mongo mongo;
	private DB db;
	private DBCollection dbc;
	private String oper;
	private String dbname;
	private String collname;
	private int count;
	
	public MongodbTest(String oper,String dbname,String collname,int count,String host,int port) {
		this.oper=oper;
		this.collname=collname;
		this.dbname=dbname;
		this.count=count;
		this.mongodb_content(host, port);
	}
	
	public MongodbTest(String oper,String dbname,String collname,int count) {
		this.oper=oper;
		this.collname=collname;
		this.dbname=dbname;
		this.count=count;
		this.mongodb_content("10.123.1.57", 40005);
	}
	
	public Mongo mongodb_content(String host,int port){
		try {
			//System.setProperty("MONGO.POOLSIZE", "20000");
			//实例化mongo,连接mongodb服务器
			this.mongo=new Mongo(host,port);
			//实例化DB,连接数据库		
			this.db=mongo.getDB(dbname);
			//实例化DBCollection,连接集合
			this.dbc=db.getCollection(collname);
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}catch (MongoException e) {
			System.out.println("Mongo Exception:" + e.getMessage());
            System.out.println("Mongo error code:" + e.getCode());
			e.printStackTrace();
		}
		return mongo;
	}
	
	@Override
	public void run() {
		if (this.oper.equals("insert")) {
			this.mongodb_insert();
		}else if (this.oper.equals("update")) {
			this.mongodb_update();
		}else if(this.oper.equals("query")){
			this.mongodb_query();
		}else if (this.oper.equals("delete")) {
			this.mongodb_delete_collection();
		}else if (this.oper.equals("queryColl")) {
			this.mongodb_query_collection();
		}else if (this.oper.equals("queryData")) {
			this.mongodb_query_database();
		}else if(this.oper.equals("select")){
			this.mongodb_select();
		}
	}
	
	/**
	 * 添加数据
	 */
	public void mongodb_insert() {
		String str = "您提交的百度推广充值失败:详情请咨询020-28863558。www.payeco.com [银联语音支付]";
		String str1="百度推广的短信";
		for (int i = 0; i < this.count; i++) {
			DBObject dbo=new BasicDBObject();
			dbo.put("DICTIONID", i);
			dbo.put("PARENTCODE", "Baidu_SMS");
			dbo.put("CODE", "Baidu_SMS."+i);
			dbo.put("CODENO", i);
			dbo.put("VALUE", str);
			dbo.put("REMARK", str1);
			try {
				dbc.insert(dbo);
			} catch (MongoException e) {
				System.out.println("Mongo Exception:" + e.getMessage());
                System.out.println("Mongo error code:" + e.getCode());
                e.printStackTrace();
			}
		}
		System.out.println("--------------每个线程插入的数量:"+dbc.getCount());
		collname=null;
		db=null;
		mongo=null;
	}
}

求大神们不吝赐教。小弟百拜谢了。




举报
养不胖
发帖于5年前 13回/6K+阅
共有13个答案 最后回答: 5年前

每个线程都持有一个mongo对象,不慢才怪

mongo可以全局共享,mongo本身就是一个连接池,不需要太多

可以看看mongo java的官方文档

The MongoClient instance actually represents a pool of connections to the database; you will only need one instance of class MongoClient even with multiple threads. See the concurrency doc page for more information.
http://docs.mongodb.org/ecosystem/tutorial/getting-started-with-java-driver/#getting-started-with-java-driver
--- 共有 1 条评论 ---
养不胖请问我应该怎么做? 5年前 回复
请问我这样做对么?
public class Demo {
	public static void main(String[] args) {
		int a = 500;
		long start = System.currentTimeMillis();
		System.out.println("---------" + start);
		while (a > 0) {
			MongodbTest mt = new MongodbTest("insert", "test", "person", 500);
			mt.start();
			if (mt.isAlive()) {
				try {
					mt.join();
				} catch (InterruptedException e) {
					System.out.println(e.getMessage());
					System.out.println(e.toString());
				}
			}
			a--;
			mt = null;
		}
		long end = System.currentTimeMillis();
		System.out.println("---------" + end);
		long sum = 500 * 500;
		System.out.println(sum + "----" + (end - start));
		System.out.println("并发数:" + sum / (end - start));

	}
}
public class MongodbTest extends Thread{
	private DB db;
	private DBCollection dbc;
	private String oper;
	private String dbname;
	private String collname;
	private int count;
public static Mongo getMongo() throws UnknownHostException {
		// 实例化mongo,连接mongodb服务器
		return new Mongo("10.123.1.57", 40005);
	}
public MongodbTest(String oper,String dbname,String collname,int count) {
 this.oper=oper;
 this.collname=collname;
 this.dbname=dbname;
 this.count=count;
mongodb_content()

}

	public void mongodb_content(){
		try {
			//实例化DB,连接数据库		
			this.db=getMongo().getDB(dbname);
			//实例化DBCollection,连接集合
			this.dbc=db.getCollection(collname);
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}catch (MongoException e) {
			System.out.println("Mongo Exception:" + e.getMessage());
            System.out.println("Mongo error code:" + e.getCode());
			e.printStackTrace();
		}
	}
	
	@Override
	public void run() {
		if (this.oper.equals("insert")) {
			this.mongodb_insert();
		}else if (this.oper.equals("update")) {
			this.mongodb_update();
		}else if(this.oper.equals("query")){
			this.mongodb_query();
		}else if (this.oper.equals("delete")) {
			this.mongodb_delete_collection();
		}else if (this.oper.equals("queryColl")) {
			this.mongodb_query_collection();
		}else if (this.oper.equals("queryData")) {
			this.mongodb_query_database();
		}else if(this.oper.equals("select")){
			this.mongodb_select();
		}
	}
	
	/**
	 * 添加数据
	 */
	public void mongodb_insert() {
		String str = "您提交的百度推广充值失败:详情请咨询020-28863558。www.payeco.com [银联语音支付]";
		String str1="百度推广的短信";
		for (int i = 0; i < this.count; i++) {
			DBObject dbo=new BasicDBObject();
			dbo.put("DICTIONID", i);
			dbo.put("PARENTCODE", "Baidu_SMS");
			dbo.put("CODE", "Baidu_SMS."+i);
			dbo.put("CODENO", i);
			dbo.put("VALUE", str);
			dbo.put("REMARK", str1);
			try {
				dbc.insert(dbo);
			} catch (MongoException e) {
				System.out.println("Mongo Exception:" + e.getMessage());
                System.out.println("Mongo error code:" + e.getCode());
                e.printStackTrace();
			}
		}
		System.out.println("--------------每个线程插入的数量:"+dbc.getCount());
	}
}

--- 共有 4 条评论 ---
养不胖不是的,我之前加过close()方法,结果还是一样的。好慢。 5年前 回复
dbtop回复 @养不胖 : 看你原来的代码还有一个问题,每次新new一个连接,但是没有通过MongoClient.close()关闭连接。 5年前 回复
养不胖那应该怎样修改呢?求大神指点。 5年前 回复
dbtop你的修改依然是每次打开一个新的连接。 5年前 回复
这样试试
public class MongodbTest extends Thread{
	private DB db;
	private DBCollection dbc;
	private String oper;
	private String dbname;
	private String collname;
	private int count;
        // 实例化mongo,连接mongodb服务器
        private static Mongo mongo = new Mongo("10.123.1.57", 40005);
        getMongo() throws UnknownHostException {
		
		return mongo;
	}
--- 共有 7 条评论 ---
养不胖之前有这样做过,但是不对。 5年前 回复
dbtop回复 @养不胖 : 改回原有代码,每个连接使用结束后关闭一下。 5年前 回复
养不胖加一个final也是一样的啊。根本解决不了问题。final只是表示不可以修改的。而Mongo是要抛出异常。 5年前 回复
dbtop回复 @养不胖 : 看新的写法,我没有环境没有试过。 5年前 回复
dbtopprivate static final Mongo mongo=new Mongo('10.123.1.57',40005); 5年前 回复
public class MongodbTest extends Thread{
	private DB db;
	private DBCollection dbc;
	private String oper;
	private String dbname;
	private String collname;
	private int count;
	private static Mongo mongo=new Mongo("10.123.1.57", 40005); 
	
	
	public MongodbTest(String oper,String dbname,String collname,int count) {
		this.oper=oper;
		this.collname=collname;
		this.dbname=dbname;
		this.count=count;
	}
	
	@Override
	public void run() {
		try {
			//System.setProperty("MONGO.POOLSIZE", "20000");
			//System.out.println(mongo.getAddress()+"..."+mongo.getConnector()+"---"+mongo.debugString()+"---"+mongo.getConnectPoint()+"---"+mongo.getMaxBsonObjectSize()+"----"+mongo.getMajorVersion()+"---"+mongo.getMinorVersion()+"---"+mongo.getReadPreference()+"---"+mongo.getMongoOptions()+"--"+mongo.getReplicaSetStatus()+"---"+mongo.getServerAddressList()+"--"+mongo.getUsedDatabases()+"---"+mongo.getWriteConcern());
			// /10.123.1.57:40005...com.mongodb.DBTCPConnector@87d472---DBTCPConnector: /10.123.1.57:40005 /10.123.1.57:40005---/10.123.1.57:40005---16777216----2---9---primary---description=null, connectionsPerHost=10, threadsAllowedToBlockForConnectionMultiplier=5, maxWaitTime=120000, connectTimeout=10000, socketTimeout=0, socketKeepAlive=false, autoConnectRetry=false, maxAutoConnectRetryTime=0, slaveOk=false, safe=false, w=0, wtimeout=0, fsync=false, j=false, cursorFinalizerEnabled=true, alwaysUseMBeans=false--null---[/10.123.1.57:40005]--[admin]---WriteConcern { "getlasterror" : 1} / (Continue Inserting on Errors? false)
			//实例化DB,连接数据库		
			this.db=mongo.getDB(dbname);
			//实例化DBCollection,连接集合
			this.dbc=db.getCollection(collname);
		}catch (MongoException e) {
			System.out.println("Mongo Exception:" + e.getMessage());
            System.out.println("Mongo error code:" + e.getCode());
			e.printStackTrace();
		}
		
		if (this.oper.equals("insert")) {
			this.mongodb_insert();
		}else if (this.oper.equals("update")) {
			this.mongodb_update();
		}else if(this.oper.equals("query")){
			this.mongodb_query();
		}else if (this.oper.equals("delete")) {
			this.mongodb_delete_collection();
		}else if (this.oper.equals("queryColl")) {
			this.mongodb_query_collection();
		}else if (this.oper.equals("queryData")) {
			this.mongodb_query_database();
		}else if(this.oper.equals("select")){
			this.mongodb_select();
		}
	}
	
	/**
	 * 添加数据
	 */
	public void mongodb_insert() {
		String str = "您提交的百度推广充值失败:详情请咨询020-28863558。www.payeco.com [银联语音支付]";
		String str1="百度推广的短信";
		for (int i = 0; i < this.count; i++) {
			DBObject dbo=new BasicDBObject();
			dbo.put("DICTIONID", i);
			dbo.put("PARENTCODE", "Baidu_SMS");
			dbo.put("CODE", "Baidu_SMS."+i);
			dbo.put("CODENO", i);
			dbo.put("VALUE", str);
			dbo.put("REMARK", str1);
			try {
				dbc.insert(dbo);
			} catch (MongoException e) {
				System.out.println("Mongo Exception:" + e.getMessage());
                System.out.println("Mongo error code:" + e.getCode());
                e.printStackTrace();
			}
		}
	}
}

看你原来的代码还有一个问题,每次新new一个连接,但是没有通过MongoClient.close()关闭连接。

官方文档上有这样一句话:

to dispose of an instance, make sure you call MongoClient.close() to clean up resources
你现在的新写法就只有一个连接,就没有必要关闭,但是在多线程的情况下会不会有问题不好说了。

不过可以测试一下。


--- 共有 1 条评论 ---
养不胖这个我关闭过,如果使用close().在多线程的情况下会照成客户端提前关闭的错误。也就是下面的connection reset by peer:socket write error 5年前 回复
这是之前封装的一个mongo链接的类,可以参考下。每次获取链接可以:
MongoDBPool.getInstance().getCollection()
这样多个线程就可以共享连接池了


package com.sharetour.db;

import java.net.UnknownHostException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.mongodb.DBCollection;
import com.mongodb.MongoClient;
import com.mongodb.DB;

public class MongoDBPool {
	
	public static final String HOST = "localhost";
	public static final int	   PORT = 27017;
	public static final String DB   = "db";
	public static final String USER = "";
	public static final String PWD  = "";
	
	
	private static MongoClient mongo;
	private static MongoDBPool mongoPool;
	
	private static final Log log = LogFactory.getLog(MongoDBPool.class);
	
	private MongoDBPool(){
		try {
			mongo = new MongoClient(HOST, PORT);
			log.info("mongo db connection success");
		} catch (UnknownHostException e) {
			log.error("mongo db connection error");
			e.printStackTrace();
		}
	}
	
	public static MongoDBPool getInstance(){
		if(mongoPool == null){
			synchronized(MongoDBPool.class){
				if(mongoPool == null){
					mongoPool = new MongoDBPool();
					log.info("MongoDBPool init completed");
				}
			}
		}
		return mongoPool;
	}
	/*
	 * 关闭mongo连接池
	 */
	public static void close(){
		if(mongo != null){
			mongo.close();
			mongo = null;
		}
		
	}
	
	public DB getDB(){
		DB db =	mongo.getDB(DB);
		if(USER != null && USER.length() != 0){
			if(db.authenticate(USER, PWD.toCharArray())){
				return db;
			}
		}
		return db;
	}
	
	public DBCollection getCollection(String coll){
		DB db =	getDB();
		return db.getCollection(coll);
		
	}
}
--- 共有 12 条评论 ---
断鸿@养不胖 可能是你并发线程数太多了吧 去搜搜mongo的性能测试数据吧 我没测过 不好说 5年前 回复
养不胖回复 @断鸿 : 恩,我就是这样用的啊。但是插入的数据每秒也才1k-2k左右。 5年前 回复
断鸿@养不胖 应该没事 5年前 回复
养不胖回复 @断鸿 : 每次我都用getMongo().getDB()这样调用的。算不算持有一个mongo连接池? 5年前 回复
断鸿回复 @养不胖 : ......我的意思是你每个线程要用conection的话,就去连接池里取,但mongo池在多个线程间共享,每个线程获得链接即可,不许要都自己持有一个私有的mongo连接池 5年前 回复
public static Mongo getMongo() throws UnknownHostException {
if (mongo ==null) {
// 实例化mongo,连接mongodb服务器
synchronized (MongodbTest.class) {
if (mongo==null) {
mongo=new Mongo("10.123.1.57", 40005);
System.out.println("创建新的线程!");
}
}
}
return mongo;
}
我这样写的,但是性能还是没有增加。
--- 共有 9 条评论 ---
养不胖回复 @断鸿 : 恩,是的,每个线程都会调用main方法,然后执行线程中的东西。 4年前 回复
养不胖回复 @dbtop : 不是我期望多少并发,我是测试mongodb最大能可以有多少个并发。 4年前 回复
养不胖回复 @断鸿 : 不是我期望有多少并发,我想测试看看,mongodb最高可以有多少并发。 4年前 回复
养不胖回复 @断鸿 : 你用发的那个方法。用mong.getCollection()的时候不是也每次都创建了? 5年前 回复
断鸿回复 @养不胖 : 那不还是每个Thread持有一个mongo池吗 5年前 回复
顶部