MySQL的批量更新和批量新增優(yōu)化方式
MySQL的批量更新和批量插入優(yōu)化
如果需要批量插入和批量更新操作就需要進(jìn)行sql 的優(yōu)化,否則近30萬條數(shù)據(jù)的插入或更新就會耗費(fèi)幾分鐘甚至更多的時間, 此文僅批量插入和批量更新的幾種優(yōu)化。
- 批量插入篇(使用多條
insert
語句、使用union all
創(chuàng)建臨時表、使用多個values); - 批量更新篇(使用多條
update
語句、使用union all
創(chuàng)建臨時表創(chuàng)建臨時表、使用replace into
、使用insert ... on duplicate key ... update...
)。
如果有需要的同僚可根據(jù)下列內(nèi)容使用jdbcTemplate和Java反射技術(shù)將其封裝。
特別提示:做批量操作時,請限制每次1000-2000條數(shù)據(jù),以避免GC和OOM。后期也會貼出相關(guān)代碼,歡迎指正優(yōu)化或提供其它更好的方法。
批量插入篇
1. 多條insert語句(快)
實(shí)測:50*6500行數(shù)據(jù)耗時8-12秒,如果不是手動提交事務(wù),耗時約70-180秒
類型: insert into table_name(id,name,title) values(?, ?, ?);
常用的插入操作就是批量執(zhí)行1條insert類型的SQL語句,這樣的語句在執(zhí)行大量的插入數(shù)據(jù)時, 其效率低下就暴露出來了。
特別注意:jdbc.url需要加上:allowMultiQueries=true
jdbc.url = jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC&allowMultiQueries=true
(1)sql 語句
start transaction; insert into table_name(id, name, title) values(1, '張三', '如何抵擋美食的誘惑?'); insert into table_name(id, name, title) values(2, '李四', '批判張三的《如何抵擋美食的誘惑?》'); insert into table_name(id, name, title) values(3, '王五', '會看鬼子進(jìn)村的那些不堪入目的事兒'); insert into table_name(id, name, title) values(4, '趙柳', 'Java該怎樣高效率學(xué)習(xí)'); commit;
(2)mapper 文件的 sql
<insert id="batchSave" parameterType="java.util.List"> start transaction; <foreach collection="list" index="index" item="item"> insert into table_name(id, name, title) values(#{item.id}, #{item.name}, #{item.title}); </foreach> commit; </insert>
2. 多個values語句(快)
實(shí)測:50*6500行數(shù)據(jù)耗時6至10秒(與服務(wù)器的有關(guān))
類型: insert into table_name(id, name, title) values(?, ?, ?), ..., (?, ?, ?);
(1)sql 語句
insert into table_name(id, name, title) values (1, '張三', '如何抵擋美食的誘惑?'), (2, '李四', '批判張三的《如何抵擋美食的誘惑?》'), (3, '王五', '會看鬼子進(jìn)村的那些不堪入目的事兒'), (4, '趙柳', 'Java該怎樣高效率學(xué)習(xí)');
(2)mapper 文件的 sql
<insert id="batchSave" parameterType="java.util.List"> insert into table_name(id, name, title) values <foreach collection="list" index="index" item="item" separator=", "> (#{item.id}, #{item.name}, #{item.title}) </foreach> </insert>
3. 使用union all 創(chuàng)建臨時表(快)
實(shí)測:50*6500行數(shù)據(jù)耗時6至10秒(與服務(wù)器的有關(guān))
類型: insert into table_name(id,name,title) select ?, ?, ? union all select ?, ?, ? union all ...
union all 在這里select ?, ?, ? union all select ?, ?, ? union all ...
是創(chuàng)建臨時表的原理,先創(chuàng)建整張臨時表到內(nèi)存中,然后將整張臨時表導(dǎo)入數(shù)據(jù)庫,連接關(guān)閉時即銷毀臨時表,其他的不多說,可自行了解。
(1)sql 語句
insert into table_name(id, name, title) select 1, '張三', '如何抵擋美食的誘惑?' union all select 2, '李四', '批判張三的《如何抵擋美食的誘惑?》' union all select 3, '王五', '會看鬼子進(jìn)村的那些不堪入目的事兒' union all select 4, '趙柳', 'Java該怎樣高效率學(xué)習(xí)';
a. 創(chuàng)建臨時表方式1 - 使用 temporary + union all
簡單列舉三種創(chuàng)建臨時表的方式, 這里熟悉了temporary
、 select ?, ? ,? union all select ?, ?, ?
和 、insert into ... values(?, ?, ?), (?, ?, ?), (?, ?, ?)...
之后,都可以組合創(chuàng)建臨時表, 效率幾乎差不多。個人更加偏向第二種,因?yàn)楹唵畏奖恪?/p>
create temporary table tmp(id int(4) primary key,name varchar(50),title varchar(50)); SELECT id, name, title FROM tmp union all select 1, '張三', '如何抵擋美食的誘惑?' union all select 2, '李四', '批判張三的《如何抵擋美食的誘惑?》' union all select 3, '王五', '會看鬼子進(jìn)村的那些不堪入目的事兒' union all select 4, '趙柳', 'Java該怎樣高效率學(xué)習(xí)';
b. 創(chuàng)建臨時表方式2 - 使用 select + union all
select id, name, title from table_name where id = -1 union all select 1, '張三', '如何抵擋美食的誘惑?' union all select 2, '李四', '批判張三的《如何抵擋美食的誘惑?》' union all select 3, '王五', '會看鬼子進(jìn)村的那些不堪入目的事兒' union all select 4, '趙柳', 'Java該怎樣高效率學(xué)習(xí)';
c. 創(chuàng)建臨時表方式3 - 使用 temporary + 多個insert values
create temporary table tmp(id int(4) primary key,name varchar(50),title varchar(50)); insert into tmp(id, name, title) values (1, '張三', '如何抵擋美食的誘惑?'), (2, '李四', '批判張三的《如何抵擋美食的誘惑?》'), (3, '王五', '會看鬼子進(jìn)村的那些不堪入目的事兒'), (4, '趙柳', 'Java該怎樣高效率學(xué)習(xí)');
(2)mapper 文件的 sql
<insert id="batchSave" parameterType="java.util.List"> insert into table_name(id, name, title) <foreach collection="list" index="index" item="item" separator=" union all "> select #{item.id}, #{item.name}, #{item.title} </foreach> </insert>
批量更新篇
1. 多條update語句批量更新(快)
實(shí)測:50*6500行數(shù)據(jù)耗時26-30秒,如果不是手動提交事務(wù),耗時約70-180秒
類型: update table_name set name = ?, title = ? where id = ?;
由于批量更新存在條件判斷,所以整體上時效上沒有批量插入那么高(下面是手動提交事務(wù)的代碼)。
特別注意:jdbc.url需要加上:allowMultiQueries=true
jdbc.url = jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC&allowMultiQueries=true
(1)sql 語句
start transaction; update table_name set name = '張三', title = 'springboot如何入門' where id = 1; update table_name set name = '李四', title = 'JVM到底是怎樣運(yùn)行的' where id = 2; update table_name set name = '王五', title = '并發(fā)編程你需要注意什么' where id = 3; update table_name set name = '趙柳', title = '別讓一時的貪成為你不努力的理由' where id = 4; commit;
(2)mapper 文件的 sql
<update id="batchUpdate" parameterType="java.util.List"> start transaction; <foreach collection="list" index="index" item="item"> update table_name set name = #{item.id}, title = #{item.title} where id = #{item.id}; </foreach> commit; </update >
2. 創(chuàng)建臨時表批量更新(快)
實(shí)測:50*6500行數(shù)據(jù)耗時26至28秒
(1)批量更新(使用 temporary + select … union all … select …創(chuàng)建臨時表)
類型: create temporary table 臨時表; select id, name, title FROM 臨時表 union all select ... union all ... select ...
(A)sql 語句
這里也可以使用 union all 加上 temporary 的方式創(chuàng)建臨時表, 詳情請看批量插入篇的創(chuàng)建臨時表的兩種方式
create temporary table tmp(id int(4) primary key,name varchar(50),title varchar(50)); select id, name, title from tmp union all select 1, '張三', '如何抵擋美食的誘惑?' union all select 2, '李四', '批判張三的《如何抵擋美食的誘惑?》' union all select 3, '王五', '會看鬼子進(jìn)村的那些不堪入目的事兒' union all select 4, '趙柳', 'Java該怎樣高效率學(xué)習(xí)'; update table_name, tmp set table_name.name=tmp.name, table_name.title= tmp.title where table_name.id = tmp.id;
(B)mapper 文件的 sql
<update id="batchUpdate" parameterType="java.util.List"> create temporary table tmp(id int(4) primary key,name varchar(50),title varchar(50)); update table_name, (SELECT id, name, title FROM tmp union all <foreach collection="list" index="index" item="item" separator=" union all "> select #{item.id}, #{item.name}, #{item.title} </foreach>) as tmp set table_name.name=tmp.name, table_name.title= tmp.title where table_name.id = tmp.id; </insert>
(2)批量更新(使用 temporary + insert into values(…), (…)… 創(chuàng)建臨時表)
類型: create temporary table 臨時表; insert into values(...), (...)...; update ... set ... where ...;
(A)sql 語句
這里也可以使用 union all 加上 temporary 的方式創(chuàng)建臨時表, 詳情請看批量插入篇的創(chuàng)建臨時表的兩種方式
create temporary table tmp(id int(4) primary key,name varchar(50),title varchar(50)); insert into tmp(id, name, title) values (1, '張三', '如何抵擋美食的誘惑?'), (2, '李四', '批判張三的《如何抵擋美食的誘惑?》'), (3, '王五', '會看鬼子進(jìn)村的那些不堪入目的事兒'), (4, '趙柳', 'Java該怎樣高效率學(xué)習(xí)') ; update table_name, tmp set table_name.name=tmp.name, table_name.title= tmp.title where table_name.id = tmp.id;
(B)mapper 文件的 sql
<update id="batchUpdate" parameterType="java.util.List"> create temporary table tmp(id int(4) primary key,name varchar(50),title varchar(50)); insert into tmp(id, name, title) values <foreach collection="list" index="index" item="item" separator=","> (#{item.id}, #{item.name}, #{item.title}) </foreach>; update table_name, tmp set table_name.name=tmp.name, table_name.title= tmp.title where table_name.id = tmp.id; </insert>
(3)批量更新(使用 select … union all… 創(chuàng)建臨時表)
類型: update 表名, (select ... union all ...) as tmp set ... where ...
注意: id=-1為數(shù)據(jù)庫一個不存在的主鍵id
(A)sql 語句
update table_name, (select id, name, title from table_name where id = -1 union all select 1, '張三', '如何抵擋美食的誘惑?' union all select 2, '李四', '批判張三的《如何抵擋美食的誘惑?》' union all select 3, '王五', '會看鬼子進(jìn)村的那些不堪入目的事兒' union all select 4, '趙柳', 'Java該怎樣高效率學(xué)習(xí)') as tmp set table_name.name=tmp.name, table_name.title= tmp.title where table_name.id = tmp.id;
(B)mapper 文件的 sql
<update id="batchUpdate" parameterType="java.util.List"> update table_name, (select id, name, title from table_name where id = -1 union all <foreach collection="list" index="index" item="item" separator=" union all "> select #{item.id}, #{item.name}, #{item.title} </foreach>) as tmp set table_name.name=tmp.name, table_name.title= tmp.title where table_name.id = tmp.id; </insert>
3. replace into …批量更新(快)
實(shí)測:50*6500行數(shù)據(jù)耗時26至28秒
類型: replace into ... values (...),(...),...
(1)sql 語句
replace into table_name(id, name, title) values (1, '張三', '如何抵擋美食的誘惑?'), (2, '李四', '批判張三的《如何抵擋美食的誘惑?》'), (3, '王五', '會看鬼子進(jìn)村的那些不堪入目的事兒'), (4, '趙柳', 'Java該怎樣高效率學(xué)習(xí)') ;
(2)mapper 文件的 sql
<update id="batchUpdate" parameterType="java.util.List"> replace into table_name(id, name, title) values <foreach collection="list" index="index" item="item" separator=","> (#{item.id}, #{item.name}, #{item.title}) </foreach> </update>
4. insert into … on duplicate key … update …批量更新(快)
實(shí)測:50*6500行數(shù)據(jù)批量更新耗時27-29秒, 批量插入耗時9-12秒
類型: insert into ... values (...),(...),...on duplicate key ... update ...
這句類型的SQL在遇到 duplicate key 時執(zhí)行更新操作, 否則執(zhí)行插入操作(時效略微慢一點(diǎn))
(1)sql 語句
insert into table_name(id, name, title) values (1, '張三', '如何抵擋美食的誘惑?'), (2, '李四', '批判張三的《如何抵擋美食的誘惑?》'), (3, '王五', '會看鬼子進(jìn)村的那些不堪入目的事兒'), (4, '趙柳', 'Java該怎樣高效率學(xué)習(xí)') on duplicate key update name=values(name), title=values(title);
(2)mapper 文件的 sql
<update id="batchUpdate" parameterType="java.util.List"> replace into table_name(id, name, title) values <foreach collection="list" index="index" item="item" separator=","> (#{item.id}, #{item.name}, #{item.title}) </foreach> on duplicate key update id= values(id); </update>
總結(jié)
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Mysql復(fù)合主鍵和聯(lián)合主鍵的區(qū)別解析
這篇文章主要介紹了Mysql復(fù)合主鍵和聯(lián)合主鍵的區(qū)別,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04Navicat中導(dǎo)入mysql大數(shù)據(jù)時出錯解決方法
這篇文章主要介紹了Navicat中導(dǎo)入mysql大數(shù)據(jù)時出錯解決方法,需要的朋友可以參考下2017-04-04在MySQL中實(shí)現(xiàn)基于時間點(diǎn)的數(shù)據(jù)恢復(fù)
在MySQL中實(shí)現(xiàn)基于時間點(diǎn)的數(shù)據(jù)恢復(fù)是一個復(fù)雜但可行的過程,主要依賴于MySQL的二進(jìn)制日志(Binary Log),本文介紹了實(shí)現(xiàn)此功能的一般步驟,并有詳細(xì)的代碼供大家參考,需要的朋友可以參考下2024-03-03MySQL與PHP的基礎(chǔ)與應(yīng)用專題之自連接
MySQL是一個關(guān)系型數(shù)據(jù)庫管理系統(tǒng),由瑞典MySQL AB 公司開發(fā),屬于 Oracle 旗下產(chǎn)品。MySQL 是最流行的關(guān)系型數(shù)據(jù)庫管理系統(tǒng)之一,本系列將帶你掌握php與mysql的基礎(chǔ)應(yīng)用,本篇從自連接開始2022-02-02MySQL 可擴(kuò)展設(shè)計(jì)的基本原則
可擴(kuò)展設(shè)計(jì)是一個非常復(fù)雜的系統(tǒng)工程,所涉及的各個方面非常的廣泛,技術(shù)也較為復(fù)雜,可能還會帶來很多其他方面的問題。但不管我們?nèi)绾卧O(shè)計(jì),不管遇到哪些問題,有些原則我們還是必須確保的。2021-05-05mysql技巧:提高插入數(shù)據(jù)(添加記錄)的速度
這篇文章主要介紹了mysql技巧:提高插入數(shù)據(jù)(添加記錄)的速度,需要的朋友可以參考下2014-12-12CentOS 7 下使用yum安裝MySQL5.7.20 最簡單方法
這篇文章主要介紹了CentOS 7 下使用yum安裝MySQL5.7.20 最簡單 方法,需要的朋友可以參考下2018-11-11