批处理写入以及动态与参数化SQL,数据库的性能如何?
批處理寫入是最有效的數(shù)據(jù)庫(kù)優(yōu)化之一。 批處理寫入受大多數(shù)現(xiàn)代數(shù)據(jù)庫(kù)和JDBC標(biāo)準(zhǔn)的一部分支持,并且受大多數(shù)JPA提供程序支持。
普通數(shù)據(jù)庫(kù)訪問(wèn)包括在單獨(dú)的數(shù)據(jù)庫(kù)/網(wǎng)絡(luò)訪問(wèn)中將每個(gè)DML(插入,更新,刪除)語(yǔ)句發(fā)送到數(shù)據(jù)庫(kù)。 每個(gè)數(shù)據(jù)庫(kù)訪問(wèn)都有一定的開銷,并且數(shù)據(jù)庫(kù)必須獨(dú)立處理每個(gè)語(yǔ)句。 批處理寫入有兩種形式,動(dòng)態(tài)的和參數(shù)化的。 參數(shù)化是最常見的方法,通常可以帶來(lái)最大的好處,因?yàn)閯?dòng)態(tài)可能存在解析問(wèn)題。
要了解批處理編寫,您必須首先了解參數(shù)化的SQL。 SQL執(zhí)行由兩部分組成,即解析和執(zhí)行。 解析包括將字符串SQL表示形式轉(zhuǎn)換為數(shù)據(jù)庫(kù)表示形式。 執(zhí)行包括在數(shù)據(jù)庫(kù)上執(zhí)行已解析的SQL。 數(shù)據(jù)庫(kù)和JDBC支持綁定參數(shù),因此SQL(數(shù)據(jù))的參數(shù)不必嵌入到SQL中。 這避免了將數(shù)據(jù)轉(zhuǎn)換為文本的成本,并允許重復(fù)執(zhí)行同一SQL語(yǔ)句并執(zhí)行多次。 這允許單個(gè)解析和多個(gè)執(zhí)行,也稱為“參數(shù)化SQL”。 大多數(shù)JDBC DataSource實(shí)現(xiàn)和JPA提供程序都支持參數(shù)化的SQL和語(yǔ)句緩存,這可以有效避免在運(yùn)行的應(yīng)用程序中進(jìn)行解析。
動(dòng)態(tài)SQL示例
INSERT INTO EMPLOYEE (ID, NAME) VALUES (34567, "Bob Smith")參數(shù)化的SQL示例
INSERT INTO EMPLOYEE (ID, NAME) VALUES (?, ?)參數(shù)化批處理編寫涉及執(zhí)行單個(gè)DML語(yǔ)句,但是具有用于多個(gè)同質(zhì)語(yǔ)句的一組綁定參數(shù),而不是用于單個(gè)語(yǔ)句的綁定參數(shù)。 這有效地允許數(shù)據(jù)庫(kù)和網(wǎng)絡(luò)將大批同質(zhì)的插入,更新或刪除作為單個(gè)操作而不是n個(gè)操作來(lái)處??理。 數(shù)據(jù)庫(kù)僅需要執(zhí)行最少的工作,因?yàn)橹挥幸粭l語(yǔ)句,因此最多只有一個(gè)解析。 它也與語(yǔ)句緩存兼容,因此根本不需要進(jìn)行語(yǔ)句解析。 限制是所有語(yǔ)句的SQL必須相同。 因此,說(shuō)插入1,000個(gè)Orders確實(shí)非常有用,因?yàn)槊總€(gè)Order的插入SQL都是相同的,只是bind參數(shù)不同。 但這對(duì)于插入1個(gè)訂單或插入1個(gè)訂單,1個(gè)OrderLine和1個(gè)客戶沒有幫助。 同樣,所有語(yǔ)句必須是同一數(shù)據(jù)庫(kù)事務(wù)的一部分。
動(dòng)態(tài)批處理編寫包括將一堆異構(gòu)動(dòng)態(tài)SQL語(yǔ)句鏈接到一個(gè)塊中,然后通過(guò)單個(gè)數(shù)據(jù)庫(kù)/網(wǎng)絡(luò)訪問(wèn)將整個(gè)塊發(fā)送到數(shù)據(jù)庫(kù)。 這是有利的,因?yàn)橹挥幸粋€(gè)網(wǎng)絡(luò)訪問(wèn)權(quán)限,因此,如果數(shù)據(jù)庫(kù)是遠(yuǎn)程的或通過(guò)慢速的網(wǎng)絡(luò)訪問(wèn),則可能會(huì)有很大的不同。 缺點(diǎn)是不允許參數(shù)綁定,并且數(shù)據(jù)庫(kù)必須在接收到此龐大的SQL塊時(shí)對(duì)其進(jìn)行解析。 在某些情況下,解析成本可能超過(guò)網(wǎng)絡(luò)收益。 另外,動(dòng)態(tài)SQL與語(yǔ)句緩存不兼容,因?yàn)槊總€(gè)SQL都不相同。
JDBC通過(guò)其Statement和PrepareStatement批處理API(從JDBC 2.0開始,很早以前就是JDK 1.2)標(biāo)準(zhǔn)化了批處理寫入。 JDBC批處理API需要不同的JDBC代碼,因此,如果您使用的是原始JDBC,則需要重寫代碼以在批處理和非批處理API之間切換。 現(xiàn)在,大多數(shù)JDBC驅(qū)動(dòng)程序都支持這些API,但是有些驅(qū)動(dòng)程序?qū)嶋H上并不模擬DML批量發(fā)送DML到數(shù)據(jù)庫(kù)。 那么,如何知道您是否真的正在批量編寫? 唯一真正的方法是對(duì)其進(jìn)行測(cè)試,并衡量性能差異。
JPA規(guī)范沒有標(biāo)準(zhǔn)化批寫配置,但是大多數(shù)JPA提供程序都支持它。 通常,通過(guò)持久性單元屬性在JPA中啟用批處理寫入,因此打開或關(guān)閉它是一個(gè)簡(jiǎn)單的配置問(wèn)題,并且不需要更改編碼。 一些JPA提供程序在使用開放式鎖定時(shí)可能不支持批處理寫入,并且可能不對(duì)SQL進(jìn)行重新排序以使其能夠進(jìn)行批處理,因此即使啟用了批處理寫入,您仍可能無(wú)法進(jìn)行批處理寫入。 始終在啟用和禁用批寫的情況下測(cè)試您的應(yīng)用程序,并測(cè)量差異以確保其實(shí)際運(yùn)行。
EclipseLink支持參數(shù)化和動(dòng)態(tài)批處理編寫(自EclipseLink 1.0起)。 在EclipseLink中,通過(guò)"eclipselink.jdbc.batch-writing"持久性單元屬性啟用批處理寫入。 EclipseLink提供了三個(gè)選項(xiàng): "JDBC" , "Buffered"和"Oracle-JDBC" 。 應(yīng)始終使用"JDBC"選項(xiàng)。
"Buffered"用于不支持批量寫入的JDBC驅(qū)動(dòng)程序,并將動(dòng)態(tài)SQL語(yǔ)句鏈接到單個(gè)塊本身中。 "Buffered"不支持參數(shù)化的SQL,因此不建議使用。
"Oracle-JDBC"使用早于JDBC標(biāo)準(zhǔn)API的Oracle數(shù)據(jù)庫(kù)JDBC API,現(xiàn)在已過(guò)時(shí)。 在EclipseLink 2.5之前,此選項(xiàng)允許在使用開放式鎖定時(shí)進(jìn)行批處理寫入,但是現(xiàn)在常規(guī)的"JDBC"選項(xiàng)支持開放式鎖定。
EclipseLink 2.5支持在所有(兼容)數(shù)據(jù)庫(kù)平臺(tái)上進(jìn)行樂觀鎖定的批處理寫入,而以前僅在選定的數(shù)據(jù)庫(kù)平臺(tái)上才支持。 EclipseLink 2.5還提供了一個(gè)"eclipselink.jdbc.batch-writing"查詢提示,以禁止無(wú)法寫入的本機(jī)查詢(例如DDL或某些數(shù)據(jù)庫(kù)平臺(tái)上的存儲(chǔ)過(guò)程)的批量寫入。
EclipseLink通過(guò)"eclipselink.jdbc.bind-parameters"和"eclipselink.jdbc.cache-statements"持久單元屬性來(lái)支持參數(shù)化SQL。 但是,通常不需要設(shè)置這些參數(shù),因?yàn)閰?shù)綁定是默認(rèn)設(shè)置,因此您只需將屬性設(shè)置為禁用綁定即可。 默認(rèn)情況下,語(yǔ)句緩存未啟用,如果使用EclipseLink的連接池,則僅與EclipseLink相關(guān);如果使用的是JDBC或Java EE DataSource,則必須在DataSource配置中配置語(yǔ)句緩存。
在EclipseLink中啟用批量寫入時(shí),默認(rèn)情況下它是參數(shù)化的批量寫入。 要啟用動(dòng)態(tài)批處理寫入,必須禁用參數(shù)綁定。 這與啟用緩沖批寫入相同。
支持批處理寫入并不是很難,大多數(shù)JPA提供程序都支持這一點(diǎn),對(duì)SQL進(jìn)行排序以使其可以進(jìn)行批處理是困難的部分。 在提交或刷新操作期間,EclipseLink會(huì)自動(dòng)按表對(duì)SQL進(jìn)行分組,以確保可以批處理同類的SQL語(yǔ)句(同時(shí)仍保持引用完整性約束并避免死鎖)。 大多數(shù)JPA提供程序都不這樣做,因此即使它們支持批處理寫入,SQL的很多時(shí)間也無(wú)法從批處理中受益。
要在EclipseLink中啟用批處理寫入,請(qǐng)將以下內(nèi)容添加到持久性單元屬性;
"eclipselink.jdbc.batch-writing"="JDBC"您還可以使用"eclipselink.jdbc.batch-writing.size"持久性單元屬性來(lái)配置批處理大小。 默認(rèn)大小為100。
"eclipselink.jdbc.batch-writing.size"="1000"批處理非常依賴數(shù)據(jù)庫(kù),并且依賴JDBC驅(qū)動(dòng)程序。 因此,我對(duì)與哪些數(shù)據(jù)庫(kù),它使用的驅(qū)動(dòng)程序以及好處有興趣。 我進(jìn)行了兩次測(cè)試,一個(gè)進(jìn)行了50次插入操作,一個(gè)進(jìn)行了100次更新操作(使用樂觀鎖定)。 我嘗試了所有批處理寫入選項(xiàng),以及不使用任何批處理。
請(qǐng)注意,這不是數(shù)據(jù)庫(kù)基準(zhǔn),我不是在相互比較數(shù)據(jù)庫(kù),而只是對(duì)自己進(jìn)行比較 。
每個(gè)數(shù)據(jù)庫(kù)都在不同的硬件上運(yùn)行,有些是本地的,有些是跨網(wǎng)絡(luò)的,因此不要將一個(gè)數(shù)據(jù)庫(kù)與另一個(gè)數(shù)據(jù)庫(kù)進(jìn)行比較。 感興趣的數(shù)據(jù)是使批寫入相對(duì)于不使用批寫入所帶來(lái)的百分比收益。 對(duì)于插入測(cè)試,我還測(cè)量了使用參數(shù)化SQL與動(dòng)態(tài)SQL以及不使用語(yǔ)句緩存的參數(shù)化SQL之間的差異。 結(jié)果是在10秒內(nèi)處理的事務(wù)數(shù)(運(yùn)行5次,并平均),因此,較大的數(shù)目是更好的結(jié)果。
驅(qū)動(dòng)程序:MySQL-AB JDBC驅(qū)動(dòng)程序版本:mysql-connector-java-5.1.22
插入測(cè)試
| 參數(shù)化SQL,無(wú)批處理 | 483 | 0% |
| 動(dòng)態(tài)SQL,無(wú)批次 | 499 | 3% |
| 參數(shù)化的SQL,無(wú)語(yǔ)句緩存 | 478 | -1% |
| 動(dòng)態(tài)SQL,批處理 | 499 | 3% |
| 參數(shù)化SQL,批處理 | 509 | 5% |
更新測(cè)試
| 參數(shù)化SQL | 245 | 0% |
| 動(dòng)態(tài)SQL,批處理 | 244 | 0% |
| 參數(shù)化SQL,批處理 | 248 | 1% |
因此,結(jié)果似乎表明批處理寫入沒有任何影響(5%在方差之內(nèi))。 這的真正含義是,MySQL JDBC驅(qū)動(dòng)程序?qū)嶋H上并不使用批處理,它只是模擬JDBC批處理API,并在其下逐個(gè)執(zhí)行語(yǔ)句。
盡管MySQL確實(shí)具有批處理支持,但它只需要不同的SQL。 MySQL JDBC驅(qū)動(dòng)程序確實(shí)支持此功能,但是需要設(shè)置rewriteBatchedStatements=true JDBC連接屬性。 可以通過(guò)修改您的連接URL輕松地進(jìn)行設(shè)置,例如;
jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=trueMySQL:rewriteBatchedStatements = true
插入測(cè)試
| 參數(shù)化SQL,無(wú)批處理 | 504 | 0% |
| 動(dòng)態(tài)SQL,無(wú)批次 | 508 | 0% |
| 參數(shù)化的SQL,無(wú)語(yǔ)句緩存 | 483 | -4% |
| 動(dòng)態(tài)SQL,批處理 | 1292 | 156% |
| 參數(shù)化SQL,批處理 | 2181 | 332% |
更新測(cè)試
| 參數(shù)化SQL | 250 | 0% |
| 動(dòng)態(tài)SQL,批處理 | 669 | 167% |
| 參數(shù)化SQL,批處理 | 699 | 179% |
因此,如果配置正確(似乎JDBC驅(qū)動(dòng)程序默認(rèn)不執(zhí)行此操作,我不知道),那么批處理寫入確實(shí)會(huì)在MySQL中產(chǎn)生很大的不同。 參數(shù)化批處理寫入效果最佳,插入速度快332%,更新速度快179%。 動(dòng)態(tài)批處理寫入效果也很好。 有趣的是,MySQL上的動(dòng)態(tài)SQL和參數(shù)化SQL之間似乎沒有什么區(qū)別(我猜想MySQL解析的速度確實(shí)更快,或者對(duì)預(yù)準(zhǔn)備語(yǔ)句的優(yōu)化很少)。
PostgreSQL 8.4 JDBC4
插入測(cè)試
| 參數(shù)化SQL,無(wú)批處理 | 479 | 0% |
| 動(dòng)態(tài)SQL,無(wú)批次 | 418 | -12% |
| 參數(shù)化的SQL,無(wú)語(yǔ)句緩存 | 428 | -10% |
| 動(dòng)態(tài)SQL,緩沖 | 1127 | 135% |
| 動(dòng)態(tài)SQL,批處理 | 1127 | 135% |
| 參數(shù)化SQL,批處理 | 2037 | 325% |
更新測(cè)試
| 參數(shù)化SQL | 233 | 0% |
| 動(dòng)態(tài)SQL,批處理 | 395 | 69% |
| 參數(shù)化SQL,批處理 | 707 | 203% |
結(jié)果表明,批寫在PostgreSQL上有很大的不同。 參數(shù)化批處理寫入性能最佳,插入速度快325%,更新速度快203%。 動(dòng)態(tài)批處理寫入效果也很好。 對(duì)于PostgreSQL,我還評(píng)估了EclipseLink的緩沖批處理寫入的性能,該性能與動(dòng)態(tài)JDBC批處理寫入的性能相同,因此我假設(shè)驅(qū)動(dòng)程序在做相同的事情。 參數(shù)化的SQL優(yōu)于動(dòng)態(tài)SQL約10%,但不帶語(yǔ)句緩存的參數(shù)化SQL與動(dòng)態(tài)SQL相似。
Oracle JDBC驅(qū)動(dòng)程序版本:11.2.0.2.0
插入測(cè)試
| 參數(shù)化SQL,無(wú)批處理 | 548 | 0% |
| 動(dòng)態(tài)SQL,無(wú)批次 | 494 | -9% |
| 參數(shù)化的SQL,無(wú)語(yǔ)句緩存 | 452 | -17% |
| 動(dòng)態(tài)SQL,緩沖 | 383 | -30% |
| 動(dòng)態(tài)SQL,批處理 | 489 | -10% |
| 參數(shù)化SQL,批處理 | 3308 | 503% |
更新測(cè)試
| 參數(shù)化SQL | 282 | 0% |
| 動(dòng)態(tài)SQL,批處理 | 258 | -8% |
| 參數(shù)化SQL,批處理 | 1672 | 492% |
結(jié)果表明,參數(shù)化批處理寫入對(duì)Oracle產(chǎn)生了很大的影響,插入速度快503%,更新速度快492%。 動(dòng)態(tài)批處理寫入沒有任何好處,這是因?yàn)镺racle的JDBC驅(qū)動(dòng)程序僅模擬動(dòng)態(tài)批處理并逐個(gè)執(zhí)行語(yǔ)句,因此它具有與動(dòng)態(tài)SQL相同的性能。 緩沖批寫入實(shí)際上比根本不批處理具有更差的性能。 這是因?yàn)榫薮蟮膭?dòng)態(tài)SQL塊的解析成本,這在不同的配置中可能會(huì)有所不同,如果數(shù)據(jù)庫(kù)是遠(yuǎn)程的或跨慢速的網(wǎng)絡(luò),則我會(huì)看到這樣做的好處。
帶有語(yǔ)句緩存的參數(shù)化SQL比動(dòng)態(tài)SQL提供約10%的收益,并指出,要從參數(shù)化中受益,您需要使用語(yǔ)句緩存,否則性能可能會(huì)比動(dòng)態(tài)SQL差。 粗略地講,參數(shù)化SQL還有其他好處,因?yàn)樗鼜姆?wù)器中刪除了CPU處理,這在單線程情況下可能無(wú)濟(jì)于事,但在數(shù)據(jù)庫(kù)是瓶頸的多線程情況下,可能會(huì)產(chǎn)生很大的不同。
(本地)
插入測(cè)試
| 參數(shù)化SQL,無(wú)批處理 | 3027 | 0% |
| 動(dòng)態(tài)SQL,無(wú)批次 | 24 | -99% |
| 參數(shù)化的SQL,無(wú)語(yǔ)句緩存 | 50 | -98% |
| 動(dòng)態(tài)SQL,批處理 | 24 | -99% |
| 參數(shù)化SQL,批處理 | 3252 | 7% |
更新測(cè)試
| 參數(shù)化SQL | 1437 | 0% |
| 動(dòng)態(tài)SQL,批處理 | 6 | -99% |
| 參數(shù)化SQL,批處理 | 2172 | 51% |
結(jié)果表明,參數(shù)化批處理寫入對(duì)Derby有所不同,插入速度快7%,更新速度快51%。 由于我的數(shù)據(jù)庫(kù)是本地?cái)?shù)據(jù)庫(kù),因此結(jié)果差異不如其他數(shù)據(jù)庫(kù)那么大。 對(duì)于網(wǎng)絡(luò)數(shù)據(jù)庫(kù),這將是一個(gè)更大的差異,但這確實(shí)表明,即使對(duì)于本地?cái)?shù)據(jù)庫(kù),批處理寫入也可以帶來(lái)好處,因此,這不僅僅是網(wǎng)絡(luò)優(yōu)化。 Derby真正有趣的結(jié)果是動(dòng)態(tài)和非緩存語(yǔ)句的可怕性能。 這表明Derby具有巨大的解析成本,因此,如果您使用的是Derby,那么將帶參數(shù)的SQL與語(yǔ)句緩存一起使用非常重要。
用于JDBC和SQLJ的IBM數(shù)據(jù)服務(wù)器驅(qū)動(dòng)程序版本:4.0.100
結(jié)果基本上與Oracle類似,因?yàn)閰?shù)化的批處理編寫具有很大的性能優(yōu)勢(shì)。 動(dòng)態(tài)批處理寫入的性能較差,因此無(wú)法使用參數(shù)化SQL進(jìn)行批處理,而動(dòng)態(tài)SQL和未使用語(yǔ)句緩存的參數(shù)化SQL會(huì)導(dǎo)致性能降低。
Microsoft SQL Server JDBC驅(qū)動(dòng)程序2.0版本:2.0.1803.100
結(jié)果類似于PostgreSQL,顯示參數(shù)化和動(dòng)態(tài)批處理編寫均提供了顯著的好處。 參數(shù)化批處理編寫性能最好,參數(shù)化SQL優(yōu)于動(dòng)態(tài)SQL,并且沒有語(yǔ)句緩存。
**更新**
有人要求我也測(cè)試H2和HSQL,所以這里是結(jié)果。
(本地)
插入測(cè)試
| 參數(shù)化SQL,無(wú)批處理 | 4757 | 0% |
| 動(dòng)態(tài)SQL,無(wú)批次 | 3210 | -32% |
| 參數(shù)化的SQL,無(wú)語(yǔ)句緩存 | 4757 | 0% |
| 動(dòng)態(tài)SQL,緩沖 | 1935年 | -59% |
| 動(dòng)態(tài)SQL,批處理 | 3293 | -30% |
| 參數(shù)化SQL,批處理 | 5753 | 20% |
結(jié)果表明,通過(guò)參數(shù)化批處理寫入,H2的執(zhí)行速度提高了20%。 H2是一個(gè)內(nèi)存數(shù)據(jù)庫(kù)(由持久日志文件支持),因此預(yù)期不會(huì)受益于不涉及任何網(wǎng)絡(luò)的情況。 動(dòng)態(tài)批處理編寫和動(dòng)態(tài)SQL執(zhí)行的參數(shù)化SQL更差。 有趣的是,將參數(shù)緩存與參數(shù)化SQL一起使用不會(huì)產(chǎn)生任何區(qū)別。 我的假設(shè)是H2始終在其連接中緩存準(zhǔn)備好的語(yǔ)句,因此用戶不需要自己進(jìn)行語(yǔ)句緩存。
(本地)
插入測(cè)試
| 參數(shù)化SQL,無(wú)批處理 | 7319 | 0% |
| 動(dòng)態(tài)SQL,無(wú)批次 | 5054 | -30% |
| 參數(shù)化的SQL,無(wú)語(yǔ)句緩存 | 6776 | -7% |
| 動(dòng)態(tài)SQL,批處理 | 5500 | -24% |
| 參數(shù)化SQL,批處理 | 9176 | 25% |
結(jié)果表明,通過(guò)參數(shù)化批處理編寫,HSQL的執(zhí)行速度提高了25%。 HSQL是一個(gè)內(nèi)存數(shù)據(jù)庫(kù)(由持久日志文件支持),因此期望它不會(huì)像沒有網(wǎng)絡(luò)一樣受益。 動(dòng)態(tài)批處理編寫和動(dòng)態(tài)SQL執(zhí)行的參數(shù)化SQL更差。
翻譯自: https://www.javacodegeeks.com/2013/09/batch-writing-and-dynamic-vs-parametrized-sql-how-well-does-your-database-perform.html
總結(jié)
以上是生活随笔為你收集整理的批处理写入以及动态与参数化SQL,数据库的性能如何?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 田园诗派的创始人是谁(关于陶渊明的杂诗代
- 下一篇: 透明地持久保存并从数据库中检索加密的数据