MySql 的批量操作,要加rewriteBatchedStatements参数

本文通过测试环境,对比了MySQL和Oracle在非批量和批量插入10万条记录时的性能表现,展示了MySQL的JDBC驱动在不同版本下的性能差异,并提供了测试代码和详细结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

--------------------------------结论---------------------------------

MySql   非批量10万条记录,                5700条/秒

MySql   批量(batch)10万条记录,       62500条/秒

oracle   非批量插入10万条记录,            4464条/秒

oracle   批量(batch)插入10万条记录,   27778条/秒

 

注:以上测试都是在插入10万条数据完成之后,一次性提交事务(对性能影响很大,占了很大便宜)。

另有一篇文章,说明提交事务的次数对insert性能的影响:《MySql 插入(insert)性能测试》 

 

 

 

MySql的JDBC连接的url中要加rewriteBatchedStatements参数,并保证5.1.13以上版本的驱动,才能实现高性能的批量插入。

例如:String connectionUrl="jdbc:mysql://192.168.1.100:3306/test?rewriteBatchedStatements=true"

还要保证Mysql JDBC驱的版本。MySql的JDBC驱动的批量插入操作性能是很优秀的。

 

 

 

Mysql JDBC驱动,各个版本测试结果:

MySql JDBC 驱动版本号       插入10万条数据用时
5.0.8加了rewriteBatchedStatements参数,没有提高还是17.4秒
5.1.7加了rewriteBatchedStatements参数,没有提高还是17.4秒
5.1.13加了rewriteBatchedStatements参数,插入速度提高到1.6秒

 

 

 

关于rewriteBatchedStatements参数,Mysql官方的说明:

Should the driver use multiqueries (irregardless of the setting of "allowMultiQueries") as well as rewriting of prepared statements for INSERT into multi-value inserts when executeBatch() is called? Notice that this has the potential for SQL injection if using plain java.sql.Statements and your code doesn't sanitize input correctly. Notice that for prepared statements, server-side prepared statements can not currently take advantage of this rewrite option, and that if you don't specify stream lengths when using PreparedStatement.set*Stream(), the driver won't be able to determine the optimum number of parameters per batch and you might receive an error from the driver that the resultant packet is too large. Statement.getGeneratedKeys() for these rewritten statements only works when the entire batch includes INSERT statements.

 

请参见:http://dev.mysql.com/doc/refman/5.1/en/connector-j-reference-configuration-properties.html

 

-------------------------事情的起因---------------------------------


原贴是《使用DBCP 数据库连接池遇到的两个比较怀疑的问题》
http://www.iteye.com/topic/768416

帖子是问连接池问题的,主要是使用连接池向MySql插入大量数据的事儿,有很多javaEye的朋友让作者使用JDBC的批量操作来提高大量插入数据的性能。

mercyblitz 写道
elf8848 写道
前面有多位朋友提出,让楼主使用 jdbc的批量操作,就是PreparedStatement 类上的addBatch(),executeBatch()方法。

在这里要提醒一下大家,MySql的JDBC驱动,是不支持批量操作的,就算你在代码中调用了批量操作的方法,MySql的JDBC驱动,也是按一般insert操作来处理的。

同样Fetch Size特性MySql的JDBC驱动也不支持。而Oracle的JDBC驱动是都支持的。

楼主使用的是Mysql数据库, 不要指望通过批处理来提高 性能了。


请不要想当然,建议你去看一下MySQL JDBC的源代码!

MySQL JDBC驱动在发送命令是,都是传递一个数组的String类型,然后转化为一个二维byte数组。

如果是一条的Statement的话,这个String数组只有一个元素,如果是Batch的话,则有相应个元素。

最后发送IO命令。不清楚你的结论是哪里来的?


我在这里重复的提醒大家:
MySql的JDBC驱动,不是真正支持批量操作的,就算你在代码中调用了批量操作的方法,MySql的JDBC驱动也是按照一般操作来处理的。
这不是什么重大发现,也不是什么新消息,是老调重弹,如果你使用Mysql数据库不要寄希望于通过JDBC批量操作来提高大量插入数据的性能,起码目前的MySql的JDBC驱动是这样的。

 

 

---------------------------------测试环境---------------------------------

 

测试机:笔记本电脑ThinkPad T400

操作系统:Windows XP 

CPU:P8600 2.4G

内存:2G 

IP:192.168.10.124

 

数据库服务器:笔记本ThinkPad T400

操作系统:虚拟机Windows 2003 

内存:操作系统分配了500M (真可怜啊)

IP:192.168.10.139

 

数据库服务器上安装有MySql5.0.18 ,Oracle10G,都是默认安装。

 

JDBC驱动版本: 

MySql : mysql-connector-java-5.0.8 

Oracle : ojdbc6.jar  (之前使用ojdbc14.jar批量插入10万条,实际只插入了3万多条,其它的丢失了,换ojdbc6.jar后,一次commit插入100万条也没有问题)

 

表只有两个字段:

id int

uname varchar(10)

innoDB引擎

 

 

 

---------------------------------测试结果:---------------------------------

 

 mysql非批量插入10万条记录

 mysql批量插入10万条记录

(JDBC的URL中未加参数)

oracle非批量插入10万条记录 oracle批量插入10万条记录  
第1次 17437 ms 17437 ms 22391 ms 360 ms
第2次 17422 ms 17562 ms 22297 ms 328 ms
第3次 17046 ms 17140 ms 22703 ms 359 ms 


这里通过一个点也可以看出来Mysql批量与非批量性能是一样。

oracle的JDBC实现的批量操作的性能十分优秀。

 

---------------------------------测试方法:---------------------------------

 

下面是测试代码:

Java代码   收藏代码
  1. package jdbc2;  
  2. import java.sql.Connection;  
  3. import java.sql.DriverManager;  
  4. import java.sql.PreparedStatement;  
  5. import java.sql.SQLException;  
  6.   
  7. public class Mysql {  
  8.     public static void main(String[] args) {  
  9.         test_mysql();  
  10.         //test_mysql_batch();  
  11.         //test_oracle();  
  12.         //test_oracle_batch();  
  13.     }  
  14.     /** 
  15.      * mysql非批量插入10万条记录 
  16.      * 第1次:17437 ms 
  17.      * 第2次:17422 ms 
  18.      * 第3次:17046 ms 
  19.      */  
  20.     public static void test_mysql(){  
  21.         String url="jdbc:mysql://192.168.10.139:3306/test";  
  22.         String userName="root";  
  23.         String password="1234";  
  24.         Connection conn=null;  
  25.         try {        
  26.               Class.forName("com.mysql.jdbc.Driver");        
  27.               conn =  DriverManager.getConnection(url, userName, password);        
  28.               conn.setAutoCommit(false);        
  29.               String sql = "insert into t_user(id,uname) values(?,?)";        
  30.               PreparedStatement prest = conn.prepareStatement(sql);        
  31.               long a=System.currentTimeMillis();  
  32.               for(int x = 0; x < 100000; x++){        
  33.                  prest.setInt(1, x);        
  34.                  prest.setString(2"张三");        
  35.                  prest.execute();  
  36.               }        
  37.               conn.commit();        
  38.               long b=System.currentTimeMillis();  
  39.               System.out.println("MySql非批量插入10万条记录用时"+ (b-a)+" ms");  
  40.         } catch (Exception ex) {  
  41.             ex.printStackTrace();  
  42.         }finally{  
  43.             try {  
  44.                 if(conn!=null)conn.close();  
  45.             } catch (SQLException e) {  
  46.                 e.printStackTrace();  
  47.             }     
  48.         }  
  49.     }  
  50.     /** 
  51.      * mysql批量插入10万条记录(未加rewriteBatchedStatements参数)   ,加了参数后是1600ms左右 
  52.      * 第1次:17437 ms 
  53.      * 第2次:17562 ms 
  54.      * 第3次:17140 ms 
  55.      */  
  56.     public static void test_mysql_batch(){  
  57.         //String url="jdbc:mysql://192.168.10.139:3306/test";  
  58. <p style="margin: 0.0px 0.0px 0.0px 0.0px; color: #3b3df5;"><span style="font-family: Verdana,Arial,Helvetica,sans-serif; color: #000000;">     String url="jdbc:mysql://127.0.0.1:3306/dev?rewriteBatchedStatements=true";</span>  
  59.   
  60.   
  61. </p>  
  62.   
  63.   
  64.         String userName="root";  
  65.         String password="1234";  
  66.         Connection conn=null;  
  67.         try {        
  68.             Class.forName("com.mysql.jdbc.Driver");        
  69.             conn =  DriverManager.getConnection(url, userName, password);        
  70.             conn.setAutoCommit(false);        
  71.             String sql = "insert into t_user(id,uname) values(?,?)";        
  72.             PreparedStatement prest = conn.prepareStatement(sql);        
  73.             long a=System.currentTimeMillis();  
  74.             for(int x = 0; x < 100000; x++){        
  75.                 prest.setInt(1, x);        
  76.                 prest.setString(2"张三");        
  77.                 prest.addBatch();      
  78.             }        
  79.             prest.executeBatch();        
  80.             conn.commit();        
  81.             long b=System.currentTimeMillis();  
  82.             System.out.println("MySql批量插入10万条记录用时"+ (b-a)+" ms");  
  83.         } catch (Exception ex) {  
  84.             ex.printStackTrace();  
  85.         }finally{  
  86.             try {  
  87.                 if(conn!=null)conn.close();  
  88.             } catch (SQLException e) {  
  89.                 e.printStackTrace();  
  90.             }     
  91.         }  
  92.     }  
  93.     /** 
  94.      * oracle非批量插入10万条记录 
  95.      * 第1次:22391 ms 
  96.      * 第2次:22297 ms 
  97.      * 第3次:22703 ms 
  98.      */  
  99.     public static void test_oracle(){  
  100.         String url="jdbc:oracle:thin:@192.168.10.139:1521:orcl";  
  101.         String userName="scott";  
  102.         String password="tiger";      
  103.         Connection conn=null;  
  104.         try {        
  105.             Class.forName("oracle.jdbc.OracleDriver");        
  106.             conn =  DriverManager.getConnection(url, userName, password);        
  107.             conn.setAutoCommit(false);        
  108.             String sql = "insert into t_user(id,uname) values(?,?)";        
  109.             PreparedStatement prest = conn.prepareStatement(sql);        
  110.             long a=System.currentTimeMillis();  
  111.             for(int x = 0; x < 100000; x++){        
  112.                 prest.setInt(1, x);        
  113.                 prest.setString(2"张三");        
  114.                 prest.execute();  
  115.             }  
  116.             conn.commit();        
  117.             long b=System.currentTimeMillis();  
  118.             System.out.println("Oracle非批量插入10万记录用时"+ (b-a)+" ms");  
  119.             conn.close();      
  120.         } catch (Exception ex) {  
  121.             ex.printStackTrace();  
  122.         }finally{  
  123.             try {  
  124.                 if(conn!=null)conn.close();  
  125.             } catch (SQLException e) {  
  126.                 e.printStackTrace();  
  127.             }     
  128.         }  
  129.     }  
  130.     /** 
  131.      * oracle批量插入10万条记录 
  132.      * 第1次:360 ms 
  133.      * 第2次:328 ms 
  134.      * 第3次:359 ms 
  135.      */  
  136.     public static void test_oracle_batch(){  
  137.         String url="jdbc:oracle:thin:@192.168.10.139:1521:orcl";  
  138.         String userName="scott";  
  139.         String password="tiger";      
  140.         Connection conn=null;  
  141.         try {        
  142.             Class.forName("oracle.jdbc.OracleDriver");        
  143.             conn =  DriverManager.getConnection(url, userName, password);        
  144.             conn.setAutoCommit(false);        
  145.             String sql = "insert into t_user(id,uname) values(?,?)";        
  146.             PreparedStatement prest = conn.prepareStatement(sql);        
  147.             long a=System.currentTimeMillis();  
  148.             for(int x = 0; x < 100000; x++){        
  149.                 prest.setInt(1, x);        
  150.                 prest.setString(2"张三");        
  151.                 prest.addBatch();  
  152.             }  
  153.             prest.executeBatch();        
  154.             conn.commit();        
  155.             long b=System.currentTimeMillis();  
  156.             System.out.println("Oracle批量插入10万记录用时"+ (b-a)+" ms");  
  157.             conn.close();      
  158.         } catch (Exception ex) {  
  159.             ex.printStackTrace();  
  160.         }finally{  
  161.             try {  
  162.                 if(conn!=null)conn.close();  
  163.             } catch (SQLException e) {  
  164.                 e.printStackTrace();  
  165.             }     
  166.         }  
  167.     }  

<think>好的,我现在要帮用户解决在MySQL中实现批量修改数据的问题。用户已经提供了一些相关引用,我需要仔细看看这些引用内容,然后整理出合适的回答。 首先,引用1提到了MySQL批量修改原生SQL的流程,但具体代码示例可能没有详细给出。引用2讨论了批量插入的方法,比如使用`addBatch`和`executeBatch`,以及MyBatis-Plus的批量处理,默认1000条一批次。引用3则提到MyBatis传入List进行批量更新,需要在数据库连接URL中添`allowMultiQueries=true`参数。引用4涉及游标和InnoDB的事务支持,可能和批量操作的事务处理有关。 接下来,我需要确定用户的需求是批量更新,而不仅仅是插入。常见的批量更新方法有几种:使用CASE WHEN语句、使用临时表、使用INSERT ... ON DUPLICATE KEY UPDATE,或者结合编程语言的批处理功能,比如JDBC的批处理。 根据引用2和3,使用JDBC的批处理或者MyBatis-Plus的批量操作是比较高效的方法,特别是处理大量数据时。而使用CASE WHEN可以在一条SQL中更新多条记录,但需要注意SQL长度限制。引用3提到的allowMultiQueries参数可能在使用多条UPDATE语句时是必需的。 另外,事务处理也很重要,引用4提到InnoDB支持事务,确保批量操作的原子性。所以,在代码示例中应该包含事务的控制。 现在需要将这些方法整理成步骤,并给出对应的示例。例如,CASE WHEN的方法需要构建一个包含多个WHEN条件的UPDATE语句;临时表方法需要创建临时表,然后通过JOIN来更新;JDBC批处理则需要循环添参数,最后执行批处理。 同时要注意用户提到的示例代码,可能需要展示不同方法的SQL语句和编程层面的实现,比如Java的JDBC或MyBatis的示例。此外,引用3中的MyBatis示例可能需要配置allowMultiQueries,这也是需要注意的地方。 最后,要确保回答结构清晰,分点说明,并给出相关问题,帮助用户进一步了解相关内容。比如性能优化、事务处理、不同方法的适用场景等。</think>### 逐步解答:MySQL 批量修改数据的方法 #### 方法1:使用 `CASE WHEN` 动态更新 通过构建动态条件实现单条SQL批量更新,适用于数据量较小的情况。 ```sql UPDATE user SET age = CASE id WHEN 1 THEN 20 WHEN 2 THEN 25 WHEN 3 THEN 30 END, name = CASE id WHEN 1 THEN 'Tom' WHEN 2 THEN 'Jerry' WHEN 3 THEN 'Spike' END WHERE id IN (1,2,3); ``` **说明**:通过`CASE`为不同`id`设置不同字段值,需明确主键或唯一标识。 --- #### 方法2:临时表关联更新 通过创建临时表存储批量数据,再关联更新目标表,适合数据量较大的场景。 ```sql -- 创建临时表 CREATE TEMPORARY TABLE tmp_user (id INT, age INT, name VARCHAR(50)); -- 插入临时数据 INSERT INTO tmp_user VALUES (1,20,'Tom'), (2,25,'Jerry'), (3,30,'Spike'); -- 关联更新 UPDATE user u JOIN tmp_user t ON u.id = t.id SET u.age = t.age, u.name = t.name; ``` **优点**:避免单条SQL过长,提升可维护性。 --- #### 方法3:`INSERT ... ON DUPLICATE KEY UPDATE` 若需批量插入或更新,可利用唯一键约束实现。 ```sql INSERT INTO user (id, age, name) VALUES (1,20,'Tom'), (2,25,'Jerry'), (3,30,'Spike') ON DUPLICATE KEY UPDATE age = VALUES(age), name = VALUES(name); ``` **要求**:表中需有唯一索引(如`id`为主键)。 --- #### 方法4:编程语言批处理(以Java为例) 通过JDBC批处理减少网络IO,提升效率[^2]。 ```java Connection conn = DriverManager.getConnection(url); conn.setAutoCommit(false); // 关闭自动提交 PreparedStatement ps = conn.prepareStatement( "UPDATE user SET age=?, name=? WHERE id=?" ); for (User user : userList) { ps.setInt(1, user.getAge()); ps.setString(2, user.getName()); ps.setInt(3, user.getId()); ps.addBatch(); // 添到批处理 } ps.executeBatch(); // 批量执行 conn.commit(); // 提交事务 ``` **优化点**:设置`rewriteBatchedStatements=true`参数速批处理[^2]。 --- #### 方法5:MyBatis 批量更新(需配置参数) 在MyBatis中实现批量更新需配置`allowMultiQueries=true`[^3]。 ```xml <update id="batchUpdate"> <foreach collection="list" item="item" separator=";"> UPDATE user SET age=#{item.age}, name=#{item.name} WHERE id=#{item.id} </foreach> </update> ``` **注意**:需在数据库连接URL中添参数: ```properties jdbc:mysql://host:port/db?allowMultiQueries=true ``` --- ### 性能优化建议 1. **事务控制**:批量操作时显式开启事务,减少事务提交次数[^4]。 2. **分批处理**:每批次处理1000-5000条数据,避免内存溢出[^2]。 3. **索引优化**:确保`WHERE`条件字段有索引,但批量更新期间可暂时移除非必要索引。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值