cassandra Oracle区别,用JFreeChart 来分析Cassandra/Oracle插入海量数据的性能

为了分析在插入海量数据到Cassandra集群或者Oracle时的表现,也就是插入速率,我们用java程序对插入数据的用时进行了采样,最终用JFreeChart把采样结果绘制出来了。

为了公平起见,我们做了以下处理:

1.所有的循环变量都放在了循环外面

2.对于Cassandra的replication-factor设置为1,这样插入数据不需要插入额外的备份。

3.对于Oracle我们用预编译语句,这样插入操作的执行计划可以重用。

4.所有的测试都在周末进行,这样不可能有其他人去干扰这些服务器。

5.这些机器上运行的其他进程都被我kill掉了,这样保证CPU,内存的专用性。

6.在用java代码插入Cassandra记录时候,我采用了thrift API, 因为它的效率比Hector API高。

以下是实验(分两部分,一是采样部分,二是数据分析部分)

Part 1:采样:

Cassandra的采样:

我们这里依然用循环插入50W条记录,不同的是,在循环的开始和循环每10000条记录时,我们把时间戳记录在List中,最终把这个List写入文本文件(cassandra_input_sample_data.txt):

packagecom.charles.cassandra.demo;

importjava.io.File;

importjava.io.FileWriter;

importjava.util.ArrayList;

importjava.util.List;

importorg.apache.cassandra.thrift.Cassandra;

importorg.apache.cassandra.thrift.Column;

importorg.apache.cassandra.thrift.ColumnParent;

importorg.apache.cassandra.thrift.ConsistencyLevel;

importorg.apache.cassandra.thrift.TBinaryProtocol;

importorg.apache.thrift.protocol.TProtocol;

importorg.apache.thrift.transport.TFramedTransport;

importorg.apache.thrift.transport.TSocket;

importorg.apache.thrift.transport.TTransport;

importcom.charles.cassandra.util.CassandraOperationUtil;

publicclassCassandraClusterStressTest

{

publicstaticvoidmain(String[] args)

throwsException

{

//包装好的socket

TTransport tr = newTFramedTransport(newTSocket("192.168.129.34",9160));

TProtocol proto = newTBinaryProtocol(tr);

Cassandra.Client client = newCassandra.Client(proto);

tr.open();

if(!tr.isOpen())

{

System.out.println("无法连接到服务器!");

return;

}

System.out.println("开始压力测试,我们插入50W条数据到2节点集群中");

System.out.println("...");

//标记开始时间

longstartTime = System.currentTimeMillis();

client.set_keyspace("Charles_Stress_Test2");//使用Charles_Stress_Test keyspace

ColumnParent parent = newColumnParent("student");//column family

/*

* 这里我们插入50万条数据到Student内

* 每条数据包括id和name

*/

String key_user_id = "a";

String k;

longtimestamp;

Column idColumn =null;

Column nameColumn=null;

//这个sampleData代表了每插入1W条记录到Cassandra集群的用时毫秒的数据样本

List sampleData = newArrayList(51);

for(inti =0;i <500000;i++)

{

k = key_user_id + i;//row key

timestamp = System.currentTimeMillis();//时间戳

//每行的第一个字段(id字段)

idColumn = newColumn(CassandraOperationUtil.stringToByteBuffer("id"));//字段名

idColumn.setValue(CassandraOperationUtil.stringToByteBuffer(i + ""));//字段值

idColumn.setTimestamp(timestamp);//时间戳

client.insert(

CassandraOperationUtil.stringToByteBuffer(k),

parent,

idColumn,

ConsistencyLevel.ONE);

//每行的第二个字段(name字段)

nameColumn = newColumn(CassandraOperationUtil.stringToByteBuffer("name"));

nameColumn.setValue(CassandraOperationUtil.stringToByteBuffer("student"+ i));

nameColumn.setTimestamp(timestamp);

client.insert(

CassandraOperationUtil.stringToByteBuffer(k),

parent,

nameColumn,

ConsistencyLevel.ONE);

//判断是否这是起始记录(用于标记起始时间戳)和第N 万条记录(第N万条记录的时间戳)

if( (i==0) || ( (i+1)%10000==0)){

sampleData.add((int)(timestamp));

}

}

//标记结束时间

longendTime = System.currentTimeMillis();

//标记一共用时

longelapsedTime = endTime-startTime;

System.out.println("压力测试完毕,用时: "+elapsedTime+" 毫秒");

//关闭连接

tr.close();

//压力测试结束后,我们把所有的样本数据写入文件中等待处理

FileWriter fw = newFileWriter(newFile("cassandra_insert_sample_data.txt"));

for(intj=0;j

fw.write(sampleData.get(j)+"\n");

}

fw.flush();

fw.close();

}

}

最终50W条记录插入完毕:控制台显示:

ea98d2a9d8c9b7cc4c8d392d0bcbb3c9.png

而且我们打开文本文件确定这些时间戳的样本都被记录了:

66d7e8c24822619c0fd49d69dc220791.png

当然了,因为Cassandra的存储是基于内存的,所以我们定义了一个工具类用于转换字符串和字节数组:

/*

*/

packagecom.charles.cassandra.util;

importjava.io.UnsupportedEncodingException;

importjava.nio.ByteBuffer;

/**

*

* Description: 这个类提供了一些Cassandra操作的工具类

*

* @author charles.wang

* @created May 19, 2012 11:18:27 AM

*

*/

publicclassCassandraOperationUtil {

/**

*因为在Cassandra中,信息都存在内存的,所以都是以ByteBuffer形式存储的,但是ByteBuffer对于人类来说没有String可读性强

*所以这个方法可以吧字符串转为ByteBuffer

*/

publicstaticByteBuffer stringToByteBuffer(String s)throwsUnsupportedEncodingException{

returnByteBuffer.wrap(s.getBytes("UTF-8"));

}

/**

*因为在Cassandra中,信息都存在内存的,所以都是以ByteBuffer形式存储的,但是ByteBuffer对于人类来说没有String可读性强

*所以对称的,这个方法吧ByteBuffer转为人类可读的字符串

*/

publicstaticString byteBufferToString (ByteBuffer b)throwsUnsupportedEncodingException{

//先构建一个字节数组

byte[] bytes =newbyte[b.remaining()];

//吧bytebuffer里面的内容全部存入字节数组

b.get(bytes);

//然后把这些bytes转为String

returnnewString(bytes,"UTF-8");

}

}

Oracle的采样:

我们这里依然用循环插入50W条记录,不同的是,在循环的开始和循环每10000条记录时,我们把时间戳记录在List中,最终把这个List写入文本文件(oracle_input_sample_data.txt):

/*

*/

packagecom.charles.cassandra.demo;

importjava.io.File;

importjava.io.FileWriter;

importjava.sql.Connection;

importjava.sql.Date;

importjava.sql.DriverManager;

importjava.sql.PreparedStatement;

importjava.sql.ResultSet;

importjava.sql.Statement;

importjava.sql.ResultSetMetaData;

importjava.sql.Timestamp;

importjava.util.ArrayList;

importjava.util.List;

/**

*

* Description:插入50W条记录到关系数据库Oracle中

*

* @author charles.wang

* @created May 19, 2012 5:25:36 PM

*

*/

publicclassOracleStressTest {

/**

* 既然要测负载,就尽可能减少方法调用的时间开销,所以我用了最原始的写法

* @param args

*/

publicstaticvoidmain(String[] args){

String url="jdbc:oracle:thin:@192.168.129.14:15210:ora11g";

String username="Charles_Stress_Test1";

String password="Charles_Stress_Test1";

String sDBDriver = "oracle.jdbc.driver.OracleDriver";

try{

System.out.println("开始压力测试,我们以预编译的方式插入50W条数据到Oracle中");

System.out.println("...");

//标记开始时间

longstartTime=System.currentTimeMillis();

Class.forName(sDBDriver).newInstance();

Connection conn = DriverManager.getConnection(url,username,password);

//因为这里使用预编译语句,所以不用每次都生成新的执行计划

String rowkey=null;

String id=null;

String name=null;

Date date=null;

String statementString="insert into Student (rowkey,id,name,create_date )values(?,?,?,?)";;

PreparedStatement pstmt = conn.prepareStatement(statementString);

//这个sampleData代表了每插入1W条记录到Oracle数据库用时毫秒的数据样本

List sampleData = newArrayList(51);

for(inti=0;i<500000;i++){

longtimestamp = System.currentTimeMillis();

rowkey="a"+i;

id=""+i;

name="student"+i;

date= newDate(timestamp);

pstmt.setString(1,rowkey);

pstmt.setString(2, id);

pstmt.setString(3,name);

pstmt.setDate(4, date);

pstmt.execute();

//判断是否这是起始记录(用于标记起始时间戳)和第N 万条记录(第N万条记录的时间戳)

if( (i==0) || ( (i+1)%10000==0)){

sampleData.add((int)(timestamp));

}

}

//关闭相关连接

pstmt.close();

conn.close();

longendTime=System.currentTimeMillis();

longelapsedTime=endTime-startTime;

System.out.println("压力测试完毕,用时: "+elapsedTime+" 毫秒");

//在压力测试结束之后,我们来把样本数据写入文本文件中

FileWriter fw = newFileWriter(newFile("oracle_insert_sample_data.txt"));

for(intj=0;j

fw.write(sampleData.get(j)+"\n");

}

fw.flush();

fw.close();

}catch(Exception e){

System.out.println("数据库连接失败");

e.printStackTrace();

}

}

}

最终50W条记录插入完毕:控制台显示:

e4d872111852c2622936085c66b22eef.png

而且我们打开文本文件确定这些时间戳的样本都被记录了:

b953b0ffedbd4ad59ebe99da0a400332.png

Part 2: 分析采样数据并且绘制比较图:

我们用JFreechart强大的图表制作能力来绘制比较图:

首先我们依然定义一个工具类 ParseDataUtil,它可以完成两件事情,一是从样本文件中读取数据,然后时间戳相减,最终把所有每1W条数据的耗时时间存入List对象,二是它可以吧List对象传递给JFreechart的数据模型:

/*

*/

packagecom.charles.parsedata.util;

importjava.io.BufferedReader;

importjava.io.File;

importjava.io.FileInputStream;

importjava.io.FileNotFoundException;

importjava.io.FileReader;

importjava.io.IOException;

importjava.io.InputStreamReader;

importjava.util.ArrayList;

importjava.util.List;

importorg.jfree.data.category.DefaultCategoryDataset;

/**

*

* Description:

*

* @author charles.wang

* @created May 21, 2012 8:45:28 AM

*

*/

publicclassParseDataUtil {

/**

* 这个方法用于添加指定的分析来的数据作为JFreechart显示的数据集

*

* @param ds

*            JFreechart的数据集对象

* @param datas

*            从压力测试采样并且经过加工后的数据

* @param seriesName

*            曲线的名称

*/

publicstaticvoidaddDataToDataset(DefaultCategoryDataset ds, List datas, String seriesName) {

// 对于数据集合的检查

if(datas.size() <=0)

return;

// type表示横轴的每个坐标点

Integer value = 0;

String type = null;

// 用循环依次添加

for(inti =1; i <= datas.size(); i++) {

// 获取每个样本数据中的横坐标纵坐标

type = i + "";

value = datas.get(i - 1);

ds.addValue(value, seriesName, type);

}

}

/**

* 这个方法用于从样本数据中构建最终传入到JFreechart绘制的数据

*

* @param fileName

* @param numOfRecords

* @return

*/

publicstaticList buildSampleDataListFromFile(String fileName,intnumOfRecords) {

// 判断参数

if(numOfRecords <=0)

returnnull;

try{

// 创建一个rawSampleData 作为采样数据的List

List rawSampleData = newArrayList(numOfRecords);

// 打开一个到指定文件的输入流

FileInputStream fis = newFileInputStream(fileName);

InputStreamReader isr = newInputStreamReader(fis);

BufferedReader br = newBufferedReader(isr);

if(br ==null) {

System.out.println("样本文件不存在!");

returnnull;

}

// 依次读入

for(inti =0; i 

String rawRecord = br.readLine();

rawSampleData.add(Integer.parseInt(rawRecord));

}

// 读完了关闭输入流

br.close();

isr.close();

fis.close();

// 现在我们把rawSampleData转为真正可以被JFreeChart显示的SampleData

// 这里SampleData的每个数据都是用时,所以是当前时间戳-第一条记录的时间戳

intsampleDataSize = rawSampleData.size() -1;

List sampleData = newArrayList(sampleDataSize);

// 设置起始时间戳,以后每一个时间戳都要减去这个起始时间戳

Integer baseTimeStamp = rawSampleData.get(0);

// System.out.println("baseTimeStamp: "+baseTimeStamp);

// System.out.println("sampleDataSize: "+sampleData.size());

// System.out.println("hello");

for(intj =0; j 

inttime = rawSampleData.get(j +1) - baseTimeStamp;

System.out.println(time);

sampleData.add(time);

}

returnsampleData;

} catch(Exception ex) {

ex.printStackTrace();

returnnull;

}

}

}

然后我们有个最终执行画图的类,这个类吧从原始数据分析后的数据显示在图表上,并且作为对比,吧Oracle和Cassandra集群的数据显示在同一张表上:

/*

*/

packagecom.charles.parsedata;

importjava.util.ArrayList;

importjava.util.List;

importjavax.swing.JPanel;

importorg.jfree.chart.ChartFactory;

importorg.jfree.chart.ChartPanel;

importorg.jfree.chart.JFreeChart;

importorg.jfree.chart.axis.NumberAxis;

importorg.jfree.chart.plot.CategoryPlot;

importorg.jfree.chart.plot.PlotOrientation;

importorg.jfree.data.category.DefaultCategoryDataset;

importorg.jfree.ui.ApplicationFrame;

importorg.jfree.ui.RefineryUtilities;

importcom.charles.parsedata.util.ParseDataUtil;

/**

*

* Description: 用JFreechart来分析插入数据

*

* @author charles.wang

* @created May 21, 2012 8:38:27 AM

*

*/

publicclassInsertDataStressTestDataParserextendsApplicationFrame{

publicInsertDataStressTestDataParser(String s) {

super(s);

setContentPane(createDemoLine());

}

publicstaticvoidmain(String[] args)  {

InsertDataStressTestDataParser fjc = newInsertDataStressTestDataParser("Cassandra&Oracle插入数据对比图");

fjc.pack();

RefineryUtilities.centerFrameOnScreen(fjc);

fjc.setVisible(true);

}

// 生成显示图表的面板

publicstaticJPanel createDemoLine(){

JFreeChart jfreechart = createChart(createDataset());

returnnewChartPanel(jfreechart);

}

// 生成图表主对象JFreeChart

publicstaticJFreeChart createChart(DefaultCategoryDataset linedataset) {

//定义图表对象

JFreeChart chart = ChartFactory.createLineChart("Cassandra和Oracle插入数据对比图",// chart title

"记录数(单位:万条)",// 横轴标签

"用时(毫秒)",// 纵轴标签

linedataset, // 传入的数据集

PlotOrientation.VERTICAL, // 方向

true,// bool变量表示是否要加入图例(legend)

true,// 工具集

false// 是否添加url

);

CategoryPlot plot = chart.getCategoryPlot();

// 范围轴线

NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();

rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());

rangeAxis.setAutoRangeIncludesZero(true);

rangeAxis.setUpperMargin(0.20);

rangeAxis.setLabelAngle(Math.PI / 2.0);

returnchart;

}

//生成数据

publicstaticDefaultCategoryDataset createDataset() {

DefaultCategoryDataset ds = newDefaultCategoryDataset();

List data1 = ParseDataUtil.buildSampleDataListFromFile("cassandra_insert_sample_data.txt",51);

List data2 = ParseDataUtil.buildSampleDataListFromFile("oracle_insert_sample_data.txt",51);

ParseDataUtil.addDataToDataset(ds, data1, "Cassandra插入数据所用时间分布图");

ParseDataUtil.addDataToDataset(ds, data2, "Oracle插入数据所用时间分布图");

returnds;

}

}

最终对比图如下:

c5d865316fb69a34db37c9dfe9c33645.png

结论:

所以我们这里很清楚的看到:

(1) 无论是Cassandra集群还是Oracle,其插入操作用时都是线性的,也就是它的平均插入速率基本是恒速。

(2) 在低配置服务器上,Cassandra集群的插入数据操作耗时要高于Oracle关系数据库。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值