单位IM改版了
用户聊天内容要存放在数据库.

一般JAVA Insert MySQL有如下几种方式
1.自动提交Insert
2.事务提交Insert
3.批量提交
4.使用Load File接口

模拟表结构如下

  1. create table chat_message(
  2. id bigint primary key auto_increment,
  3. src_userid bigint not null,
  4. target_userid bigint not null,
  5. message varchar(200),
  6. ts timestamp not null default current_timestamp,
  7. s1 int,
  8. s2 int,
  9. s3 int,
  10. s4 int
  11. );

下面代码,分别使用四种方式,Insert 2w记录.记录执行时间.

依赖
commons-lang3-3.3.2.jar
mysql-connector-java-5.1.31-bin.jar(低版本驱动有性能影响)

  1. import java.io.ByteArrayInputStream;
  2. import java.io.InputStream;
  3. import java.io.UnsupportedEncodingException;
  4. import java.sql.Connection;
  5. import java.sql.DriverManager;
  6. import java.sql.PreparedStatement;
  7. import java.sql.SQLException;
  8. import org.apache.commons.lang3.RandomStringUtils;
  9. public class Main {
  10. private static String URL = "jdbc:mysql://127.0.0.1:3306/mvbox";
  11. private static String USERNAME = "xx";
  12. private static String PWD = "xx";
  13. private static int MAX = 20000;
  14. private static String SQL = "insert into chat_message(src_userid,target_userid,message,s1,s2,s3,s4) values(?,?,?,?,?,?,?)";
  15. public static void main(String[] args) throws ClassNotFoundException, SQLException, UnsupportedEncodingException {
  16. long start = System.currentTimeMillis();
  17. testLoadFile(100);
  18. long end = System.currentTimeMillis();
  19. System.out.println((end - start));
  20. System.out.println(MAX / ((end - start) / 1000));
  21. }
  22. private static Connection getConnection() throws SQLException, ClassNotFoundException {
  23. Class.forName("com.mysql.jdbc.Driver");
  24. Connection con = DriverManager.getConnection(URL, USERNAME, PWD);
  25. return con;
  26. }
  27. private static void testInsert() throws ClassNotFoundException, SQLException {
  28. Connection con = getConnection();
  29. con.setAutoCommit(false);
  30. PreparedStatement pt = con.prepareStatement(SQL);
  31. int i = 0;
  32. while (i < MAX) {
  33. pt.setLong(1, 1 + (int) (Math.random() * 100000000));
  34. pt.setLong(2, 1 + (int) (Math.random() * 100000000));
  35. pt.setString(3, RandomStringUtils.randomAscii(200));
  36. pt.setInt(4, 1);
  37. pt.setInt(5, 1);
  38. pt.setInt(6, 1);
  39. pt.setInt(7, 1);
  40. pt.executeUpdate();
  41. con.commit();
  42. i++;
  43. }
  44. con.close();
  45. }
  46. private static void testInsertAutoCommit() throws ClassNotFoundException, SQLException {
  47. Connection con = getConnection();
  48. con.setAutoCommit(true);
  49. PreparedStatement pt = con.prepareStatement(SQL);
  50. int i = 0;
  51. while (i < MAX) {
  52. pt.setLong(1, 1 + (int) (Math.random() * 100000000));
  53. pt.setLong(2, 1 + (int) (Math.random() * 100000000));
  54. pt.setString(3, RandomStringUtils.randomAscii(200));
  55. pt.setInt(4, 1);
  56. pt.setInt(5, 1);
  57. pt.setInt(6, 1);
  58. pt.setInt(7, 1);
  59. pt.executeUpdate();
  60. i++;
  61. }
  62. con.close();
  63. }
  64. private static void testBatchInsert(int batchSize) throws ClassNotFoundException, SQLException {
  65. Connection con = getConnection();
  66. con.setAutoCommit(false);
  67. PreparedStatement pt = con.prepareStatement(SQL);
  68. int i = 0;
  69. while (i < MAX) {
  70. pt.setLong(1, 1 + (int) (Math.random() * 100000000));
  71. pt.setLong(2, 1 + (int) (Math.random() * 100000000));
  72. pt.setString(3, RandomStringUtils.randomAscii(200));
  73. pt.setInt(4, 1);
  74. pt.setInt(5, 1);
  75. pt.setInt(6, 1);
  76. pt.setInt(7, 1);
  77. pt.addBatch();
  78. if (i % batchSize == 1) {
  79. pt.executeBatch();
  80. con.commit();
  81. }
  82. i++;
  83. }
  84. pt.executeBatch();
  85. con.commit();
  86. con.close();
  87. }
  88. private static void testLoadFile(int batchSize)
  89. throws ClassNotFoundException, SQLException, UnsupportedEncodingException {
  90. String fieldsterminated = "\t\t";
  91. String linesterminated = "\t\r\n";
  92. String loadDataSql = "LOAD DATA LOCAL INFILE 'sql.csv' INTO TABLE chat_message FIELDS TERMINATED BY '"
  93. + fieldsterminated + "'  LINES TERMINATED BY '" + linesterminated
  94. + "' (src_userid,target_userid,message,s1,s2,s3,s4) ";
  95. Connection con = getConnection();
  96. con.setAutoCommit(false);
  97. PreparedStatement pt = con.prepareStatement(loadDataSql);
  98. com.mysql.jdbc.PreparedStatement mysqlStatement = null;
  99. if (pt.isWrapperFor(com.mysql.jdbc.Statement.class)) {
  100. mysqlStatement = pt.unwrap(com.mysql.jdbc.PreparedStatement.class);
  101. }
  102. int i = 0;
  103. StringBuilder sb = new StringBuilder(10000);
  104. while (i < MAX) {
  105. sb.append(1 + (int) (Math.random() * 100000000));
  106. sb.append(fieldsterminated);
  107. sb.append(1 + (int) (Math.random() * 100000000));
  108. sb.append(fieldsterminated);
  109. sb.append(RandomStringUtils.randomAscii(200).replaceAll("\\\\", " "));
  110. sb.append(fieldsterminated);
  111. sb.append(1);
  112. sb.append(fieldsterminated);
  113. sb.append(1);
  114. sb.append(fieldsterminated);
  115. sb.append(1);
  116. sb.append(fieldsterminated);
  117. sb.append(1);
  118. sb.append(linesterminated);
  119. if (i % batchSize == 1) {
  120. byte[] bytes = sb.toString().getBytes();
  121. InputStream in = new ByteArrayInputStream(bytes);
  122. mysqlStatement.setLocalInfileInputStream(in);
  123. mysqlStatement.executeUpdate();
  124. con.commit();
  125. sb = new StringBuilder(10000);
  126. }
  127. i++;
  128. }
  129. byte[] bytes = sb.toString().getBytes();
  130. InputStream in = new ByteArrayInputStream(bytes);
  131. mysqlStatement.setLocalInfileInputStream(in);
  132. mysqlStatement.executeUpdate();
  133. con.commit();
  134. con.close();
  135. }
  136. }

测试结果:

执行方式 执行时间(毫秒) 每秒Insert数量
自动提交 17437 1176
事务提交 22990 909
batchInsert 每10条提交 12646 1666
batchInsert 每50条提交 13758 1538
batchInsert 每100条提交 15870 1333
loadfile 每10条提交 6973 3333
loadfile 每50条提交 5037 4000
loadfile 每100条提交 4175 5000

http://blog.itpub.net/29254281/viewspace-1841299/

一、我们遇到了什么问题

在标准SQL里面,我们通常会写下如下的SQL insert语句。

1
INSERT INTO TBL_TEST (id) VALUES(1);

很显然,在MYSQL中,这样的方式也是可行的。但是当我们需要批量插入数据的时候,这样的语句却会出现性能问题。例如说,如果有需要插入100000条数据,那么就需要有100000条insert语句,每一句都需要提交到关系引擎那里去解析,优化,然后才能够到达存储引擎做真的插入工作。

正是由于性能的瓶颈问题,MYSQL官方文档也就提到了使用批量化插入的方式,也就是在一句INSERT语句里面插入多个值。即,

1
INSERT INTO TBL_TEST (id) VALUES (1), (2), (3)

这样的做法确实也可以起到加速批量插入的功效,原因也不难理解,由于提交到服务器的INSERT语句少了,网络负载少了,最主要的是解析和优化的时间看似增多,但是实际上作用的数据行却实打实地多了。所以整体性能得以提高。根据网上的一些说法,这种方法可以提高几十倍。

然而,我在网上也看到过另外的几种方法,比如说预处理SQL,比如说批量提交。那么这些方法的性能到底如何?本文就会对这些方法做一个比较。

二、比较环境和方法

我的环境比较苦逼,基本上就是一个落后的虚拟机。只有2核,内存为6G。操作系统是SUSI Linux,MYSQL版本是5.6.15。

可以想见,这个机子的性能导致了我的TPS一定非常低,所以下面的所有数据都是没有意义的,但是趋势却不同,它可以看出整个插入的性能走向。

由于业务特点,我们所使用的表非常大,共有195个字段,且写满(每个字段全部填满,包括varchar)大致会有略小于4KB的大小,而通常来说,一条记录的大小也有3KB。

由于根据我们的实际经验,我们很肯定的是,通过在一个事务中提交大量INSERT语句可以大幅度提高性能。所以下面的所有测试都是建立在每插入5000条记录提交一次的做法之上。

最后需要说明的是,下面所有的测试都是通过使用MYSQL C API进行的,并且使用的是INNODB存储引擎。

三、比较方法

理想型测试(一)——方法比较

目的:找出理想情况下最合适的插入机制

关键方法:

1. 每个进/线程按主键顺序插入

2. 比较不同的插入方法

3. 比较不同进/线程数量对插入的影响

*“普通方法”指的是一句INSERT只插入一个VALUE的情况。

*“预处理SQL”指的是使用预处理MYSQL C API的情况。

* “多表值SQL(10条)”是使用一句INSERT语句插入10条记录的情况。为什么是10条?后面的验证告诉了我们这样做性能最高。

结论,很显然,从三种方法的趋势上来看,多表值SQL(10条)的方式最为高效。

理想型测试(二)——多表值SQL条数比较

很显然,在数据量提高的情况下,每条INSERT语句插入10条记录的做法最为高效。

理想型测试(三)——连接数比较

结论:在2倍与CPU核数的连接和操作的时候,性能最高

一般性测试—— 根据我们的业务量进行测试

目的:最佳插入机制适合普通交易情况?

关键方法:

1. 模拟生产数据(每条记录约3KB)

2. 每个线程主键乱序插入

很显然,如果是根据主键乱序插入的话,性能会有直线下降的情况。这一点其实和INNODB的内部实现原理所展现出来的现象一致。但是仍然可以肯定的是,多表值SQL(10条)的情况是最佳的。

压力测试

目的:最佳插入机制适合极端交易情况?

关键方法:

1. 将数据行的每一个字段填满(每条记录约为4KB)

2. 每个线程主键乱序插入

结果和我们之前的规律类似,性能出现了极端下降。并且这里验证了随着记录的增大(可能已经超过了一个page的大小,毕竟还有slot和page head信息占据空间),会有page split等现象,性能会下降。

四、结论

根据上面的测试,以及我们对INNODB的了解,我们可以得到如下的结论。

•采用顺序主键策略(例如自增主键,或者修改业务逻辑,让插入的记录尽可能顺序主键)

•采用多值表(10条)插入方式最为合适

•将进程/线程数控制在2倍CPU数目相对合适

http://www.cnblogs.com/aicro/p/3851434.html

MySQL大量数据入库的性能比较的更多相关文章

  1. MySQL大数据量分页性能优化

    mysql大数据量使用limit分页,随着页码的增大,查询效率越低下. 测试实验 1.   直接用limit start, count分页语句, 也是我程序中用的方法: select * from p ...

  2. 【MYSQL】mysql大数据量分页性能优化

    转载地址: http://www.cnblogs.com/lpfuture/p/5772055.html https://www.cnblogs.com/shiwenhu/p/5757250.html ...

  3. mysql数据库数据入库时间跟当前时间差了8个小时

    vim /etc/my.cnf[mysqld]default-time_zone = '+8:00'重启mysql服务./etc/init.d/mysqld restart 未测试

  4. excel数据 入库mysql 和 mysql数据 导入excel文件

    1.excel数据入库mysql 首先准备excel文件, 标红的地方需要留意,一个是字段名所在行,一个表名对应页: 然后私用mysql工具 navicat, 选择数据库,然后导入文件, 选中相应ex ...

  5. 【Mysql】修改mysql的字符集和默认存储引擎,解决数据入库乱码问题

    背景 在使用Python + Testlink做自动化的过程中,遇到了数据入库出现乱码的情况,后来通过修改字符集的方式解决了这个问题.下面的内容主要来自于一篇相关博文,博主做了细微的调整, 原文链接: ...

  6. MySQL插入数据性能调优

    插入数据性能调优总结: 1.SQL插入语句调优 2.如果是InnoDB引擎的话,尝试开启事务,批量提交 3.调整MySQl数据库配置     参考: 百度空间 - MySQL插入数据性能调优 CSDN ...

  7. 【转】MySQL批量SQL插入各种性能优化

    原文:http://mp.weixin.qq.com/s?__biz=MzA5MzY4NTQwMA==&mid=403182899&idx=1&sn=74edf28b0bd29 ...

  8. 删除MySQL重复数据

    删除MySQL重复数据 项目背景 在最近做的一个linux性能采集项目中,发现线程的程序入库很慢,再仔细定位,发现数据库里面很多冗余数据.因为在采集中,对于同一台设备,同一个时间点应该只有一个数据,然 ...

  9. MySQL批量SQL插入各种性能优化

    对于一些数据量较大的系统.数据库面临的问题除了查询效率低下,还有就是数据入库时间长.特别像报表系统,每天花费在数据导入上的时间可能会长达几个小时或十几个小时之久.因此.优化数据库插入性能是非常有意义的 ...

随机推荐

  1. 可靠联机的 TCP 协议

    可靠联机的 TCP 协议 在前面的 OSI 七层协议当中,在网络层的 IP 之上则是传送层,而传送层的数据打包成什么? 最常见的就是 TCP 封包了.这个 TCP 封包数据必须要能够放到 IP 的数据 ...

  2. Linux权限与命令间的关系

    极重要!权限与命令间的关系: 我们知道权限对於使用者帐号来说是非常重要的,因为他可以限制使用者能不能读取/创建/删除/修改文件或目录! 在这一章我们介绍了很多文件系统的管理命令,第六章则介绍了很多文件 ...

  3. OpenCV处理视频序列的类

    代码出处,opencv2 cookbook: /*--------------------------------------------------------------------------- ...

  4. java解决hash算法冲突

    看了ConcurrentHashMap的实现, 使用的是拉链法. 虽然我们不希望发生冲突,但实际上发生冲突的可能性仍是存在的.当关键字值域远大于哈希表的长度,而且事先并不知道关键字的具体取值时.冲突就 ...

  5. 史上最强Spring mvc入门

    一.SpringMVC基础入门,创建一个HelloWorld程序 1.首先,导入SpringMVC需要的jar包. 2.添加Web.xml配置文件中关于SpringMVC的配置 1 2 3 4 5 6 ...

  6. 飞鱼相册笔记(1)----外置SD卡文件夹名称不区分大小写

    飞鱼相册笔记(1)----外置SD卡文件夹名称不区分大小写 在飞鱼相册发布的第一个测试版中,很多用户表示无法查看外置SD卡中的照片.乍一听觉得加个外置SD卡的根目录,然后在扫描所有图片的时候把这个根目 ...

  7. Salesforce的数据权限机制

    本文主要介绍了 Salesforce 对于系统中数据的访问控制是如何设计的,然后也了解了下 Alfresco 和 Oracle VPD 的数据权限机制.希望对一些业务系统的数据权限的访问控制设计能有所 ...

  8. WebService学习--(四)调用第三方提供的webService服务

    互联网上面有很多的免费webService服务,我们可以调用这些免费的WebService服务,将一些其他网站的内容信息集成到我们的Web应用中显示,下面就以获取天气预报数据和查询国内手机号码归属地为 ...

  9. linux 下使用 tc 模拟网络延迟和丢包

    1 模拟延迟传输简介 netem 与 tc: netem 是 Linux 2.6 及以上内核版本提供的一个网络模拟功能模块.该功能模块可以用来在性能良好的局域网中,模拟出复杂的互联网传输性能,诸如低带 ...

  10. IT轮子系列(二)——mvc API 说明文档的自动生成——Swagger的使用(一)

    这篇文章主要介绍如何使用Swashbuckle插件在VS 2013中自动生成MVC API项目的说明文档.为了更好说明的swagger生成,我们从新建一个空API项目开始. 第一步.新建mvc api ...