數據庫選型之內存數據庫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
  如果,您認為閱讀這篇博客讓您有些收獲,不妨點擊一下右下角的【推薦】。
  如果,您希望更容易地發現我的新博客,不妨點擊一下左下角的【關注我】。
  如果,您對我的博客所講述的內容有興趣,請繼續關注我的后續博客,我是【志青雲集】。
  本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接。


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM