刘勇 Email: [email protected]
本博客记录作者在工作与研究中所经历的点滴,一方面给自己的工作与生活留下印记,另一方面若是能对大家有所帮助,则幸甚至哉矣!
简介
鉴于高频中心库task(核心业务处理与存储逻辑)部分占用机器较多,为节省成本,调研数据库或缓存,以期满足高频生产的需求:1)峰值1w条/s;2)峰值60w条/m。本着节省成本的角度,本文对开源、免费的数据库MySQL在固态硬盘下从事务处理条目下展开测试,测试目标平均写入速率达10000条/s 以上则能满足要求。
测试环境
硬件环境
10.1.120.34:Intel Core I5-4590, 主频:3.30G, 内存:16G, 有固态硬盘
软件环境:
10.1.120.34: Cent OS 6.5, MySQL 5.6.26 (社区版)
表结构:
1 DROP TABLE IF EXISTS `transaction`; 2 CREATE TABLE `transaction` ( 3 `tradedate` datetime DEFAULT NOT NULL, 4 `symbol` varchar(6) DEFAULT NOT NULL, 5 `symbolname` varchar(8) DEFAULT NOT NULL, 6 `trdmintime` varchar(6) DEFAULT NOT NULL, 7 `startprice` decimal(9,3) DEFAULT NOT NULL, 8 `highprice` decimal(9,3) DEFAULT NOT NULL, 9 `lowprice` decimal(9,3) DEFAULT NOT NULL, 10 `endprice` decimal(9,3) DEFAULT NOT NULL, 11 `change` decimal(9,3) DEFAULT NOT NULL, 12 `changeratio` decimal(6,3) DEFAULT NOT NULL, 13 `minvolume` decimal(10,0) DEFAULT NOT NULL, 14 `minamout` decimal(16,3) DEFAULT NOT NULL, 15 `unix` bigint(20) DEFAULT NOT NULL, 16 `market` varchar(3) DEFAULT NOT NULL 17 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
配置文件:即数据库配置文件,见/etc/my.cnf
1 # read_rnd_buffer_size = 2M 2 datadir=/var/lib/mysql 3 socket=/var/lib/mysql/mysql.sock 4 innodb_flush_log_at_trx_commit = 2 5 innodb_autoinc_lock_mode = 2 6 query_cache_type = 1 7 query_cache_size = 20M 8 innodb_buffer_pool_size = 2G 9 innodb_flush_method = O_DSYNC 10 # Disabling symbolic-links is recommended to prevent assorted security risks 11 symbolic-links=0
性能测试
事务处理
针对高频生产的应用需求,本文构造高频中心库系统的数据结构,从事物处理角度,对本地节点MySQL进行写入操作,分别存储数据量为60K、100K、600K条数据,对其速率进行测试。需要指出,由于常见I/O访问的瓶颈主要受限于写入测试,本文只针对写入操作进行测试,暂不考虑读取操作或者混合读写方式,若写入操作不满足要求,其它操作无需测试。
本文采用写入多条数据执行事务处理。因为10.1.120.34上采用固态硬盘作为存储介质,其安装有MySQL,根据应用场景,第一种从远程访问,即测试主机10.1.25.50访问MySQL,第二种从本地访问10.1.120.34访问MySQL。以下分别从事务处理条目为1000、2000和3000下展开测试。
以1000条数据为基础执行事务处理,结果见表-1。
表-1 1000条下事务处理MySQL测试结果
节点 |
数据库IP |
数据量(K) |
平均写入速率(条/s) |
远程节点 | 10.1.120.34 | 60 | 2328 |
远程节点 | 10.1.120.34 | 100 | 2366 |
远程节点 | 10.1.120.34 | 600 | 2372 |
本地节点 | 10.1.120.34 | 60 | 14936 |
本地节点 | 10.1.120.34 | 100 | 16526 |
本地节点 | 10.1.120.34 | 600 | 21169 |
以2000条数据为基础执行事务处理,结果见表-2。
表-2 2000条下事务处理MySQL测试结果
节点 |
数据库IP |
数据量(K) |
平均写入速率(条/s) |
远程节点 | 10.1.120.34 | 60 | 2318 |
远程节点 | 10.1.120.34 | 100 | 2333 |
远程节点 | 10.1.120.34 | 600 | 2320 |
本地节点 | 10.1.120.34 | 60 | 12714 |
本地节点 | 10.1.120.34 | 100 | 17914 |
本地节点 | 10.1.120.34 | 600 | 20214 |
以3000条数据为基础执行事务处理,结果见表-3。
表-3 3000条下事务处理MySQL测试结果
节点 |
数据库IP |
数据量(K) |
平均写入速率(条/s) |
远程节点 | 10.1.120.34 | 60 | 2319 |
远程节点 | 10.1.120.34 | 100 | 2377 |
远程节点 | 10.1.120.34 | 600 | 2429 |
本地节点 | 10.1.120.34 | 60 | 11202 |
本地节点 | 10.1.120.34 | 100 | 17587 |
本地节点 | 10.1.120.34 | 600 | 18577 |
小结
从表1-3可知:1)在远程节点访问MySQL,即使采用固态硬盘,其速率还是较慢,无法满足需求;2)在本地节点,访问MySQL,则能够满足高频生产的需求。
批处理
为比较批处理方式和事务处理方式的性能,本文针对其进行比较测试。
每1000条数据,执行一次批处理,测试结果如表-4。
表-4 1000条下批处理与事务处理MySQL测试结果
方式 |
数据库IP |
数据量(K) |
平均写入速率(条/s) |
批处理 | 10.1.120.34 | 60 | 15974 |
批处理 | 10.1.120.34 | 100 | 16328 |
批处理 | 10.1.120.34 | 600 | 17633 |
事务处理 | 10.1.120.34 | 60 | 14936 |
事务处理 | 10.1.120.34 | 100 | 16526 |
事务处理 | 10.1.120.34 | 600 | 21169 |
每2000条数据,执行一次批处理,测试结果如表-5。
表-5 2000条下批处理与事务处理MySQL测试结果
方式 |
数据库IP |
数据量(K) |
平均写入速率(条/s) |
批处理 | 10.1.120.34 | 60 | 14840 |
批处理 | 10.1.120.34 | 100 | 16310 |
批处理 | 10.1.120.34 | 600 | 17364 |
事务处理 | 10.1.120.34 | 60 | 12714 |
事务处理 | 10.1.120.34 | 100 | 17914 |
事务处理 | 10.1.120.34 | 600 | 20214 |
每3000条数据,执行一次批处理,测试结果如表-6。
表-6 3000条下批处理与事务处理MySQL测试结果
节点 |
数据库IP |
数据量(K) |
平均写入速率(条/s) |
批处理 | 10.1.120.34 | 60 | 14641 |
批处理 | 10.1.120.34 | 100 | 16371 |
批处理 | 10.1.120.34 | 600 | 17593 |
事务处理 | 10.1.120.34 | 60 | 11202 |
事务处理 | 10.1.120.34 | 100 | 17587 |
事务处理 | 10.1.120.34 | 600 | 18577 |
小结
从表4-6测试结果来看,在写入数据量少时,批处理方式较事务方式速率快一些,但是随着访问数据量增加,事务处理方式稍微快一些。
总结
从上述测试结果来看,以固态硬盘作为存储介质,在本地访问MySQL可以满足高频生产的需求。此外,鉴于高频生产高负载的需求,优先选择事务处理方式。
附录:
测试部分源代码:
1 public void initMySQL() 2 { 3 String driver = "com.mysql.jdbc.Driver"; 4 String url = "jdbc:mysql://10.1.120.34:3306/hdfs"; 5 String user = "root"; 6 String password = ""; 7 8 try { 9 Class.forName(driver); 10 conn = DriverManager.getConnection(url, user, password); 11 if (!conn.isClosed()) 12 System.out.println("Start MySQL!"); 13 } catch (Exception e) { 14 e.printStackTrace(); 15 } 16 17 count = 0; 18 sql = "insert into transaction" + " values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; 19 try { 20 pstm = conn.prepareStatement(sql); 21 conn.setAutoCommit(false); 22 } catch (SQLException e) { 23 e.printStackTrace(); 24 } 25 }
1 public void insertData(Transaction ts) 2 { 3 try { 4 pstm.setDate(1, ts.getTradedate()); 5 pstm.setString(2, ts.getSymbol()); 6 pstm.setString(3, ts.getSymbolName()); 7 pstm.setString(4, ts.getTrdmintime()); 8 pstm.setBigDecimal(5, ts.getStartprice()); 9 pstm.setBigDecimal(6, ts.getHighprice()); 10 pstm.setBigDecimal(7, ts.getLowprice()); 11 pstm.setBigDecimal(8, ts.getEndprice()); 12 pstm.setBigDecimal(9, ts.getChange()); 13 pstm.setBigDecimal(10, ts.getChangeratio()); 14 pstm.setBigDecimal(11, ts.getMinvolume()); 15 pstm.setBigDecimal(12, ts.getMinamout()); 16 pstm.setLong(13, ts.getUnix()); 17 pstm.setString(14, ts.getMarket()); 18 19 pstm.executeUpdate(); 20 count++ ; 21 if (count == Test.PREFIX) { 22 conn.commit(); 23 conn.setAutoCommit(false); 24 count = 0; 25 } 26 27 } catch (SQLException e) { 28 try { 29 conn.rollback(); 30 } catch (SQLException e1) { 31 e1.printStackTrace(); 32 } 33 e.printStackTrace(); 34 } 35 }