数据库选型之内存数据库eXtremeDB
刘勇 Email:lyssym@sina.com
简介
鉴于内存数据库访问速率快的特点,本文分别从单线程、多线程(并发访问)和多线程读/写混合访问角度对eXtremeDB数据库读写速率展开测试。需要指出的是,本文读取操作包含将数据读取后,并在控制台显示出来。测试结果表明:eXtremeDB在单一读/写访问时,速率大约在10w条/s,其速率是比较快的;同时相对单线程来说,多线程读或者写操作并发访问eXtremeDB,也并未衰减其性能,因此在一定程度上可以满足并发访问需求;另一方面,多线程读/写混合访问eXtremeDB时,单个线程写入速率大约在10w条/s,单个线程读取速率大约在4w条/s,此外,随着读/写线程个数的增加,其读写速率在整体上趋于稳定。经过上述测试,该数据库适合于嵌入式系统设计,对于有存储需求的实时系统来说,可以采用内存与硬盘混合方式 ,但是该策略必然会衰减其性能。
测试环境
硬件环境:
Localhost:CPU:Intel Core I5;主频:3.10G;内存:4G
软件环境:
Localhost: extremedb_6.0_im_win32vs2012_x86_sql_eval.EXE;jdk 1.8
表结构:
DROP TABLE IF EXISTS `TAQ`;
CREATE TABLE `TAQ` (
`SECCODE` varchar(6) NOT NULL,
`SECNAME` varchar(20) NOT NULL,
`TDATE` varchar(10) NOT NULL,
`TTIME` varchar(6) NOT NULL,
`LASTCLOSE` decimal(19,3) DEFAULT NULL,
`OP` decimal(19,3) DEFAULT NULL,
`CP` decimal(19,3) DEFAULT NULL,
`TQ` decimal(19,3) DEFAULT NULL,
`TM` decimal(19,3) DEFAULT NULL,
`CQ` decimal(18,0) DEFAULT NULL,
`CM` decimal(19,3) DEFAULT NULL,
`CT` decimal(19,3) DEFAULT NULL,
`BS` varchar(18) DEFAULT NULL,
`BSRATIO` decimal(19,3) DEFAULT NULL,
`SPD` decimal(19,3) DEFAULT NULL,
`RPD` decimal(19,3) DEFAULT NULL,
`UNIX` bigint(20) DEFAULT NULL,
`MARKET` varchar(4) DEFAULT NULL,
KEY `SECCODE` (`SECCODE`,`TDATE`,`TTIME`)
) /*!50100 TABLESPACE ts_cloudstore STORAGE DISK */ ENGINE=ndbcluster DEFAULT CHARSET=utf8;
Table TAQ
性能测试
本文先从单线程和多线程(并发访问)和2个角度,以60K、100K和600K条为基础数据总量,对eXtremeDB内存数据库展开测试。其中单线程部分,以单条测试和批处理2个方面展开测试,并进行对比;多线程部分则主要以不同线程个数对eXtremeDB进行测试。需要指出的是,本文读取操作包含将数据读取后,并在控制台显示出来,而写入数据数据部分,则以数据写入数据为结束节点。然后从多线程读/写混合访问eXtremeDB角度,以100K和600K条数据为基础数据库,针对不同读取线程和写入线程个数展开测试。
单线程访问
以60K、100K和600K条为基础数据总量,在单条和批处理下对I/O进行测试,其中单条下测试结果如表-1所示。
批处理以批处理数据量为1000,2000和3000条角度展开测试,主要针对批处理写入速率,由于本文读取操作涉及从数据库读取数据并在控制台显示出来,其处理瓶颈主要集中于数据显示部分,因此本部分读取速率暂不考虑,其测试结果如表-2所示。
小结
从表-1和表-2可知:1)从写入速率角度来看,批处理相对单条处理而言,并没有优势,主要原因在于,eXtremeDB为内存数据库,批处理在内存中还多了一份批量累积过程;2)从整体而言, 相对之前对MySQL(即使是在固态硬盘本地,详细内容见以前测试报告),eXtremeDB的读写速率还是比较快的,测试速率大约为10w条/s。
多线程访问
以60K、100K和600K条为基础数据总量,在不同线程个数下,对eXtremeDB展开读写访问,其中在不同线程个数下的写入速率如表-3所示。需要指出的是,此处多线程写入测试,以多线程处理平摊的方式,即写入60K条数据,采用20个线程执行,则每个线程处理3K(60K/20)条数据,详细内容见测试源代码,在此不再赘述。
在不同线程下的读取速率如表-4所示。需要指出的是,此处多线程读取速率,每个线程则完整读取给定的数据总量,即若数据总量为60K条,则每个线程读取60K数据,获取全表中指定的2个字段,并显示出来。
小结
从表-3和表-4可知:1)随着数据总量的增加,多线程访问速率整体上变化不明显,即线程读写速率相对比较稳定;2)与表-1相比较,针对多线程,若以平均速率乘以线程个数角度来说,则多线程与单线程在读取数据并显示出来上,两者速率差不多,此外,多线程写入和单线程写入的速率也差不多(注意多线程写入是平摊方式,两者可以直接比较),因此,多线程访问eXtremeDB,相对单线程并未衰减其性能。
总结
从上述2种场景测试结果来看,eXtremeDB的读写速率大约在10w条/s,相对一般的数据库(非内存数据库,即使在固态硬盘上)其速率也是比较快的。此外,针对多线程单一读/写并发访问,与单线程相比,也并未衰减其读写性能,因此对于并发访问来说,也能满足一定的需求。
测试源程序:
import com.mcobject.extremedb.*;
import java.sql.Date;
import java.math.BigDecimal; @Persistent // class will be stored in eXtremeDB database
class TestTable {
@Indexable(unique=true)
public String secCode; public String secName;
public Date tDate;
public Date tTime; public BigDecimal lastClose;
public BigDecimal OP;
public BigDecimal CP;
public BigDecimal TQ;
public BigDecimal TM;
public BigDecimal CQ;
public BigDecimal CM;
public BigDecimal CT;
public String BS; public BigDecimal BSRATIO;
public BigDecimal SPD;
public BigDecimal RPD;
public long UNIX;
public String market;
}
Class TestTable
import com.mcobject.extremedb.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.*; public class DataAccess {
public static int PAGE_SIZE = 128;
public static int DATABASE_SIZE = 512*1024*1024;
public static int MAXCONN = 100;
public static int NUM = 100*1000;
public static int BATCH = 1000;
private Database db;
private String sql;
private SqlLocalConnection con; public void initDatabase()
{
int config = Database.MCO_CFG_SQL_SUPPORT;
Database.Parameters params = new Database.Parameters();
params.memPageSize = PAGE_SIZE;
params.classes = new Class[] {TestTable.class};
params.maxConnections = MAXCONN; Database.Device device[] = new Database.Device[1];
device[0] = new Database.PrivateMemoryDevice(Database.Device.Kind.Data, DATABASE_SIZE);
db = new Database(config);
db.open("DataAccess", params, device);
sql = "select secCode, secName from TestTable";
con = db.connectSql();
} public void insertTransaction(TestTable table)
{
int code = 100000;
long start = getRunTime();
for (int i = 0; i < NUM; i++)
{
con.startTransaction(Database.TransactionType.ReadWrite);
table.secCode = Integer.toString(code);
table.secName ="中国银行";
table.tDate = new Date(System.currentTimeMillis());
table.tTime = new Date(System.currentTimeMillis()+5);
table.UNIX += 1;
con.insert(table);
con.commitTransaction();
code++ ;
}
long end = getRunTime();
System.out.println("写入速率为: " + NUM*1000/(end-start));
con.saveSnapshot("db.img");
} public long getRunTime()
{
return System.currentTimeMillis();
} public void getTransaction()
{
long start = getRunTime();
con.startTransaction(Database.TransactionType.ReadWrite);
SqlResultSet result = con.executeQuery(sql);
for (String column : result.getColumnNames()) {
System.out.print(column + " ");
}
System.out.println(); for (SqlTuple tuple : result) {
System.out.println(tuple.get("secCode") + " " + tuple.get("secName") + " ");
}
con.commitTransaction(); long end = getRunTime();
System.out.println("读取速率为: " + NUM*1000/(end-start));
} public void closeDatabase()
{
con.disconnect();
db.close();
} public void deleteData()
{
con.startTransaction(Database.TransactionType.ReadWrite);
con.removeAll(TestTable.class);
con.commitTransaction();
System.out.println("delete all the data!");
} public static void main(String[] args) {
// TODO Auto-generated method stub
TestTable table = new TestTable();
table.secCode = null;
table.secName = null;
table.tDate = null;
table.tTime = null; table.lastClose = new BigDecimal(15.857).setScale(3, RoundingMode.HALF_UP);
table.OP = new BigDecimal(10.132).setScale(3, RoundingMode.HALF_UP);
table.CP = new BigDecimal(12.310).setScale(3, RoundingMode.HALF_UP);
table.TQ = new BigDecimal(14.185).setScale(3, RoundingMode.HALF_UP);
table.TM = new BigDecimal(19.107).setScale(3, RoundingMode.HALF_UP);
table.CQ = new BigDecimal(8.457).setScale(3, RoundingMode.HALF_UP);
table.CM = new BigDecimal(7.859).setScale(3, RoundingMode.HALF_UP);
table.CT = new BigDecimal(13.101).setScale(3, RoundingMode.HALF_UP);
table.BS = null; table.BSRATIO = new BigDecimal(18.525).setScale(3, RoundingMode.HALF_UP);
table.SPD = new BigDecimal(6.108).setScale(3, RoundingMode.HALF_UP);
table.RPD = new BigDecimal(3.199).setScale(3, RoundingMode.HALF_UP);
table.UNIX = System.currentTimeMillis();
table.market = "SSE"; DataAccess da = new DataAccess();
da.initDatabase();
da.insertTransaction(table);
// da.getTransaction();
// da.deleteData();
da.closeDatabase();
} }
Class DataAccess
并发读/写混合测试
本文先构建100K和600K条的基础数据量,以避免读取线程初始读取数据失败情形。然后在该基础数据量的基础上,写入数据总量为100K条的数据,其中写入操作采用平摊的方式,即写入的总数据量平摊至每个写入线程中,而读取操作则保持在每个线程读取100K条的数据,详细内容见测试源代码。
以下从基础数据量为100K和600K条2个方面对eXtremeDB展开测试。
100K
从不同线程个数角度对该数据库展开测试,其中读/写线程各占50%,即若表格中线程个数为10个时,读/写线程各占5个,后续表格内容与之类同,不再赘述了。写入操作以写入总量100K条数据,并平摊给每个写入线程,读取操作中每个读取线程执行 "select secCode, secName from TestTable limit 100000"操作。基础数据量为100K条的测试结果见表-5。
600K
写入操作以写入总量100K条数据,并平摊给每个写入线程,读取操作中每个读取线程执行 "select secCode, secName from TestTable limit 100000, 100000"操作。基础数据量为600K条下的测试结果见表-6。
总结
从表-5和表-6可知:1)多线程读写混合访问eXtremeDB的单个线程写入速率大约在10w条/s, 单个线程读取速率大约在4w条/s;2)随着读取线程和写入线程的增加,eXtremeDB的读写速率变化不大,整体上比较稳定。
并发读/写测试源代码:
import com.mcobject.extremedb.*;
import java.sql.Date;
import java.math.BigDecimal; @Persistent // class will be stored in eXtremeDB database
class TestTable {
@Indexable(unique=true)
public String secCode; public String secName;
public Date tDate;
public Date tTime; public BigDecimal lastClose;
public BigDecimal OP;
public BigDecimal CP;
public BigDecimal TQ;
public BigDecimal TM;
public BigDecimal CQ;
public BigDecimal CM;
public BigDecimal CT;
public String BS; public BigDecimal BSRATIO;
public BigDecimal SPD;
public BigDecimal RPD;
public long UNIX;
public String market;
}
Class TestTable
import java.sql.Date;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import com.mcobject.extremedb.Database;
import com.mcobject.extremedb.SqlLocalConnection; public class WriteThread extends Thread {
private int code;
private Database db;
private int id;
private SqlLocalConnection con;
private TestTable table; public WriteThread(Database db, int code, TestTable table, int id)
{
this.db = db;
this.code = code;
this.table = table;
this.id = id;
con = db.connectSql();
con.startTransaction(Database.TransactionType.ReadWrite);
} public void run()
{
insertTransaction();
closeConnection();
} public void insertTransaction()
{
long start = getRunTime();
for (int i = 0; i < MultThreadAccess.NUM*2/MultThreadAccess.THREAD; i++)
{
con.startTransaction(Database.TransactionType.ReadWrite);
table.secCode = Integer.toString(code);
table.secName = "中国银行";
table.tDate = new Date(System.currentTimeMillis());
table.tTime = new Date(System.currentTimeMillis()+5);
table.UNIX += 1;
con.insert(table);
con.commitTransaction();
code++ ;
}
long end = getRunTime(); try {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(id%5+5 + ".txt", true)));
String str = Long.toString(MultThreadAccess.NUM*2/MultThreadAccess.THREAD*1000/(end-start));
bw.write(str);
bw.write("\n");
bw.flush();
bw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} public long getRunTime()
{
return System.currentTimeMillis();
} public void closeConnection()
{
con.disconnect();
} }
Class WriteThread
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import com.mcobject.extremedb.Database;
import com.mcobject.extremedb.SqlLocalConnection;
import com.mcobject.extremedb.SqlResultSet;
import com.mcobject.extremedb.SqlTuple; public class ReadThread extends Thread {
private Database db;
private String sql;
private SqlLocalConnection con;
private int id; public ReadThread (Database db, int id)
{
this.db = db;
sql = "select secCode, secName from TestTable limit 100000";
this.id = id;
con = db.connectSql();
} public void run()
{
getTransaction();
closeConnection();
} public void getTransaction()
{
long start = getRunTime();
SqlResultSet result = con.executeQuery(sql);
for (String column : result.getColumnNames()) {
System.out.print(column + " ");
}
System.out.println(); for (SqlTuple tuple : result)
System.out.println(tuple.get("secCode") + " " + tuple.get("secName") + " "); con.commitTransaction();
long end = getRunTime(); try {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(id%5 + ".txt", true)));
String str = Long.toString(100000*1000/(end-start));
bw.write(str);
bw.write("\n");
bw.flush();
bw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} public long getRunTime()
{
return System.currentTimeMillis();
} public void closeConnection()
{
con.disconnect();
} }
Class ReadThread
import java.math.BigDecimal;
import java.math.RoundingMode;
import com.mcobject.extremedb.Database; public class MultThreadAccess {
public static int PAGE_SIZE = 128;
public static int DATABASE_SIZE = 1024*1024*1024;
public static int MAXCONN = 100;
public static int NUM = 100*1000;
public static int THREAD = 2;
private Database db; public void initDataBase()
{
int config = Database.MCO_CFG_SQL_SUPPORT;
Database.Parameters params = new Database.Parameters();
params.memPageSize = PAGE_SIZE;
params.databaseSnapshotFilePath = "db.img";
params.classes = new Class[] {TestTable.class};
params.maxConnections = MAXCONN; Database.Device device[] = new Database.Device[1];
device[0] = new Database.PrivateMemoryDevice(Database.Device.Kind.Data, DATABASE_SIZE);
db = new Database(config);
db.open("DataAccess", params, device);
System.out.println("OK");
} public void closeDataBase()
{
db.close();
} public Database getDB()
{
return db;
} public static void main(String[] args) {
// TODO Auto-generated method stub
TestTable []table = new TestTable[THREAD];
for (int i = 0; i < THREAD; i++) {
table[i] = new TestTable();
table[i].secCode = null;
table[i].secName = null;
table[i].tDate = null;
table[i].tTime = null; table[i].lastClose = new BigDecimal(15.857).setScale(3, RoundingMode.HALF_UP);
table[i].OP = new BigDecimal(10.132).setScale(3, RoundingMode.HALF_UP);
table[i].CP = new BigDecimal(12.310).setScale(3, RoundingMode.HALF_UP);
table[i].TQ = new BigDecimal(14.185).setScale(3, RoundingMode.HALF_UP);
table[i].TM = new BigDecimal(19.107).setScale(3, RoundingMode.HALF_UP);
table[i].CQ = new BigDecimal(8.457).setScale(3, RoundingMode.HALF_UP);
table[i].CM = new BigDecimal(7.859).setScale(3, RoundingMode.HALF_UP);
table[i].CT = new BigDecimal(13.101).setScale(3, RoundingMode.HALF_UP);
table[i].BS = null; table[i].BSRATIO = new BigDecimal(18.525).setScale(3, RoundingMode.HALF_UP);
table[i].SPD = new BigDecimal(6.108).setScale(3, RoundingMode.HALF_UP);
table[i].RPD = new BigDecimal(3.199).setScale(3, RoundingMode.HALF_UP);
table[i].UNIX = System.currentTimeMillis();
table[i].market = "SSE";
} int code = 700000;
MultThreadAccess mt = new MultThreadAccess();
mt.initDataBase();
WriteThread[] wThread = new WriteThread[THREAD/2];
ReadThread[] rThread = new ReadThread[THREAD/2];
for (int i = 0; i < THREAD/2; i++) {
wThread[i] = new WriteThread(mt.getDB(), code+i*NUM/THREAD, table[i], i);
rThread[i] = new ReadThread(mt.getDB(), i);
wThread[i].start();
rThread[i].start();
} try {
while(true);
} catch (Exception e) {
mt.closeDataBase();
} }
}
Class MultThreadAccess
作者:志青云集
出处:http://www.cnblogs.com/lyssym
如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】。
如果,您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是【志青云集】。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
数据库选型之内存数据库eXtremeDB的更多相关文章
- Java免费开源数据库、Java嵌入式数据库、Java内存数据库
Java免费开源数据库.Java嵌入式数据库.Java内存数据库 http://blog.csdn.net/leiyinsu/article/details/8597680
- nosql数据库选型
http://blogread.cn/it/article/6654 今天在书店里翻完了一遍<七天七数据库>.这本书简单介绍了postgreSQL,riak,mongodb,HBase,r ...
- 数据库选型之MySQL(多线程并发)
刘勇 Email: lyssym@sina.com 本博客记录作者在工作与研究中所经历的点滴,一方面给自己的工作与生活留下印记,另一方面若是能对大家有所帮助,则幸甚至哉矣! 简介 鉴于高频中心库 ...
- 时间序列数据库选型——本质是列存储,B-tree索引,抑或是搜索引擎中的倒排索引
时间序列数据库最多,使用也最广泛.一般人们谈论时间序列数据库的时候指代的就是这一类存储.按照底层技术不同可以划分为三类. 直接基于文件的简单存储:RRD Tool,Graphite Whisper.这 ...
- 数据库选型之亿级数据量并发访问(MySQL集群)
刘 勇 Email:lyssym@sina.com 简介 针对实际应用中并发访问MySQL的场景,本文采用多线程对MySQL进行并发读取访问,其中以返回用户所需的数据并显示在终端为测试结束节点,即将 ...
- 数据库选型之MySQL(固态硬盘)
刘勇 Email: lyssym@sina.com 本博客记录作者在工作与研究中所经历的点滴,一方面给自己的工作与生活留下印记,另一方面若是能对大家有所帮助,则幸甚至哉矣! 简介 鉴于高频中心库 ...
- 数据库选型之MySQL(普通硬盘)
刘勇 Email:lyssym@sina.com 本博客记录作者在工作与研究中所经历的点滴,一方面给自己的工作与生活留下印记,另一方面若是能对大家有所帮助,则幸甚至哉矣! 简介 鉴于高频中心库ta ...
- IOT数据库选型——NOSQL,MemSQL,cassandra,Riak或者OpenTSDB,InfluxDB
IoT databases should be as flexible as required by the application. NoSQLdatabases -- especially key ...
- sqlite内存数据库和文件数据库的同步[转]
由于sqlite对多进程操作支持效果不太理想,在项目中,为了避免频繁读写 文件数据库带来的性能损耗,我们可以采用操作sqlite内存数据库,并将内存数据库定时同步到文件数据库中的方法. 实现思路如下: ...
随机推荐
- 在windows上安装和启动Elasticseach、Kibana
写在前面的话:读书破万卷,编码如有神-------------------------------------------------------------------- 参考内容: <Ela ...
- c# -- 介绍File.AppendAllText 方法
下面介绍两个函数: File.AppendAllText (String, String) File.AppendAllText (String, String, String) File.Appen ...
- C++ -- STL泛型编程(一)之vector
STL提供三种组件:容器,迭代器,算法,它们都支持泛型程序设计标准容器有两类:顺序容器和关联容器. 顺序容器(vector,list,deque,string等)是一系列元素的有序组合. 关联容器(s ...
- C# 基于正则表达式的字符串验证
输入的字符串校验,是开发中经常遇到的问题,常用的办法是利用正则表达式进行判断.其特点是简洁有效. 1.正则表达基础知识 正则表达式的教程很多,这里两个基础教程: a.http://www.cnblog ...
- ExtJs ComboBox 在IE 下 自动完成功能无效的解决方案
使用 ComboBox 来作为自动完成的组件,就像google suggestion ,可是在IE下怎么也无法输入字符,是处于不可编辑状态,而firefox和chrome都正常显示.我在2个ExtJs ...
- DC-DC converter Control techniques
As shown in figure 3.4, PWM controller contains two main parts; voltage error-amplifier and voltage ...
- Mysql字符串连接函数 CONCAT()与 CONCAT_WS()
从数据库里取N个字段,然后组合到一起用“,”分割显示,起初想到用CONCAT()来处理,好是麻烦,没想到在手册里居然有提到 CONCAT_WS(),非常好用. CONCAT_WS(separator, ...
- cadence学习(1)常规封装的建立
1.建立焊盘. (1)首先要获得datasheet(或可用pcb matrix ipc-7531标准的可查询封装软件)中元器件的封装信息. (2)建立.pad文件.打开PCB Editor Utili ...
- 不用call和apply方法模拟实现ES5的bind方法
本文首发我的个人博客:前端小密圈,评论交流送1024邀请码,嘿嘿嘿
- asmack xmpp 获取离线消息
原文:http://plplum.blog.163.com/blog/static/31032400201503015345948/ 注意事项: 1.登录前要将状态设置为离线: ConnectionC ...