歡迎光臨
每天分享高質量文章

MyBastis 三種批次插入方式的效能比較

點選上方“芋道原始碼”,選擇“置頂公眾號”

技術文章第一時間送達!

原始碼精品專欄

 

來源:http://t.cn/E70PpTf

資料庫使用的是sqlserver,JDK版本1.8,執行在SpringBoot環境下

對比3種可用的方式

1、反覆執行單條插入陳述句
2、xml拼接sql
3、批處理執行

先說結論:少量插入請使用反覆插入單條資料,方便。數量較多請使用批處理方式。(可以考慮以有需求的插入資料量20條左右為界吧,在我的測試和資料庫環境下耗時都是百毫秒級的,方便最重要)。無論何時都不用xml拼接sql的方式

程式碼

拼接SQL的xml

newId()是sqlserver生成UUID的函式,與本文內容無關

<insert id="insertByBatch" parameterType="java.util.List">
    INSERT INTO tb_item VALUES
    <foreach collection="list" item="item" index="index" separator=",">
        (newId(),#{item.uniqueCode},#{item.projectId},#{item.name},#{item.type},#{item.packageUnique},
        #{item.isPackage},#{item.factoryId},#{item.projectName},#{item.spec},#{item.length},#{item.weight},
        #{item.material},#{item.setupPosition},#{item.areaPosition},#{item.bottomHeight},#{item.topHeight},
        #{item.serialNumber},#{item.createTime}foreach>


insert>

Mapper介面

Mapper 是 mybatis外掛tk.Mapper 的介面,與本文內容關係不大

public interface ItemMapper extends Mapper<Item{
    int insertByBatch(List itemList);
}

Service類

@Service
public class ItemService {
    @Autowired
    private ItemMapper itemMapper;
    @Autowired
    private SqlSessionFactory sqlSessionFactory;
    //批處理
    @Transactional
    public void add(List itemList) {
        SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH,false);
        ItemMapper mapper = session.getMapper(ItemMapper.class);
        for (int i = 0; i             mapper.insertSelective(itemList.get(i));
            if(i%1000==999){//每1000條提交一次防止記憶體上限溢位
                session.commit();
                session.clearCache();
            }
        }
        session.commit();
        session.clearCache();
    }
    //拼接sql
    @Transactional
    public void add1(List itemList) {
        itemList.insertByBatch(itemMapper::insertSelective);
    }
    //迴圈插入
    @Transactional
    public void add2(List itemList) {
        itemList.forEach(itemMapper::insertSelective);
    }
}

測試類

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = ApplicationBoot.class)
public class ItemServiceTest {
    @Autowired
    ItemService itemService;

    private List itemList = new ArrayList<>();
    //生成測試List
    @Before 
    public void createList(){
        String json ="{\n" +
                "        \"areaPosition\": \"TEST\",\n" +
                "        \"bottomHeight\": 5,\n" +
                "        \"factoryId\": \"0\",\n" +
                "        \"length\": 233.233,\n" +
                "        \"material\": \"Q345B\",\n" +
                "        \"name\": \"TEST\",\n" +
                "        \"package\": false,\n" +
                "        \"packageUnique\": \"45f8a0ba0bf048839df85f32ebe5bb81\",\n" +
                "        \"projectId\": \"094b5eb5e0384bb1aaa822880a428b6d\",\n" +
                "        \"projectName\": \"專案_TEST1\",\n" +
                "        \"serialNumber\": \"1/2\",\n" +
                "        \"setupPosition\": \"1B柱\",\n" +
                "        \"spec\": \"200X200X200\",\n" +
                "        \"topHeight\": 10,\n" +
                "        \"type\": \"Steel\",\n" +
                "        \"uniqueCode\": \"12344312\",\n" +
                "        \"weight\": 100\n" +
                "    }";
        Item test1 = JSON.parseObject(json,Item.class);
        test1.setCreateTime(new Date());
        for (int i = 0; i 1000; i++) {//測試會修改此數量
            itemList.add(test1);
        }
    }
     //批處理
    @Test
    @Transactional
    public void tesInsert() {
        itemService.add(itemList);
    }
    //拼接字串
    @Test
    @Transactional
    public void testInsert1(){
        itemService.add1(itemList);
    }
    //迴圈插入
    @Test
    @Transactional
    public void testInsert2(){
        itemService.add2(itemList);
    }
}

測試結果:

10條 25條資料插入經多次測試,波動性較大,但基本都在百毫秒級別

方式 50條 100條 500條 1000條
批處理 159ms 208ms 305ms 432ms
xml拼接sql 208ms 232ms 報錯 報錯
反覆單條插入 1013ms 2266ms 8141ms 18861ms

其中 拼接sql方式在插入500條和1000條時報錯(似乎是因為sql陳述句過長,此條跟資料庫型別有關,未做其他資料庫的測試):

com.microsoft.sqlserver.jdbc.SQLServerException: 傳入的表格格式資料流(TDS)遠端過程呼叫(RPC)協議流不正確。此 RPC 請求中提供了過多的引數。最多應為 2100

可以發現

1、迴圈插入的時間複雜度是 O(n),並且常數C很大
2、拼接SQL插入的時間複雜度(應該)是 O(logn),但是成功完成次數不多,不確定
3、批處理的效率的時間複雜度是 O(logn),並且常數C也比較小

結論

迴圈插入單條資料雖然效率極低,但是程式碼量極少,在使用tk.Mapper的外掛情況下,僅需程式碼,:

@Transactional
public void add1(List itemList) {
    itemList.forEach(itemMapper::insertSelective);
}

因此,在需求插入資料數量不多的情況下肯定用它了。

xml拼接sql是最不推薦的方式,使用時有大段的xml和sql陳述句要寫,很容易出錯,工作效率很低。更關鍵點是,雖然效率尚可,但是真正需要效率的時候你掛了,要你何用?

批處理執行是有大資料量插入時推薦的做法,使用起來也比較方便。



如果你對 Dubbo / Netty 等等原始碼與原理感興趣,歡迎加入我的知識星球一起交流。長按下方二維碼噢

目前在知識星球更新了《Dubbo 原始碼解析》目錄如下:

01. 除錯環境搭建
02. 專案結構一覽
03. 配置 Configuration
04. 核心流程一覽

05. 拓展機制 SPI

06. 執行緒池

07. 服務暴露 Export

08. 服務取用 Refer

09. 註冊中心 Registry

10. 動態編譯 Compile

11. 動態代理 Proxy

12. 服務呼叫 Invoke

13. 呼叫特性 

14. 過濾器 Filter

15. NIO 伺服器

16. P2P 伺服器

17. HTTP 伺服器

18. 序列化 Serialization

19. 叢集容錯 Cluster

20. 優雅停機

21. 日誌適配

22. 狀態檢查

23. 監控中心 Monitor

24. 管理中心 Admin

25. 運維命令 QOS

26. 鏈路追蹤 Tracing

… 一共 69+ 篇

目前在知識星球更新了《Netty 原始碼解析》目錄如下:

01. 除錯環境搭建
02. NIO 基礎
03. Netty 簡介
04. 啟動 Bootstrap

05. 事件輪詢 EventLoop

06. 通道管道 ChannelPipeline

07. 通道 Channel

08. 位元組緩衝區 ByteBuf

09. 通道處理器 ChannelHandler

10. 編解碼 Codec

11. 工具類 Util

… 一共 61+ 篇

目前在知識星球更新了《資料庫物體設計》目錄如下:


01. 商品模組
02. 交易模組
03. 營銷模組
04. 公用模組

… 一共 17+ 篇


目前在知識星球更新了《Spring 原始碼解析》目錄如下:


01. 除錯環境搭建
02. IoC Resource 定位
03. IoC BeanDefinition 載入

04. IoC BeanDefinition 註冊

05. IoC Bean 獲取

06. IoC Bean 生命週期

… 一共 35+ 篇


原始碼不易↓↓↓

點贊支援老艿艿↓↓

贊(0)

分享創造快樂