用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
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;
public class CassandraOperationUtil {
public static ByteBuffer stringToByteBuffer(String s) throws UnsupportedEncodingException{
return ByteBuffer.wrap(s.getBytes("UTF-8"));
}
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;
public class OracleStressTest {
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;
public class ParseDataUtil {
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);
}
}
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;
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關聯式資料庫。
©著作權歸作者所有:來自51CTO部落格作者charles_wang888的原創作品,如需轉載,請註明出處,否則將追究法律責任
OracleJFreechart壓力測試NoSQL
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/506/viewspace-2821247/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- JFreeChart
- JFreeChart教程
- Jfreechart資金曲線圖
- Oracle JDK商用費用分析OracleJDK
- oracle和mybatis整合,批次插入OracleMyBatis
- JFreeChart圖表製作例項
- 全面分析插入排序的三種插入方式排序
- Oracle批量插入資料insert all into用法Oracle
- MYSQL之插入極限分析MySql
- 用資料說話,億級海量資料分析效能瓶頸如何破?
- 如何將Apache Druid,Flink和Cassandra用於實時流分析和使用者評分?ApacheUI
- HBase 與 Cassandra 架構對比分析的經驗分享架構
- oracle-資料庫- insert 插入語句Oracle資料庫
- B站基於ClickHouse的海量使用者行為分析應用實踐
- 海量時序資料分析預測難?來試試 TDengine+Seeq 這一組合
- Cassandra 概況
- Cassandra Vnodes在Cassandra 2.0-4.0中的演進
- ORACLE資料庫中如何插入生僻字Oracle資料庫
- TiDB 在連鎖快餐企業丨海量交易與實時分析的應用探索TiDB
- 活動精彩實錄 | 王峰:Cassandra在360的多場景應用及未來趨勢
- Cassandra 資料模型模型
- 理解cassandra架構架構
- 資料來源管理 | 分散式NoSQL系統,Cassandra叢集管理分散式SQL
- Oracle insert all一次插入多個表中Oracle
- 50億海量資料如何高效儲存和分析?
- Oracle分析函式之LEAD和LAG實際應用Oracle函式
- 用JavaScript實現插入排序JavaScript排序
- Java通過Mybatis實現批量插入資料到Oracle中JavaMyBatisOracle
- Centos下安裝cassandraCentOS
- Cassandra的Session會話Session會話
- Cassandra與Kafka的整合Kafka
- 準備應用導數來分析函式函式
- 11 應用程式突報Oracle TNS-12514典型案例分析Oracle
- mybatis+oracle 批次插入多條資料的處理方法MyBatisOracle
- SeaTunnel用於海量資料的同步和轉換
- 新一代海量資料搜尋引擎 TurboSearch 來了!
- 海量資料分析更快、更穩、更準。GaussDB(for MySQL) HTAP只讀分析特性詳解MySql
- Apache Cassandra 的 Spring 資料ApacheSpring