MySQL匯入大量資料探討
阿新 • • 發佈:2021-08-23
筆者想進行資料庫查詢優化探索,但是前提是需要一個很大的表,因此得先匯入大量資料至一張表中。
準備工作
準備一張表,id為主鍵且自增:
方案一
首先我想到的方案就是通過for迴圈插入
xml檔案:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zy.route.mapper.BigBiaoMapper"> <resultMap id="BaseResultMap" type="com.zy.route.DO.BigBiao"> <id column="id" property="id"/> <result column="menu" property="menu"/> <result column="operation" property="operation"/> <result column="uri" property="uri"/> <result column="msg" property="msg"/> <result column="creator" property="creator"/> </resultMap> <sql id="tableName"> bigbiao </sql> <sql id="BaseColumn"> `id`, `menu`, `operation`, `uri`, `msg`,`creator` </sql> <sql id="set"> <if test="id != null"> `id` = #{id}, </if> <if test="menu != null"> `menu` = #{menu}, </if> <if test="operation != null"> `operation` = #{operation}, </if> <if test="uri != null"> `uri` = #{uri}, </if> <if test="msg != null"> `msg` = #{msg}, </if> <if test="creator != null"> `creator` = #{creator}, </if> </sql> <insert id="insertIntoBiao" parameterType="com.zy.route.DO.BigBiao"> insert <include refid="tableName"/> <set> <include refid="set"/> </set> </insert> </mapper>
這裡我就直接使用SpringBoot的測試類進行插入操作
@SpringBootTest @RunWith(SpringRunner.class) public class BigBiaoMapperTest { @Autowired private BigBiaoMapper bigBiaoMapper; @Test public void insert() { BigBiao bigBiao = new BigBiao() .setMenu("專案資料") .setOperation("查詢專案目錄") .setUri("/project/big/biao") .setMsg("{\"method\":\"Get\"\",\"\"costTime:95\"\",\"\"ip:255.255.255.0\"}") .setCreator("LonZyuan"); long start = System.currentTimeMillis(); for (int i = 0; i < 1000; i++) { bigBiaoMapper.insertIntoBiao(bigBiao); } long end = System.currentTimeMillis(); System.out.println("執行時間:" + (end - start) + "ms"); } }
執行,檢視執行時間:
可以發現,就單單1000條資料,就花了29s多
原因分析(參考MySQL45講)
首先我們需要了解一條SQL更新語句是如何執行的:
而我方案一中的操作就是,通過for迴圈,進行了1000次的客戶端與資料庫的開閉連線,然後每次就寫入一條資料,
並且由於表更新後,與該表相關的查詢快取會失效,查詢快取也沒用,因此低效是必然的。
方案二
既然一條條插入很慢,那我通過List一次性插入多個不就行了。
xml語句:
<insert id="batchInsert"> insert into bigbiao (`menu`, `operation`, `uri`, `msg`,`creator`) values <foreach collection="list" item="item" separator=","> (#{item.menu},#{item.operation},#{item.uri},#{item.msg},#{item.creator}) </foreach> </insert>
測試類:
@SpringBootTest @RunWith(SpringRunner.class) public class BigBiaoMapperTest { @Autowired private BigBiaoMapper bigBiaoMapper; @Test public void batchInsert() { BigBiao bigBiao = new BigBiao() .setMenu("批量新增2號") .setOperation("batchAdd") .setUri("/begin/batch/add/big/biao") .setMsg("{\"method:Insert\",\"costTime:88\",\"ip:0.0.0.255\",\"新增一堆東西\"}") .setCreator("LonZyuan"); List<BigBiao> bigBiaos = new ArrayList<>(); long start = System.currentTimeMillis(); for (int i = 0; i < 1000; i++) { bigBiaos.add(bigBiao); } bigBiaoMapper.batchInsert(bigBiaos); long end = System.currentTimeMillis(); System.out.println("執行時間:" + (end - start) + "ms"); } }
執行,檢視執行時間:
速度很快,那我現在插10w條進去:
@Test public void batchInsert() { BigBiao bigBiao = new BigBiao() .setMenu("批量新增2號") .setOperation("batchAdd") .setUri("/begin/batch/add/big/biao") .setMsg("{\"method:Insert\",\"costTime:88\",\"ip:0.0.0.255\",\"新增一堆東西\"}") .setCreator("LonZyuan"); List<BigBiao> bigBiaos = new ArrayList<>(); long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { bigBiaos.add(bigBiao); } bigBiaoMapper.batchInsert(bigBiaos); long end = System.currentTimeMillis(); System.out.println("執行時間:" + (end - start) + "ms"); }
但是又翻車了:
查一下Packet for query is too large,原來MySQL會根據配置檔案,限制Server接受資料包的大小,
圖中(16500100 > 4194304)就是具體問題點,4194304 B = 4MB,所以是這個值小了。
解決:
這個引數為 max_allowed_packet ,在 ini 配置檔案中設定一下:
max_allowed_packet = 20M
重啟MySQL,然後再次執行:
10w條資料插入成功了
這裡可能有小夥伴想問那我直接插1000w條進去,那就會發生OOM問題:
這個問題後續在做探討。