1. 程式人生 > 其它 >MySQL匯入大量資料探討

MySQL匯入大量資料探討

  筆者想進行資料庫查詢優化探索,但是前提是需要一個很大的表,因此得先匯入大量資料至一張表中。

準備工作

準備一張表,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問題:

這個問題後續在做探討。