用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):
- package com.charles.cassandra.demo;
- import java.io.File;
- import java.io.FileWriter;
- import java.util.ArrayList;
- import java.util.List;
- import org.apache.cassandra.thrift.Cassandra;
- import org.apache.cassandra.thrift.Column;
- import org.apache.cassandra.thrift.ColumnParent;
- import org.apache.cassandra.thrift.ConsistencyLevel;
- import org.apache.cassandra.thrift.TBinaryProtocol;
- import org.apache.thrift.protocol.TProtocol;
- import org.apache.thrift.transport.TFramedTransport;
- import org.apache.thrift.transport.TSocket;
- import org.apache.thrift.transport.TTransport;
- import com.charles.cassandra.util.CassandraOperationUtil;
- public class CassandraClusterStressTest
- {
- public static void main(String[] args)
- throws Exception
- {
- //包装好的socket
- TTransport tr = new TFramedTransport(new TSocket("192.168.129.34",9160));
- TProtocol proto = new TBinaryProtocol(tr);
- Cassandra.Client client = new Cassandra.Client(proto);
- tr.open();
- if(!tr.isOpen())
- {
- System.out.println("无法连接到服务器!");
- return;
- }
- System.out.println("开始压力测试,我们插入50W条数据到2节点集群中");
- System.out.println("...");
- //标记开始时间
- long startTime = System.currentTimeMillis();
- client.set_keyspace("Charles_Stress_Test2");//使用Charles_Stress_Test keyspace
- ColumnParent parent = new ColumnParent("student");//column family
- /*
- * 这里我们插入50万条数据到Student内
- * 每条数据包括id和name
- */
- String key_user_id = "a";
- String k;
- long timestamp;
- Column idColumn =null;
- Column nameColumn=null;
- //这个sampleData代表了每插入1W条记录到Cassandra集群的用时毫秒的数据样本
- List<Integer> sampleData = new ArrayList<Integer>(51);
- for(int i = 0;i < 500000;i++)
- {
- k = key_user_id + i;//row key
- timestamp = System.currentTimeMillis();//时间戳
- //每行的第一个字段(id字段)
- idColumn = new Column(CassandraOperationUtil.stringToByteBuffer("id"));//字段名
- idColumn.setValue(CassandraOperationUtil.stringToByteBuffer(i + ""));//字段值
- idColumn.setTimestamp(timestamp);//时间戳
- client.insert(
- CassandraOperationUtil.stringToByteBuffer(k),
- parent,
- idColumn,
- ConsistencyLevel.ONE);
- //每行的第二个字段(name字段)
- nameColumn = new Column(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));
- }
- }
- //标记结束时间
- long endTime = System.currentTimeMillis();
- //标记一共用时
- long elapsedTime = endTime-startTime;
- System.out.println("压力测试完毕,用时: "+elapsedTime+" 毫秒");
- //关闭连接
- tr.close();
- //压力测试结束后,我们把所有的样本数据写入文件中等待处理
- FileWriter fw = new FileWriter(new File("cassandra_insert_sample_data.txt"));
- for(int j=0;j<sampleData.size();j++){
- fw.write(sampleData.get(j)+"\n");
- }
- fw.flush();
- fw.close();
- }
- }
最终50W条记录插入完毕:控制台显示:
而且我们打开文本文件确定这些时间戳的样本都被记录了:
当然了,因为Cassandra的存储是基于内存的,所以我们定义了一个工具类用于转换字符串和字节数组:
- /*
- */
- package com.charles.cassandra.util;
- import java.io.UnsupportedEncodingException;
- import java.nio.ByteBuffer;
- /**
- *
- * Description: 这个类提供了一些Cassandra操作的工具类
- *
- * @author charles.wang
- * @created May 19, 2012 11:18:27 AM
- *
- */
- public class CassandraOperationUtil {
- /**
- *因为在Cassandra中,信息都存在内存的,所以都是以ByteBuffer形式存储的,但是ByteBuffer对于人类来说没有String可读性强
- *所以这个方法可以吧字符串转为ByteBuffer
- */
- public static ByteBuffer stringToByteBuffer(String s) throws UnsupportedEncodingException{
- return ByteBuffer.wrap(s.getBytes("UTF-8"));
- }
- /**
- *因为在Cassandra中,信息都存在内存的,所以都是以ByteBuffer形式存储的,但是ByteBuffer对于人类来说没有String可读性强
- *所以对称的,这个方法吧ByteBuffer转为人类可读的字符串
- */
- public static String byteBufferToString (ByteBuffer b) throws UnsupportedEncodingException{
- //先构建一个字节数组
- byte[] bytes = new byte[b.remaining()];
- //吧bytebuffer里面的内容全部存入字节数组
- b.get(bytes);
- //然后把这些bytes转为String
- return new String(bytes,"UTF-8");
- }
- }
Oracle的采样:
我们这里依然用循环插入50W条记录,不同的是,在循环的开始和循环每10000条记录时,我们把时间戳记录在List中,最终把这个List写入文本文件(oracle_input_sample_data.txt):
- /*
- */
- package com.charles.cassandra.demo;
- import java.io.File;
- import java.io.FileWriter;
- import java.sql.Connection;
- import java.sql.Date;
- import java.sql.DriverManager;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.Statement;
- import java.sql.ResultSetMetaData;
- import java.sql.Timestamp;
- import java.util.ArrayList;
- import java.util.List;
- /**
- *
- * Description:插入50W条记录到关系数据库Oracle中
- *
- * @author charles.wang
- * @created May 19, 2012 5:25:36 PM
- *
- */
- public class OracleStressTest {
- /**
- * 既然要测负载,就尽可能减少方法调用的时间开销,所以我用了最原始的写法
- * @param args
- */
- public static void main(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("...");
- //标记开始时间
- long startTime=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<Integer> sampleData = new ArrayList<Integer>(51);
- for(int i=0;i<500000;i++){
- long timestamp = System.currentTimeMillis();
- rowkey="a"+i;
- id=""+i;
- name="student"+i;
- date= new Date(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();
- long endTime=System.currentTimeMillis();
- long elapsedTime=endTime-startTime;
- System.out.println("压力测试完毕,用时: "+elapsedTime+" 毫秒");
- //在压力测试结束之后,我们来把样本数据写入文本文件中
- FileWriter fw = new FileWriter(new File("oracle_insert_sample_data.txt"));
- for(int j=0;j<sampleData.size();j++){
- fw.write(sampleData.get(j)+"\n");
- }
- fw.flush();
- fw.close();
- }catch(Exception e){
- System.out.println("数据库连接失败");
- e.printStackTrace();
- }
- }
- }
最终50W条记录插入完毕:控制台显示:
而且我们打开文本文件确定这些时间戳的样本都被记录了:
Part 2: 分析采样数据并且绘制比较图:
我们用JFreechart强大的图表制作能力来绘制比较图:
首先我们依然定义一个工具类 ParseDataUtil,它可以完成两件事情,一是从样本文件中读取数据,然后时间戳相减,最终把所有每1W条数据的耗时时间存入List<Integer>对象,二是它可以吧List<Integer>对象传递给JFreechart的数据模型:
- /*
- */
- package com.charles.parsedata.util;
- import java.io.BufferedReader;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.FileReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.util.ArrayList;
- import java.util.List;
- import org.jfree.data.category.DefaultCategoryDataset;
- /**
- *
- * Description:
- *
- * @author charles.wang
- * @created May 21, 2012 8:45:28 AM
- *
- */
- public class ParseDataUtil {
- /**
- * 这个方法用于添加指定的分析来的数据作为JFreechart显示的数据集
- *
- * @param ds
- * JFreechart的数据集对象
- * @param datas
- * 从压力测试采样并且经过加工后的数据
- * @param seriesName
- * 曲线的名称
- */
- public static void addDataToDataset(DefaultCategoryDataset ds, List<Integer> datas, String seriesName) {
- // 对于数据集合的检查
- if (datas.size() <= 0)
- return;
- // type表示横轴的每个坐标点
- Integer value = 0;
- String type = null;
- // 用循环依次添加
- for (int i = 1; i <= datas.size(); i++) {
- // 获取每个样本数据中的横坐标纵坐标
- type = i + "";
- value = datas.get(i - 1);
- ds.addValue(value, seriesName, type);
- }
- }
- /**
- * 这个方法用于从样本数据中构建最终传入到JFreechart绘制的数据
- *
- * @param fileName
- * @param numOfRecords
- * @return
- */
- public static List<Integer> buildSampleDataListFromFile(String fileName, int numOfRecords) {
- // 判断参数
- if (numOfRecords <= 0)
- return null;
- try {
- // 创建一个rawSampleData 作为采样数据的List
- List<Integer> rawSampleData = new ArrayList<Integer>(numOfRecords);
- // 打开一个到指定文件的输入流
- FileInputStream fis = new FileInputStream(fileName);
- InputStreamReader isr = new InputStreamReader(fis);
- BufferedReader br = new BufferedReader(isr);
- if (br == null) {
- System.out.println("样本文件不存在!");
- return null;
- }
- // 依次读入
- for (int i = 0; i < numOfRecords; i++) {
- String rawRecord = br.readLine();
- rawSampleData.add(Integer.parseInt(rawRecord));
- }
- // 读完了关闭输入流
- br.close();
- isr.close();
- fis.close();
- // 现在我们把rawSampleData转为真正可以被JFreeChart显示的SampleData
- // 这里SampleData的每个数据都是用时,所以是当前时间戳-第一条记录的时间戳
- int sampleDataSize = rawSampleData.size() - 1;
- List<Integer> sampleData = new ArrayList<Integer>(sampleDataSize);
- // 设置起始时间戳,以后每一个时间戳都要减去这个起始时间戳
- Integer baseTimeStamp = rawSampleData.get(0);
- // System.out.println("baseTimeStamp: "+baseTimeStamp);
- // System.out.println("sampleDataSize: "+sampleData.size());
- // System.out.println("hello");
- for (int j = 0; j < sampleDataSize; j++) {
- int time = rawSampleData.get(j + 1) - baseTimeStamp;
- System.out.println(time);
- sampleData.add(time);
- }
- return sampleData;
- } catch (Exception ex) {
- ex.printStackTrace();
- return null;
- }
- }
- }
然后我们有个最终执行画图的类,这个类吧从原始数据分析后的数据显示在图表上,并且作为对比,吧Oracle和Cassandra集群的数据显示在同一张表上:
- /*
- */
- package com.charles.parsedata;
- import java.util.ArrayList;
- import java.util.List;
- import javax.swing.JPanel;
- import org.jfree.chart.ChartFactory;
- import org.jfree.chart.ChartPanel;
- import org.jfree.chart.JFreeChart;
- import org.jfree.chart.axis.NumberAxis;
- import org.jfree.chart.plot.CategoryPlot;
- import org.jfree.chart.plot.PlotOrientation;
- import org.jfree.data.category.DefaultCategoryDataset;
- import org.jfree.ui.ApplicationFrame;
- import org.jfree.ui.RefineryUtilities;
- import com.charles.parsedata.util.ParseDataUtil;
- /**
- *
- * Description: 用JFreechart来分析插入数据
- *
- * @author charles.wang
- * @created May 21, 2012 8:38:27 AM
- *
- */
- public class InsertDataStressTestDataParser extends ApplicationFrame{
- public InsertDataStressTestDataParser(String s) {
- super(s);
- setContentPane(createDemoLine());
- }
- public static void main(String[] args) {
- InsertDataStressTestDataParser fjc = new InsertDataStressTestDataParser("Cassandra&Oracle插入数据对比图");
- fjc.pack();
- RefineryUtilities.centerFrameOnScreen(fjc);
- fjc.setVisible(true);
- }
- // 生成显示图表的面板
- public static JPanel createDemoLine(){
- JFreeChart jfreechart = createChart(createDataset());
- return new ChartPanel(jfreechart);
- }
- // 生成图表主对象JFreeChart
- public static JFreeChart 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);
- return chart;
- }
- //生成数据
- public static DefaultCategoryDataset createDataset() {
- DefaultCategoryDataset ds = new DefaultCategoryDataset();
- List<Integer> data1 = ParseDataUtil.buildSampleDataListFromFile("cassandra_insert_sample_data.txt",51);
- List<Integer> data2 = ParseDataUtil.buildSampleDataListFromFile("oracle_insert_sample_data.txt",51);
- ParseDataUtil.addDataToDataset(ds, data1, "Cassandra插入数据所用时间分布图");
- ParseDataUtil.addDataToDataset(ds, data2, "Oracle插入数据所用时间分布图");
- return ds;
- }
- }
最终对比图如下:
结论:
所以我们这里很清楚的看到:
(1) 无论是Cassandra集群还是Oracle,其插入操作用时都是线性的,也就是它的平均插入速率基本是恒速。
(2) 在低配置服务器上,Cassandra集群的插入数据操作耗时要高于Oracle关系数据库。
用JFreeChart 来分析Cassandra/Oracle插入海量数据的性能相关推荐
- Oracle数据库:创建和删除视图view,简单和复杂视图,内建视图,topN分析,oracle分页查询
Oracle数据库:创建和删除视图view,简单和复杂视图,内建视图,topN分析,oracle分页查询 2022找工作是学历.能力和运气的超强结合体,遇到寒冬,大厂不招人,可能很多算法学生都得去找开 ...
- leveldb源码分析:数据插入续(跳表)
leveldb数据的插入-跳表 本文主要是接着上一篇文章,继续深入探索Write函数调用插入之后的流程. status = WriteBatchInternal::InsertInto(updates ...
- oracle 数据有引号,oracle插入字符串数据时字符串中有'单引号问题
使用insert into(field1,field2...) values('val1','val2'...)时,若值中有单引号时会报错. 处理方法:判断一下val1,val2中是否含有单引号,若含 ...
- 通俗易懂地解决中文乱码问题(2) --- 分析解决Mysql插入移动端表情符报错 ‘incorrect string value: '\xF0......
原文:[原创]通俗易懂地解决中文乱码问题(2) --- 分析解决Mysql插入移动端表情符报错 'incorrect string value: '\xF0... 这篇blog重点在解决问题,如果你对 ...
- mysql插入日期 vs oracle插入日期
今天做oracle日期插入的时候突然开始疑惑日期是如何插入的. 用框架久了,反而不自己做简单的工作了.比如插入. 通常,新建一个表对象,然后绑定数据,前端form提交,后端getModel后直接mod ...
- oracle 插入含字符串
1.创建表 SQL> create table t(id number,name varchar2(20)); 表已创建. 2.常规方式插入 SQL> insert into t valu ...
- oracle 插入一个语句,oracle如何通过1条语句插入多个值 oracle通过1条语句插入多个值方法...
本篇文章小编给大家分享一下oracle通过1条语句插入多个值方法,小编觉得挺不错的,现在分享给大家供大家参考,有需要的小伙伴们可以来看看. 在实践过程中遇到一个问题, 我想往数据库插入多条数据时,使用 ...
- oracle表分析都分析什么,oracle表分析
analyze table tablename compute statistics; analyze index indexname compute statistics; 对于使用CBO很有好处, ...
- Oracle插入时间
现象:Oracle 插入时间时 ,报错:ORA-01861: 文字与格式字符串不匹配 解决方法: 这个错误一般出现在时间字段上,即你插入的时间格式和数据库现有的时间格式不一致,解决的方法是格式化你 插 ...
- oracle插入数字类型能用单引号括起来为什么
oracle插入数字类型能用单引号括起来为什么 2014-02-24 18:30匿名 | 浏览 631 次 create table emp(id number,name varchar2(20)); ...
最新文章
- CC++刚開始学习的人编程教程(9) Windows8.1安装VS2013并捆绑QT与编程助手
- POJ - 2400 Supervisor, Supervisee(KM+打印方案)
- POJ 3608 Bridge Across Islands 《挑战程序设计竞赛》
- 刷题刷题 ——网易CPP
- 使用OStressSQL Server压力测试
- C# CRC16 modbus
- Oracle EBS-SQL (PO-10):检查过期采购未接收订单.sql
- Volley,OkHttp,Retrofit网络请求及封装
- 最强内网穿透工具frp
- HCL配置不同VLAN之间进行通讯实验
- IPO并不遥远,飞哥IPERi模型助你打开互联网创业创新成功密码
- Linux系统连接校园网指南(JLU)
- JSPlumb文档翻译
- C++程序方法 --- 病毒感染检测
- 微信开发模式群发消息接收的是乱码
- 冰河远程控制软件使用
- 演示如何使用偏最小二乘回归方法
- 微软中间语言—MSIL(转载)
- 数据库sql语句(经典)
- 为什么要在项目中使用缓存呢?
热门文章
- oracle如何修改initial参数,oracle初始化参数设置
- java静态分页_Javaweb分页
- mysql 二进制日志格式_MySQL 二进制日志格式深入理解
- pandas基础知识---4
- perl判断变量是数值_Perl学习12之defined undef使用
- Java对象映射XML文件
- (1)、win10 本地 安装 rabbitmq
- 六石管理学:切勿通过扯皮折腾别人,一句你不要管了即可
- 发现新的预言梦种类:预言梦投射
- 编译OpenJDK12:freetypeScaler.obj error LINK2019 无法解析的外部符号