• 数据库选型之内存数据库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

    表结构:

     1 DROP TABLE IF EXISTS `TAQ`;
     2 CREATE TABLE `TAQ` (
     3   `SECCODE` varchar(6) NOT NULL,
     4   `SECNAME` varchar(20) NOT NULL,
     5   `TDATE` varchar(10) NOT NULL,
     6   `TTIME` varchar(6) NOT NULL,
     7   `LASTCLOSE` decimal(19,3) DEFAULT NULL,
     8   `OP` decimal(19,3) DEFAULT NULL,
     9   `CP` decimal(19,3) DEFAULT NULL,
    10   `TQ` decimal(19,3) DEFAULT NULL,
    11   `TM` decimal(19,3) DEFAULT NULL,
    12   `CQ` decimal(18,0) DEFAULT NULL,
    13   `CM` decimal(19,3) DEFAULT NULL,
    14   `CT` decimal(19,3) DEFAULT NULL,
    15   `BS` varchar(18) DEFAULT NULL,
    16   `BSRATIO` decimal(19,3) DEFAULT NULL,
    17   `SPD` decimal(19,3) DEFAULT NULL,
    18   `RPD` decimal(19,3) DEFAULT NULL,
    19   `UNIX` bigint(20) DEFAULT NULL,
    20   `MARKET` varchar(4) DEFAULT NULL,
    21   KEY `SECCODE` (`SECCODE`,`TDATE`,`TTIME`)
    22 ) /*!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所示。

    image

            批处理以批处理数据量为1000,2000和3000条角度展开测试,主要针对批处理写入速率,由于本文读取操作涉及从数据库读取数据并在控制台显示出来,其处理瓶颈主要集中于数据显示部分,因此本部分读取速率暂不考虑,其测试结果如表-2所示。

    image 

    小结

            从表-1和表-2可知:1)从写入速率角度来看,批处理相对单条处理而言,并没有优势,主要原因在于,eXtremeDB为内存数据库,批处理在内存中还多了一份批量累积过程;2)从整体而言, 相对之前对MySQL(即使是在固态硬盘本地,详细内容见以前测试报告),eXtremeDB的读写速率还是比较快的,测试速率大约为10w条/s。

    多线程访问

            以60K、100K和600K条为基础数据总量,在不同线程个数下,对eXtremeDB展开读写访问,其中在不同线程个数下的写入速率如表-3所示。需要指出的是,此处多线程写入测试,以多线程处理平摊的方式,即写入60K条数据,采用20个线程执行,则每个线程处理3K(60K/20)条数据,详细内容见测试源代码,在此不再赘述。

    image

            在不同线程下的读取速率如表-4所示。需要指出的是,此处多线程读取速率,每个线程则完整读取给定的数据总量,即若数据总量为60K条,则每个线程读取60K数据,获取全表中指定的2个字段,并显示出来。

    image

    小结

            从表-3和表-4可知:1)随着数据总量的增加,多线程访问速率整体上变化不明显,即线程读写速率相对比较稳定;2)与表-1相比较,针对多线程,若以平均速率乘以线程个数角度来说,则多线程与单线程在读取数据并显示出来上,两者速率差不多,此外,多线程写入和单线程写入的速率也差不多(注意多线程写入是平摊方式,两者可以直接比较),因此,多线程访问eXtremeDB,相对单线程并未衰减其性能。

    总结

            从上述2种场景测试结果来看,eXtremeDB的读写速率大约在10w条/s,相对一般的数据库(非内存数据库,即使在固态硬盘上)其速率也是比较快的。此外,针对多线程单一读/写并发访问,与单线程相比,也并未衰减其读写性能,因此对于并发访问来说,也能满足一定的需求。

    测试源程序:

     1 import com.mcobject.extremedb.*;
     2 import java.sql.Date;
     3 import java.math.BigDecimal;
     4 
     5 @Persistent // class will be stored in eXtremeDB database 
     6 class TestTable {
     7      @Indexable(unique=true)
     8      public String secCode;
     9      
    10      public String secName;
    11      public Date tDate;
    12      public Date tTime;
    13      
    14      public BigDecimal lastClose;
    15      public BigDecimal OP;
    16      public BigDecimal CP;
    17      public BigDecimal TQ;
    18      public BigDecimal TM;
    19      public BigDecimal CQ;
    20      public BigDecimal CM;
    21      public BigDecimal CT;
    22      public String BS;
    23      
    24      public BigDecimal BSRATIO;
    25      public BigDecimal SPD;
    26      public BigDecimal RPD;
    27      public long UNIX;
    28      public String market;
    29 }
    Class TestTable
      1 import com.mcobject.extremedb.*;
      2 import java.math.BigDecimal;
      3 import java.math.RoundingMode;
      4 import java.sql.*;
      5 
      6 public class DataAccess {
      7     public static int PAGE_SIZE = 128;
      8     public static int DATABASE_SIZE = 512*1024*1024;
      9     public static int MAXCONN = 100;
     10     public static int NUM = 100*1000;
     11     public static int BATCH = 1000;
     12     private Database db;
     13     private String sql;
     14     private SqlLocalConnection con;
     15     
     16     public void initDatabase()
     17     {
     18         int config = Database.MCO_CFG_SQL_SUPPORT;
     19         Database.Parameters params = new Database.Parameters();
     20         params.memPageSize = PAGE_SIZE;
     21         params.classes = new Class[] {TestTable.class};
     22         params.maxConnections = MAXCONN;
     23         
     24         Database.Device device[] = new Database.Device[1];
     25         device[0] = new Database.PrivateMemoryDevice(Database.Device.Kind.Data, DATABASE_SIZE);
     26         db = new Database(config);
     27         db.open("DataAccess", params, device);
     28         sql = "select secCode, secName from TestTable";
     29         con = db.connectSql();
     30     }
     31     
     32     
     33     public void insertTransaction(TestTable table)
     34     {
     35         int code = 100000;
     36         long start = getRunTime();
     37         for (int i = 0; i < NUM; i++) 
     38         {
     39             con.startTransaction(Database.TransactionType.ReadWrite);
     40             table.secCode = Integer.toString(code);
     41             table.secName ="中国银行";
     42             table.tDate = new Date(System.currentTimeMillis());
     43             table.tTime = new Date(System.currentTimeMillis()+5);
     44             table.UNIX += 1;
     45             con.insert(table);    
     46             con.commitTransaction();            
     47             code++ ;
     48         }
     49         long end = getRunTime();
     50         System.out.println("写入速率为: " + NUM*1000/(end-start));
     51         con.saveSnapshot("db.img");
     52     }
     53     
     54     
     55     public long getRunTime()
     56     {
     57         return System.currentTimeMillis();
     58     }
     59     
     60     
     61     public void getTransaction()
     62     {
     63         long start = getRunTime();
     64         con.startTransaction(Database.TransactionType.ReadWrite);
     65         SqlResultSet result = con.executeQuery(sql);
     66         for (String column : result.getColumnNames()) { 
     67             System.out.print(column + "        ");
     68         }
     69         System.out.println();
     70         
     71         for (SqlTuple tuple : result) {
     72              System.out.println(tuple.get("secCode") + "        " + tuple.get("secName") + "        ");
     73         }
     74         con.commitTransaction();
     75         
     76         long end = getRunTime();
     77         System.out.println("读取速率为: " + NUM*1000/(end-start));
     78     }
     79     
     80     
     81     public void closeDatabase()
     82     {
     83         con.disconnect();
     84         db.close();
     85     }
     86     
     87     
     88     public void deleteData()
     89     {
     90         con.startTransaction(Database.TransactionType.ReadWrite);
     91         con.removeAll(TestTable.class);
     92         con.commitTransaction();
     93         System.out.println("delete all the data!");
     94     }
     95         
     96     public static void main(String[] args) {
     97         // TODO Auto-generated method stub
     98         TestTable table = new TestTable();
     99         table.secCode = null;
    100         table.secName = null;
    101         table.tDate = null;
    102         table.tTime = null;
    103          
    104         table.lastClose = new BigDecimal(15.857).setScale(3, RoundingMode.HALF_UP);
    105         table.OP = new BigDecimal(10.132).setScale(3, RoundingMode.HALF_UP);
    106         table.CP = new BigDecimal(12.310).setScale(3, RoundingMode.HALF_UP);
    107         table.TQ = new BigDecimal(14.185).setScale(3, RoundingMode.HALF_UP);
    108         table.TM = new BigDecimal(19.107).setScale(3, RoundingMode.HALF_UP);
    109         table.CQ = new BigDecimal(8.457).setScale(3, RoundingMode.HALF_UP);
    110         table.CM = new BigDecimal(7.859).setScale(3, RoundingMode.HALF_UP);
    111         table.CT = new BigDecimal(13.101).setScale(3, RoundingMode.HALF_UP);
    112         table.BS = null;
    113          
    114         table.BSRATIO = new BigDecimal(18.525).setScale(3, RoundingMode.HALF_UP);
    115         table.SPD = new BigDecimal(6.108).setScale(3, RoundingMode.HALF_UP);
    116         table.RPD = new BigDecimal(3.199).setScale(3, RoundingMode.HALF_UP);
    117         table.UNIX = System.currentTimeMillis();
    118         table.market = "SSE";
    119         
    120         DataAccess da = new DataAccess();
    121         da.initDatabase();
    122         da.insertTransaction(table);
    123 //        da.getTransaction();
    124 //        da.deleteData();
    125         da.closeDatabase();
    126     }
    127 
    128 }
    Class DataAccess
    并发读/写混合测试

            本文先构建100K和600K条的基础数据量,以避免读取线程初始读取数据失败情形。然后在该基础数据量的基础上,写入数据总量为100K条的数据,其中写入操作采用平摊的方式,即写入的总数据量平摊至每个写入线程中,而读取操作则保持在每个线程读取100K条的数据,详细内容见测试源代码。

            以下从基础数据量为100K和600K条2个方面对eXtremeDB展开测试。

    100K

            从不同线程个数角度对该数据库展开测试,其中读/写线程各占50%,即若表格中线程个数为10个时,读/写线程各占5个,后续表格内容与之类同,不再赘述了。写入操作以写入总量100K条数据,并平摊给每个写入线程,读取操作中每个读取线程执行 "select secCode, secName from TestTable limit 100000"操作。基础数据量为100K条的测试结果见表-5。

    image

    600K

            写入操作以写入总量100K条数据,并平摊给每个写入线程,读取操作中每个读取线程执行 "select secCode, secName from TestTable limit 100000, 100000"操作。基础数据量为600K条下的测试结果见表-6。

    image

    总结

            从表-5和表-6可知:1)多线程读写混合访问eXtremeDB的单个线程写入速率大约在10w条/s, 单个线程读取速率大约在4w条/s;2)随着读取线程和写入线程的增加,eXtremeDB的读写速率变化不大,整体上比较稳定。

     并发读/写测试源代码:

     1 import com.mcobject.extremedb.*;
     2 import java.sql.Date;
     3 import java.math.BigDecimal;
     4 
     5 @Persistent // class will be stored in eXtremeDB database 
     6 class TestTable {
     7      @Indexable(unique=true)
     8      public String secCode;
     9      
    10      public String secName;
    11      public Date tDate;
    12      public Date tTime;
    13      
    14      public BigDecimal lastClose;
    15      public BigDecimal OP;
    16      public BigDecimal CP;
    17      public BigDecimal TQ;
    18      public BigDecimal TM;
    19      public BigDecimal CQ;
    20      public BigDecimal CM;
    21      public BigDecimal CT;
    22      public String BS;
    23      
    24      public BigDecimal BSRATIO;
    25      public BigDecimal SPD;
    26      public BigDecimal RPD;
    27      public long UNIX;
    28      public String market;
    29 }
    Class TestTable
     1 import java.sql.Date;
     2 import java.io.BufferedWriter;
     3 import java.io.FileOutputStream;
     4 import java.io.IOException;
     5 import java.io.OutputStreamWriter;
     6 import com.mcobject.extremedb.Database;
     7 import com.mcobject.extremedb.SqlLocalConnection;
     8 
     9 public class WriteThread extends Thread {
    10     private int code;
    11     private Database db;
    12     private int id;
    13     private SqlLocalConnection con;
    14     private TestTable table;
    15     
    16     public WriteThread(Database db, int code, TestTable table, int id)
    17     {
    18         this.db = db;
    19         this.code = code;
    20         this.table = table;
    21         this.id = id;
    22         con = db.connectSql();
    23         con.startTransaction(Database.TransactionType.ReadWrite);
    24     }
    25     
    26     
    27     public void run()
    28     {
    29         insertTransaction();
    30         closeConnection();
    31     }
    32     
    33     
    34     public void insertTransaction()
    35     {
    36         long start = getRunTime();
    37         for (int i = 0; i < MultThreadAccess.NUM*2/MultThreadAccess.THREAD; i++) 
    38         {
    39             con.startTransaction(Database.TransactionType.ReadWrite);
    40             table.secCode = Integer.toString(code);
    41             table.secName = "中国银行";
    42             table.tDate = new Date(System.currentTimeMillis());
    43             table.tTime = new Date(System.currentTimeMillis()+5);
    44             table.UNIX += 1;
    45             con.insert(table);
    46             con.commitTransaction();
    47             code++ ;
    48         }
    49         long end = getRunTime();
    50         
    51         try {
    52             BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(id%5+5 + ".txt", true)));
    53             String str = Long.toString(MultThreadAccess.NUM*2/MultThreadAccess.THREAD*1000/(end-start));
    54             bw.write(str);
    55             bw.write("\n");
    56             bw.flush();
    57             bw.close();
    58         } catch (IOException e) {
    59             // TODO Auto-generated catch block
    60             e.printStackTrace();
    61         }
    62     }
    63     
    64     
    65     public long getRunTime()
    66     {
    67         return System.currentTimeMillis();
    68     }
    69     
    70     
    71     public void closeConnection()
    72     {
    73         con.disconnect();
    74     }
    75 
    76 }
    Class WriteThread
     1 import java.io.BufferedWriter;
     2 import java.io.FileOutputStream;
     3 import java.io.IOException;
     4 import java.io.OutputStreamWriter;
     5 import com.mcobject.extremedb.Database;
     6 import com.mcobject.extremedb.SqlLocalConnection;
     7 import com.mcobject.extremedb.SqlResultSet;
     8 import com.mcobject.extremedb.SqlTuple;
     9 
    10 public class ReadThread extends Thread {
    11     private Database db;
    12     private String sql;
    13     private SqlLocalConnection con;
    14     private int id;
    15     
    16     public ReadThread (Database db, int id)
    17     {
    18         this.db = db;
    19         sql = "select secCode, secName from TestTable limit 100000";
    20         this.id = id;
    21         con = db.connectSql();
    22     }
    23     
    24     
    25     public void run() 
    26     {
    27         getTransaction();
    28         closeConnection();
    29     }
    30     
    31     
    32     public void getTransaction()
    33     {
    34         long start = getRunTime();
    35         SqlResultSet result = con.executeQuery(sql);
    36         for (String column : result.getColumnNames()) { 
    37             System.out.print(column + "        ");
    38         }
    39         System.out.println();
    40         
    41         for (SqlTuple tuple : result)
    42              System.out.println(tuple.get("secCode") + "        " + tuple.get("secName") + "        ");
    43 
    44         con.commitTransaction();
    45         long end = getRunTime();
    46         
    47         try {
    48             BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(id%5 + ".txt", true)));
    49             String str = Long.toString(100000*1000/(end-start));
    50             bw.write(str);
    51             bw.write("\n");
    52             bw.flush();
    53             bw.close();
    54         } catch (IOException e) {
    55             // TODO Auto-generated catch block
    56             e.printStackTrace();
    57         }
    58     }
    59     
    60     
    61     public long getRunTime()
    62     {
    63         return System.currentTimeMillis();
    64     }
    65     
    66     
    67     public void closeConnection()
    68     {
    69         con.disconnect();
    70     }
    71 
    72 }
    Class ReadThread
     1 import java.math.BigDecimal;
     2 import java.math.RoundingMode;
     3 import com.mcobject.extremedb.Database;
     4 
     5 public class MultThreadAccess {
     6     public static int PAGE_SIZE = 128;
     7     public static int DATABASE_SIZE = 1024*1024*1024;
     8     public static int MAXCONN = 100;
     9     public static int NUM = 100*1000;
    10     public static int THREAD = 2;
    11     private Database db;
    12     
    13     
    14     public void initDataBase()
    15     {
    16         int config = Database.MCO_CFG_SQL_SUPPORT;
    17         Database.Parameters params = new Database.Parameters();
    18         params.memPageSize = PAGE_SIZE;
    19         params.databaseSnapshotFilePath = "db.img";
    20         params.classes = new Class[] {TestTable.class};
    21         params.maxConnections = MAXCONN;
    22         
    23         Database.Device device[] = new Database.Device[1];
    24         device[0] = new Database.PrivateMemoryDevice(Database.Device.Kind.Data, DATABASE_SIZE);
    25         db = new Database(config);
    26         db.open("DataAccess", params, device);
    27         System.out.println("OK");
    28     }
    29     
    30     
    31     public void closeDataBase()
    32     {
    33         db.close();
    34     }
    35     
    36     
    37     public Database getDB()
    38     {
    39         return db;
    40     }
    41     
    42 
    43     public static void main(String[] args) {
    44         // TODO Auto-generated method stub
    45         TestTable []table = new TestTable[THREAD];
    46         for (int i = 0; i < THREAD; i++) {
    47             table[i] = new TestTable();
    48             table[i].secCode = null;
    49             table[i].secName = null;
    50             table[i].tDate = null;
    51             table[i].tTime = null;
    52              
    53             table[i].lastClose = new BigDecimal(15.857).setScale(3, RoundingMode.HALF_UP);
    54             table[i].OP = new BigDecimal(10.132).setScale(3, RoundingMode.HALF_UP);
    55             table[i].CP = new BigDecimal(12.310).setScale(3, RoundingMode.HALF_UP);
    56             table[i].TQ = new BigDecimal(14.185).setScale(3, RoundingMode.HALF_UP);
    57             table[i].TM = new BigDecimal(19.107).setScale(3, RoundingMode.HALF_UP);
    58             table[i].CQ = new BigDecimal(8.457).setScale(3, RoundingMode.HALF_UP);
    59             table[i].CM = new BigDecimal(7.859).setScale(3, RoundingMode.HALF_UP);
    60             table[i].CT = new BigDecimal(13.101).setScale(3, RoundingMode.HALF_UP);
    61             table[i].BS = null;
    62              
    63             table[i].BSRATIO = new BigDecimal(18.525).setScale(3, RoundingMode.HALF_UP);
    64             table[i].SPD = new BigDecimal(6.108).setScale(3, RoundingMode.HALF_UP);
    65             table[i].RPD = new BigDecimal(3.199).setScale(3, RoundingMode.HALF_UP);
    66             table[i].UNIX = System.currentTimeMillis();
    67             table[i].market = "SSE";
    68         }
    69         
    70         int code = 700000;
    71         MultThreadAccess mt = new MultThreadAccess();
    72         mt.initDataBase();
    73         WriteThread[] wThread = new WriteThread[THREAD/2];
    74         ReadThread[] rThread = new ReadThread[THREAD/2];
    75         for (int i = 0; i < THREAD/2; i++) {
    76             wThread[i] = new WriteThread(mt.getDB(), code+i*NUM/THREAD, table[i], i);
    77             rThread[i] = new ReadThread(mt.getDB(), i);
    78             wThread[i].start();
    79             rThread[i].start();
    80         }
    81         
    82         try { 
    83             while(true);
    84         } catch (Exception e) {
    85             mt.closeDataBase();
    86         }
    87         
    88     }
    89 }
    Class MultThreadAccess

      


      作者:志青云集
      出处:http://www.cnblogs.com/lyssym
      如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
      如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】。
      如果,您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是【志青云集】。
      本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。


  • 相关阅读:
    web.xml中<web-app>报错
    groovy初体验:groovy在java中的应用
    Mac安装JMeter时Unable to access jarfile ./ApacheJMeter.jar 解决方法
    intellij idea中解决java.lang.VerifyError: Expecting a stackmap frame at branch target的方法
    关于go get无法安装国内被墙软件解决办法
    Oracle 序列
    无锁并发框架Disruptor学习入门
    vsftp服务器部署
    FinalShell 推荐
    supperset (python 2.7.12 + mysql)记录
  • 原文地址:https://www.cnblogs.com/lyssym/p/4881009.html
Copyright © 2020-2023  润新知