一、相关说明
前面有一篇博客写的是多线程读文本写入OracleNoSQL,但是写入的效率很慢(数据库和客户端不在一个机器上):三个文件夹(三个线程分别读里面的文本,共有4.3G*3大小的原始数据,花费时间大约为5-6小时)自己就想对比下写入MongoDB的效率和写入OracleNoSQL的效率。所以这就博客出现的原因。
同样由于代码过长,也没什么营养,主要是贴出来怕自己忘记。
二、代码
1.工程一览图
2.constructDoc.java
package oracle.common;
import java.util.Date;
<span style="color:#ff0000;">import org.bson.Document;</span>
import com.mongodb.client.MongoCollection;
public class constructDoc {
public static Document construct(String[] fields)
{
int fieldsLen=fields.length;
Document result=null;
Document attribute=null;
Date date=commonFunc.StringToDate(fields[1]);
int cioid=commonFunc.StringToInt(fields[5]);
int cgpsf=commonFunc.StringToInt(fields[7]);
int cspeed=commonFunc.StringToInt(fields[8]);
int cdir=commonFunc.StringToInt(fields[9]);
float dy_54=commonFunc.StringToFloat(fields[fieldsLen-8]);
float dx_54=commonFunc.StringToFloat(fields[fieldsLen-7]);
float totalkm=commonFunc.StringToFloat(fields[fieldsLen-5]);
int datekm=commonFunc.StringToInt(fields[fieldsLen-4]);
int shtxt=commonFunc.StringToInt(fields[fieldsLen-1]);
attribute=new Document("CTELNUM", fields[2])
.append("DX", fields[3])
.append("DY", fields[4])
.append("CIOID", cioid)
.append("CSTATUS",fields[6])
.append("CGPSF",cgpsf)
.append("CSPEED",cspeed)
.append("CDIR",cdir)
.append("ADDRESS",fields[10])
.append("DALARMPEOPLE",fields[fieldsLen-6])
.append("TOTALKM",totalkm)
.append("DATEKM",datekm)
.append("SHTXT",shtxt);
result=new Document("objectid",fields[0])
.append("start_time", date)
.append("end_time", date)
.append("center", <span style="color:#ff0000;">new Document</span>("lat",dy_54).append("lon", dx_54))
.append("attribute",attribute);
if(date==constants.StringToDateFunc_Exception
||cioid==constants.StringToIntFunc_Exception
||cgpsf==constants.StringToIntFunc_Exception
||cspeed==constants.StringToIntFunc_Exception
||dx_54==constants.StringToFloatFunc_Exception
||dy_54==constants.StringToFloatFunc_Exception
||totalkm==constants.StringToFloatFunc_Exception
||datekm==constants.StringToIntFunc_Exception
||shtxt==constants.StringToIntFunc_Exception)
{
return null;
}
return result;
}
}
3.ReadData.java
package oracle.writeMongo;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import oracle.common.connectMongo;
import oracle.common.constructDoc;
import org.bson.Document;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
public class ReadData implements Runnable{
private String folderPath;
private MongoDatabase db;
private String encoding="GBK";
public ReadData(String folderPath,MongoDatabase db) {
// TODO Auto-generated constructor stub
this.folderPath=folderPath;
this.db=db;
}
private static String[] getFilePaths(String folderPath)
{
File files=new File(folderPath);
String[] result=files.list();
for(int i=0;i<result.length;i++)
{
result[i]=folderPath+"/"+result[i];
}
return result;
}
public void run()
{
String[] filePaths=getFilePaths(folderPath);
BufferedReader bufferedReader=null;
try
{
MongoCollection<Document> objectStatesCollec=db.getCollection("st_objectstates");
for(int i=0;i<filePaths.length;i++)
{
System.out.println("开始读取"+folderPath+"第"+(i+1)+"个文本。。。");
File file =new File(filePaths[i]);
if(file.exists()&&file.isFile())
{
InputStreamReader reader=new InputStreamReader(new FileInputStream(file),encoding);
<span style="color:#ff0000;">bufferedReader=new BufferedReader(reader,1024*1024*80);</span>
String lineTxt="";
Document doc=null;
while((lineTxt=bufferedReader.readLine())!=null)
{
String[] fields=lineTxt.split(",");
//List<Document>documents=new ArrayList<Document>();
doc=constructDoc.construct(fields);
if(doc!=null)
{
objectStatesCollec.insertOne(doc);
}
}
reader.close();
}
System.out.println("读取"+folderPath+"第"+(i+1)+"个文本结束!");
}
System.out.println("存储任务结束!");
}
catch(Exception e)
{
System.out.println(e.getMessage());
return;
}
}
}
注意红色部分的代码,如果BufferedReader的第二个参数设置过大,会出现超出虚拟机内存的错误。同时,要理解这行代码的意思。
这行代码的意思就是:设置一次读入缓冲区的文本大小,这里为80m.在while里面循环的时候,就每次从缓冲区里读一行,然后进行解析。当缓冲区里读完了,代码会自动再次读80m的文本到缓冲区【需要切记的是这行代码不能写到while循环里,这是我们容易理解错的地方】。
4.connectMongo.java
package oracle.common;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;
public class connectMongo {
public static MongoClient getClient(String ip,int port)
{
try
{
return new MongoClient(ip,port);
}
catch(Exception e)
{
System.out.println("获取MongoClient失败,"+e.getMessage());
return null;
}
}
public static MongoDatabase getDB(MongoClient mongoClient,String dbName) {
try
{
return mongoClient.getDatabase(dbName);
}
catch(Exception e)
{
System.out.println("获取数据库失败,"+e.getMessage());
return null;
}
}
public static boolean closeClient(MongoClient mongoClient)
{
try
{
mongoClient.close();
return true;
}
catch(Exception e)
{
System.out.println(e.getMessage());
return false;
}
}
}
5.mainFunc.java
package oracle.main;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;
import oracle.common.connectMongo;
import oracle.writeMongo.ReadData;
public class mainFunc {
public static void main(String[] args) {
// TODO Auto-generated method stub
String ip="localhost";//<strong><span style="color:#ff0000;">如果数据库不在客户端,请改为对应IP</span></strong>
int port=27017;
String dbName="testdb";
MongoClient client=connectMongo.getClient(ip, port);
if(client!=null)
{
MongoDatabase db=connectMongo.getDB(client, dbName);
if(db!=null)
{
ReadData readThread1=new ReadData("/data/1-1",db);
new Thread(readThread1).start();
ReadData readThread2=new ReadData("/data/1-2",db);
new Thread(readThread2).start();
}
}
}
}
从上面代码可以看出,每个线程读的是不同文件夹下的文本。
剩下的java文件和之前的那篇“多线程读文本写入OracleNoSQL”差不多,因为是要比较写入的性能,所以要尽量保持数据的存入结构和代码的结构一致。
整个工程,可以
点击这里下载。【工程里面没有mongoDB的java driver,需要自己下载并添加进去】
同时,需要强调的是,由于本人不会如何监听除了主线程之外的所有线程何时结束,所以工程的代码中并没有在整个程序结束的时候关闭client.对于我们的程序没有影响,但是要记住自己确实没有关闭。
多线程只能公用一个MongoClient.官方文档上有说明,
查看这里,或者查看截图:
三、性能对比
同样地数据,同样地服务器,同样地线程数,写入OracleNoSQL需要5个多小时,接近6小时,写入MongoDB3小时四十分钟。这说明了写入MongoDB的效率比写入OracleNoSQL的效率高。这只是三个线程,如果是五个线程、六个线程,写入MongoDB的效率肯定能达到写入OracleNoSQL的两倍。
注:三个线程并不是最快的,读者可以将要读取的文本组织到多个文件夹(大于3)下,然后多线程读。