一個(gè)小小的批量插入,被面試官追問了六次
嗨,你好呀,我是哪吒。
面試經(jīng)常被問到“MyBatis批量入庫時(shí),xml的foreach和java的foreach,性能上有什么區(qū)別?”。
首先需要明確一點(diǎn),優(yōu)先使用批量插入,而不是在Java中通過循環(huán)單條插入。
很多小伙伴都知道這個(gè)結(jié)論,但是,為啥?很少有人能說出個(gè)所以然來。
就算我不知道,你也不能反反復(fù)復(fù)問我“同一個(gè)問題”吧?
1、MyBatis批量入庫時(shí),xml的foreach和java的foreach,性能上有什么區(qū)別?
批量入庫時(shí),如果通過Java循環(huán)語句一條一條入庫,每一條SQL都需要涉及到一次數(shù)據(jù)庫的操作,包括網(wǎng)絡(luò)IO以及磁盤IO,可想而知,這個(gè)效率是非常低下的。
xml中使用foreach的方式會(huì)一次性發(fā)送給數(shù)據(jù)庫執(zhí)行,只需要進(jìn)行一次網(wǎng)絡(luò)IO,提高了效率。
但是,xml中的foreach可能會(huì)導(dǎo)致內(nèi)存溢出OOM問題,因?yàn)樗鼤?huì)一次性將所有數(shù)據(jù)加載到內(nèi)存中。而java中的foreach可以有效避免這個(gè)問題,因?yàn)樗鼤?huì)分批次處理數(shù)據(jù),每次只處理一部分?jǐn)?shù)據(jù),從而減少內(nèi)存的使用。
如果操作比較復(fù)雜,例如需要進(jìn)行復(fù)雜的計(jì)算或者轉(zhuǎn)換,那么使用java中的foreach可能會(huì)更快,因?yàn)樗梢灾苯永胘ava的強(qiáng)大功能,而不需要通過xml進(jìn)行轉(zhuǎn)換。
孰重孰輕,就需要面試官自己拿捏了~
2、在MyBatis中,對(duì)于<foreach>標(biāo)簽的使用,通常有幾種常見的優(yōu)化方法?
比如避免一次性傳遞過大的數(shù)據(jù)集合到foreach中,可以通過分批次處理數(shù)據(jù)或者在業(yè)務(wù)層先進(jìn)行數(shù)據(jù)過濾和篩選。
預(yù)編譯SQL語句、優(yōu)化SQL語句,減少foreach編譯的工作量。
對(duì)于重復(fù)執(zhí)行的SQL語句,可以利用mybatis的緩存機(jī)制來減少數(shù)據(jù)庫的訪問次數(shù)。
對(duì)于關(guān)聯(lián)查詢,可以考慮使用mybatis的懶加載特性,延遲加載關(guān)聯(lián)數(shù)據(jù),減少一次性加載的數(shù)據(jù)量。
3、MyBatis foreach批量插入會(huì)有什么問題?
foreach在處理大量數(shù)據(jù)時(shí)會(huì)消耗大量內(nèi)存。因?yàn)閒oreach需要將所有要插入的數(shù)據(jù)加載到內(nèi)存中,如果數(shù)據(jù)量過大,可能會(huì)導(dǎo)致內(nèi)存溢出。
有些數(shù)據(jù)庫對(duì)單條SQL語句中可以插入的數(shù)據(jù)量有限制。如果超過這個(gè)限制,foreach生成的批量插入語句將無法執(zhí)行。
使用foreach進(jìn)行批量插入時(shí),需要注意事務(wù)的管理。如果部分插入失敗,可能需要進(jìn)行回滾操作。
foreach會(huì)使SQL語句變得復(fù)雜,可能影響代碼的可讀性和可維護(hù)性。
4、當(dāng)使用foreach進(jìn)行批量插入時(shí),如何處理可能出現(xiàn)的事務(wù)問題?內(nèi)存不足怎么辦?
本質(zhì)上這兩個(gè)是一個(gè)問題,就是SQL執(zhí)行慢,一次性執(zhí)行SQL數(shù)量大的問題。
大多數(shù)數(shù)據(jù)庫都提供了事務(wù)管理功能,可以確保一組操作要么全部成功,要么全部失敗。在執(zhí)行批量插入操作前,開始一個(gè)數(shù)據(jù)庫事務(wù),如果所有插入操作都成功,則提交事務(wù);如果有任何一條插入操作失敗,則回滾事務(wù)。
如果一次插入大量數(shù)據(jù),可以考慮分批插入。這樣,即使某一批插入失敗,也不會(huì)影響到其他批次的插入。
優(yōu)化foreach生成的SQL語句,避免因SQL語句過長或過于復(fù)雜而導(dǎo)致的問題。
比如MySQL的INSERT INTO ... VALUES語法 通常比使用foreach進(jìn)行批量插入更高效,也更可靠。
5、MyBati foreach批量插入時(shí)如何處理死鎖問題?
當(dāng)使用MyBatis的foreach進(jìn)行批量插入時(shí),可能會(huì)遇到死鎖問題。這主要是因?yàn)槎鄠€(gè)事務(wù)同時(shí)嘗試獲取相同的資源(如數(shù)據(jù)庫的行或表),并且每個(gè)事務(wù)都在等待其他事務(wù)釋放資源,從而導(dǎo)致了死鎖。
(1)優(yōu)化SQL語句
確保SQL語句盡可能高效,避免不必要的全表掃描或復(fù)雜的聯(lián)接操作,這可以減少事務(wù)持有鎖的時(shí)間,從而降低死鎖的可能性。
不管遇到什么問題,你就回答優(yōu)化SQL,基本上都沒毛病。
(2)設(shè)置鎖超時(shí)
為事務(wù)設(shè)置一個(gè)合理的鎖超時(shí)時(shí)間,這樣即使發(fā)生死鎖,也不會(huì)導(dǎo)致系統(tǒng)長時(shí)間無響應(yīng)。
(3)使用樂觀鎖
樂觀鎖是一種非阻塞性鎖,它假設(shè)多個(gè)事務(wù)在同一時(shí)間不會(huì)沖突,因此不會(huì)像悲觀鎖那樣在每次訪問數(shù)據(jù)時(shí)都加鎖。樂觀鎖通常用于讀取頻繁、寫入較少的場(chǎng)景。
(4)分批插入
如果一次插入大量數(shù)據(jù),可以考慮分批插入。這樣,即使某一批插入失敗,也不會(huì)影響到其他批次的插入。
(5)調(diào)整事務(wù)隔離級(jí)別
較低的隔離級(jí)別(如READ UNCOMMITTED)可能會(huì)減少死鎖的發(fā)生,但可能會(huì)導(dǎo)致其他問題,如臟讀或不可重復(fù)讀。
6、mybatis foreach批量插入時(shí)如果數(shù)據(jù)庫連接池耗盡,如何處理?
(1)增加最大連接數(shù)
數(shù)據(jù)庫連接池耗盡了,增加最大連接數(shù),這個(gè)回答,沒毛病。
(2)優(yōu)化SQL語句
減少每個(gè)連接的使用時(shí)間,從而減少連接池耗盡的可能性。
萬變不離其宗,優(yōu)化SQL,沒毛病。
(3)分批插入
避免一次性占用過多的連接,從而減少連接池耗盡的可能性。
(4)調(diào)整事務(wù)隔離級(jí)別
降低事務(wù)隔離級(jí)別可以減少每個(gè)事務(wù)持有連接的時(shí)間,從而減少連接池耗盡的可能性。但需要注意,較低的事務(wù)隔離級(jí)別可能會(huì)導(dǎo)致其他問題,如臟讀或不可重復(fù)讀。
(5)使用更高效的批量插入方法
比如MySQL的INSERT INTO ... VALUES語法。這些方法通常比使用foreach進(jìn)行批量插入更高效,也更節(jié)省連接資源。
感覺每道題的答案都是一樣呢?這就對(duì)嘍,數(shù)據(jù)庫連接池耗盡,本質(zhì)問題不就是入庫的速度太慢了嘛。
(6)定期檢查并關(guān)閉空閑時(shí)間過長的連接,以釋放連接資源。
就前面的幾個(gè)問題,做一個(gè)小總結(jié),你會(huì)發(fā)現(xiàn),它們的回答大差不差。
通過現(xiàn)象看本質(zhì),批量插入會(huì)有什么問題?事務(wù)問題?內(nèi)存不足怎么辦?如何處理死鎖問題?數(shù)據(jù)庫連接池耗盡,如何處理?
這些問題的本質(zhì)都是因?yàn)镾QL執(zhí)行慢,一次性SQL數(shù)據(jù)量太大,事務(wù)提交太慢導(dǎo)致的。
回答的核心都是:如何降低單次事務(wù)時(shí)間?
- 優(yōu)化SQL語句
- 分批插入
- 調(diào)整事務(wù)隔離級(jí)別
- 使用更高效的批量插入方法